From 2674c7c4049ee09cfe82ebbe9882dfa61afb4280 Mon Sep 17 00:00:00 2001 From: saschawillems Date: Sat, 26 Nov 2016 13:52:22 +0100 Subject: [PATCH] Added compute shader n-body particle simulation demonstrating the use of shared compute shader memory --- CMakeLists.txt | 1 + computenbody/computenbody.cpp | 760 ++++++++++++++++++ computenbody/computenbody.vcxproj | 101 +++ computenbody/computenbody.vcxproj.filters | 59 ++ data/shaders/computenbody/particle.frag | 17 + data/shaders/computenbody/particle.frag.spv | Bin 0 -> 1116 bytes data/shaders/computenbody/particle.vert | 28 + data/shaders/computenbody/particle.vert.spv | Bin 0 -> 1468 bytes .../computenbody/particle_calculate.comp | 75 ++ .../computenbody/particle_calculate.comp.spv | Bin 0 -> 4560 bytes .../computenbody/particle_integrate.comp | 35 + .../computenbody/particle_integrate.comp.spv | Bin 0 -> 1696 bytes vulkanExamples.sln | 12 + 13 files changed, 1088 insertions(+) create mode 100644 computenbody/computenbody.cpp create mode 100644 computenbody/computenbody.vcxproj create mode 100644 computenbody/computenbody.vcxproj.filters create mode 100644 data/shaders/computenbody/particle.frag create mode 100644 data/shaders/computenbody/particle.frag.spv create mode 100644 data/shaders/computenbody/particle.vert create mode 100644 data/shaders/computenbody/particle.vert.spv create mode 100644 data/shaders/computenbody/particle_calculate.comp create mode 100644 data/shaders/computenbody/particle_calculate.comp.spv create mode 100644 data/shaders/computenbody/particle_integrate.comp create mode 100644 data/shaders/computenbody/particle_integrate.comp.spv diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f54ee5b..4a4400ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/") set(EXAMPLES bloom computecullandlod + computenbody computeparticles computeshader debugmarker diff --git a/computenbody/computenbody.cpp b/computenbody/computenbody.cpp new file mode 100644 index 00000000..48fcbe6b --- /dev/null +++ b/computenbody/computenbody.cpp @@ -0,0 +1,760 @@ +/* +* Vulkan Example - Compute shader N-body simulation using two passes and shared compute shader memory +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include +#include "vulkanexamplebase.h" + +#define VERTEX_BUFFER_BIND_ID 0 +#define ENABLE_VALIDATION false +#if defined(__ANDROID__) +// Lower particle count on Android for performance reasons +#define PARTICLES_PER_ATTRACTOR 2 * 1024 +#else +#define PARTICLES_PER_ATTRACTOR 8 * 1024 +#endif + +class VulkanExample : public VulkanExampleBase +{ +public: + uint32_t numParticles; + + struct { + vkTools::VulkanTexture particle; + vkTools::VulkanTexture gradient; + } textures; + + struct { + VkPipelineVertexInputStateCreateInfo inputState; + std::vector bindingDescriptions; + std::vector attributeDescriptions; + } vertices; + + // Resources for the graphics part of the example + struct { + vk::Buffer uniformBuffer; // Contains scene matrices + VkDescriptorSetLayout descriptorSetLayout; // Particle system rendering shader binding layout + VkDescriptorSet descriptorSet; // Particle system rendering shader bindings + VkPipelineLayout pipelineLayout; // Layout of the graphics pipeline + VkPipeline pipeline; // Particle rendering pipeline + struct { + glm::mat4 projection; + glm::mat4 view; + } ubo; + } graphics; + + // Resources for the compute part of the example + struct { + vk::Buffer storageBuffer; // (Shader) storage buffer object containing the particles + vk::Buffer uniformBuffer; // Uniform buffer object containing particle system parameters + VkQueue queue; // Separate queue for compute commands (queue family may differ from the one used for graphics) + VkCommandPool commandPool; // Use a separate command pool (queue family may differ from the one used for graphics) + VkCommandBuffer commandBuffer; // Command buffer storing the dispatch commands and barriers + VkFence fence; // Synchronization fence to avoid rewriting compute CB if still in use + VkDescriptorSetLayout descriptorSetLayout; // Compute shader binding layout + VkDescriptorSet descriptorSet; // Compute shader bindings + VkPipelineLayout pipelineLayout; // Layout of the compute pipeline + VkPipeline pipelineCalculate; // Compute pipeline for N-Body velocity calculation (1st pass) + VkPipeline pipelineIntegrate; // Compute pipeline for euler integration (2nd pass) + VkPipeline blur; + VkPipelineLayout pipelineLayoutBlur; + VkDescriptorSetLayout descriptorSetLayoutBlur; + VkDescriptorSet descriptorSetBlur; + struct computeUBO { // Compute shader uniform block object + float deltaT; // Frame delta time + float destX; // x position of the attractor + float destY; // y position of the attractor + int32_t particleCount; + } ubo; + } compute; + + // SSBO particle declaration + struct Particle { + glm::vec4 pos; // xyz = position, w = mass + glm::vec4 vel; // xyz = velocity, w = gradient texture position + }; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + enableTextOverlay = true; + title = "Vulkan Example - Compute shader N-body system"; + + camera.type = Camera::CameraType::lookat; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(-26.0f, 75.0f, 0.0f)); + camera.setTranslation(glm::vec3(0.0f, 0.0f, -14.0f)); + camera.movementSpeed = 2.5f; + } + + ~VulkanExample() + { + // Graphics + vkDestroyPipeline(device, graphics.pipeline, nullptr); + vkDestroyPipelineLayout(device, graphics.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, graphics.descriptorSetLayout, nullptr); + + // Compute + compute.storageBuffer.destroy(); + compute.uniformBuffer.destroy(); + vkDestroyPipelineLayout(device, compute.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, compute.descriptorSetLayout, nullptr); + vkDestroyPipeline(device, compute.pipelineCalculate, nullptr); + vkDestroyPipeline(device, compute.pipelineIntegrate, nullptr); + vkDestroyFence(device, compute.fence, nullptr); + vkDestroyCommandPool(device, compute.commandPool, nullptr); + + textureLoader->destroyTexture(textures.particle); + textureLoader->destroyTexture(textures.gradient); + } + + void loadTextures() + { + textureLoader->loadTexture(getAssetPath() + "textures/particle01_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, &textures.particle, false); + textureLoader->loadTexture(getAssetPath() + "textures/particle_gradient_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, &textures.gradient, false); + } + + void buildCommandBuffers() + { + // Destroy command buffers if already present + if (!checkCommandBuffers()) + { + destroyCommandBuffers(); + createCommandBuffers(); + } + + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} }; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + // Set target frame buffer + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + // Draw the particle system using the update vertex buffer + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vkTools::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics.pipeline); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics.pipelineLayout, 0, 1, &graphics.descriptorSet, 0, NULL); + + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &compute.storageBuffer.buffer, offsets); + vkCmdDraw(drawCmdBuffers[i], numParticles, 1, 0, 0); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + + } + + void buildComputeCommandBuffer() + { + VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); + + VK_CHECK_RESULT(vkBeginCommandBuffer(compute.commandBuffer, &cmdBufInfo)); + + // Compute particle movement + + // Add memory barrier to ensure that the (graphics) vertex shader has fetched attributes before compute starts to write to the buffer + VkBufferMemoryBarrier bufferBarrier = vkTools::initializers::bufferMemoryBarrier(); + bufferBarrier.buffer = compute.storageBuffer.buffer; + bufferBarrier.size = compute.storageBuffer.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 + // Transfer ownership if compute and graphics queue familiy indices differ + bufferBarrier.srcQueueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; + bufferBarrier.dstQueueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + + vkCmdPipelineBarrier( + compute.commandBuffer, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_FLAGS_NONE, + 0, nullptr, + 1, &bufferBarrier, + 0, nullptr); + + vkCmdBindPipeline(compute.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineCalculate); + vkCmdBindDescriptorSets(compute.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineLayout, 0, 1, &compute.descriptorSet, 0, 0); + + // First pass: Calculate particle movement + // ------------------------------------------------------------------------------------------------------- + vkCmdDispatch(compute.commandBuffer, numParticles / 256, 1, 1); + + // Add memory barrier to ensure that compute shader has finished writing to the buffer + bufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // Compute shader has finished writes to the buffer + bufferBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + bufferBarrier.buffer = compute.storageBuffer.buffer; + bufferBarrier.size = compute.storageBuffer.descriptor.range; + // No ownership transfer necessary + bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + vkCmdPipelineBarrier( + compute.commandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_FLAGS_NONE, + 0, nullptr, + 1, &bufferBarrier, + 0, nullptr); + + // Second pass: Integrate particles + // ------------------------------------------------------------------------------------------------------- + vkCmdBindPipeline(compute.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, compute.pipelineIntegrate); + vkCmdDispatch(compute.commandBuffer, numParticles / 256, 1, 1); + + // Add memory barrier to ensure that compute shader has finished writing to the buffer + // Without this the (rendering) vertex shader may display incomplete results (partial data from last frame) + bufferBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // Compute shader has finished writes to the buffer + bufferBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; // Vertex shader invocations want to read from the buffer + bufferBarrier.buffer = compute.storageBuffer.buffer; + bufferBarrier.size = compute.storageBuffer.descriptor.range; + // Transfer ownership if compute and graphics queue familiy indices differ + bufferBarrier.srcQueueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + bufferBarrier.dstQueueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; + + vkCmdPipelineBarrier( + compute.commandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_FLAGS_NONE, + 0, nullptr, + 1, &bufferBarrier, + 0, nullptr); + + vkEndCommandBuffer(compute.commandBuffer); + } + + // Setup and fill the compute shader storage buffers containing the particles + void prepareStorageBuffers() + { +#if 0 + std::vector attractors = { + glm::vec3(2.5f, 1.5f, 0.0f), + glm::vec3(-2.5f, -1.5f, 0.0f), + }; +#else + std::vector attractors = { + glm::vec3(5.0f, 0.0f, 0.0f), + glm::vec3(-5.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 5.0f), + glm::vec3(0.0f, 0.0f, -5.0f), + glm::vec3(0.0f, 4.0f, 0.0f), + glm::vec3(0.0f, -8.0f, 0.0f), + }; +#endif + + numParticles = static_cast(attractors.size()) * PARTICLES_PER_ATTRACTOR; + + // Initial particle positions + std::vector particleBuffer(numParticles); + + std::mt19937 rndGen(static_cast(time(0))); + std::normal_distribution rndDist(0.0f, 1.0f); + + for (uint32_t i = 0; i < static_cast(attractors.size()); i++) + { + for (uint32_t j = 0; j < PARTICLES_PER_ATTRACTOR; j++) + { + Particle &particle = particleBuffer[i * PARTICLES_PER_ATTRACTOR + j]; + + // First particle in group as heavy center of gravity + if (j == 0) + { + particle.pos = glm::vec4(attractors[i] * 1.5f, 90000.0f); + particle.vel = glm::vec4(glm::vec4(0.0f)); + } + else + { + // Position + glm::vec3 position(attractors[i] + glm::vec3(rndDist(rndGen), rndDist(rndGen), rndDist(rndGen)) * 0.75f); + float len = glm::length(glm::normalize(position - attractors[i])); + position.y *= 2.0f - (len * len); + + // Velocity + glm::vec3 angular = glm::vec3(0.5f, 1.5f, 0.5f) * (((i % 2) == 0) ? 1.0f : -1.0f); + glm::vec3 velocity = glm::cross((position - attractors[i]), angular) + glm::vec3(rndDist(rndGen), rndDist(rndGen), rndDist(rndGen) * 0.025f); + + float mass = (rndDist(rndGen) * 0.5f + 0.5f) * 75.0f; + particle.pos = glm::vec4(position, mass); + particle.vel = glm::vec4(velocity, 0.0f); + } + + // Color gradient offset + particle.vel.w = (float)i * 1.0f / static_cast(attractors.size()); + } + } + + compute.ubo.particleCount = numParticles; + + VkDeviceSize storageBufferSize = particleBuffer.size() * sizeof(Particle); + + // Staging + // SSBO won't be changed on the host after upload so copy to device local memory + + vk::Buffer stagingBuffer; + + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &stagingBuffer, + storageBufferSize, + particleBuffer.data()); + + vulkanDevice->createBuffer( + // The SSBO will be used as a storage buffer for the compute pipeline and as a vertex buffer in the graphics pipeline + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &compute.storageBuffer, + storageBufferSize); + + // Copy to staging buffer + VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + VkBufferCopy copyRegion = {}; + copyRegion.size = storageBufferSize; + vkCmdCopyBuffer(copyCmd, stagingBuffer.buffer, compute.storageBuffer.buffer, 1, ©Region); + VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); + + stagingBuffer.destroy(); + + // Binding description + vertices.bindingDescriptions.resize(1); + vertices.bindingDescriptions[0] = + vkTools::initializers::vertexInputBindingDescription( + VERTEX_BUFFER_BIND_ID, + sizeof(Particle), + VK_VERTEX_INPUT_RATE_VERTEX); + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions.resize(2); + // Location 0 : Position + vertices.attributeDescriptions[0] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 0, + VK_FORMAT_R32G32B32_SFLOAT, + offsetof(Particle, pos)); + // Location 1 : Velocity (used for gradient lookup) + vertices.attributeDescriptions[1] = + vkTools::initializers::vertexInputAttributeDescription( + VERTEX_BUFFER_BIND_ID, + 1, + VK_FORMAT_R32G32B32A32_SFLOAT, + offsetof(Particle, vel)); + + // Assign to vertex buffer + vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo(); + vertices.inputState.vertexBindingDescriptionCount = static_cast(vertices.bindingDescriptions.size()); + vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data(); + vertices.inputState.vertexAttributeDescriptionCount = static_cast(vertices.attributeDescriptions.size()); + vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data(); + } + + void setupDescriptorPool() + { + std::vector poolSizes = + { + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1), + vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2) + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vkTools::initializers::descriptorPoolCreateInfo( + static_cast(poolSizes.size()), + poolSizes.data(), + 2); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + } + + void setupDescriptorSetLayout() + { + std::vector setLayoutBindings; + setLayoutBindings = { + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0), + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1), + vkTools::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 2), + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vkTools::initializers::descriptorSetLayoutCreateInfo( + setLayoutBindings.data(), + static_cast(setLayoutBindings.size())); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &graphics.descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = + vkTools::initializers::pipelineLayoutCreateInfo( + &graphics.descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &graphics.pipelineLayout)); + } + + void setupDescriptorSet() + { + VkDescriptorSetAllocateInfo allocInfo = + vkTools::initializers::descriptorSetAllocateInfo( + descriptorPool, + &graphics.descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &graphics.descriptorSet)); + + std::vector writeDescriptorSets; + writeDescriptorSets = { + vkTools::initializers::writeDescriptorSet(graphics.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &textures.particle.descriptor), + vkTools::initializers::writeDescriptorSet(graphics.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &textures.gradient.descriptor), + vkTools::initializers::writeDescriptorSet(graphics.descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &graphics.uniformBuffer.descriptor), + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); + } + + void preparePipelines() + { + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = + vkTools::initializers::pipelineInputAssemblyStateCreateInfo( + VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + 0, + VK_FALSE); + + VkPipelineRasterizationStateCreateInfo rasterizationState = + vkTools::initializers::pipelineRasterizationStateCreateInfo( + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_NONE, + VK_FRONT_FACE_COUNTER_CLOCKWISE, + 0); + + VkPipelineColorBlendAttachmentState blendAttachmentState = + vkTools::initializers::pipelineColorBlendAttachmentState( + 0xf, + VK_FALSE); + + VkPipelineColorBlendStateCreateInfo colorBlendState = + vkTools::initializers::pipelineColorBlendStateCreateInfo( + 1, + &blendAttachmentState); + + VkPipelineDepthStencilStateCreateInfo depthStencilState = + vkTools::initializers::pipelineDepthStencilStateCreateInfo( + VK_FALSE, + VK_FALSE, + VK_COMPARE_OP_ALWAYS); + + VkPipelineViewportStateCreateInfo viewportState = + vkTools::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + + VkPipelineMultisampleStateCreateInfo multisampleState = + vkTools::initializers::pipelineMultisampleStateCreateInfo( + VK_SAMPLE_COUNT_1_BIT, + 0); + + std::vector dynamicStateEnables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicState = + vkTools::initializers::pipelineDynamicStateCreateInfo( + dynamicStateEnables.data(), + static_cast(dynamicStateEnables.size()), + 0); + + // Rendering pipeline + // Load shaders + std::array shaderStages; + + shaderStages[0] = loadShader(getAssetPath() + "shaders/computenbody/particle.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getAssetPath() + "shaders/computenbody/particle.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = + vkTools::initializers::pipelineCreateInfo( + graphics.pipelineLayout, + renderPass, + 0); + + pipelineCreateInfo.pVertexInputState = &vertices.inputState; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; + pipelineCreateInfo.pRasterizationState = &rasterizationState; + pipelineCreateInfo.pColorBlendState = &colorBlendState; + pipelineCreateInfo.pMultisampleState = &multisampleState; + pipelineCreateInfo.pViewportState = &viewportState; + pipelineCreateInfo.pDepthStencilState = &depthStencilState; + pipelineCreateInfo.pDynamicState = &dynamicState; + pipelineCreateInfo.stageCount = static_cast(shaderStages.size()); + pipelineCreateInfo.pStages = shaderStages.data(); + pipelineCreateInfo.renderPass = renderPass; + + // Additive blending + blendAttachmentState.colorWriteMask = 0xF; + blendAttachmentState.blendEnable = VK_TRUE; + blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; + blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; + blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA; + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &graphics.pipeline)); + } + + void prepareCompute() + { + // Create a compute capable device queue + // The VulkanDevice::createLogicalDevice functions finds a compute capable queue and prefers queue families that only support compute + // Depending on the implementation this may result in different queue family indices for graphics and computes, + // requiring proper synchronization (see the memory barriers in buildComputeCommandBuffer) + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.pNext = NULL; + queueCreateInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + queueCreateInfo.queueCount = 1; + vkGetDeviceQueue(device, vulkanDevice->queueFamilyIndices.compute, 0, &compute.queue); + + // Create compute pipeline + // Compute pipelines are created separate from graphics pipelines even if they use the same queue (family index) + + std::vector setLayoutBindings = { + // Binding 0 : Particle position storage buffer + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_SHADER_STAGE_COMPUTE_BIT, + 0), + // Binding 1 : Uniform buffer + vkTools::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_SHADER_STAGE_COMPUTE_BIT, + 1), + }; + + VkDescriptorSetLayoutCreateInfo descriptorLayout = + vkTools::initializers::descriptorSetLayoutCreateInfo( + setLayoutBindings.data(), + static_cast(setLayoutBindings.size())); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &compute.descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = + vkTools::initializers::pipelineLayoutCreateInfo( + &compute.descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &compute.pipelineLayout)); + + VkDescriptorSetAllocateInfo allocInfo = + vkTools::initializers::descriptorSetAllocateInfo( + descriptorPool, + &compute.descriptorSetLayout, + 1); + + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &compute.descriptorSet)); + + std::vector computeWriteDescriptorSets = + { + // Binding 0 : Particle position storage buffer + vkTools::initializers::writeDescriptorSet( + compute.descriptorSet, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 0, + &compute.storageBuffer.descriptor), + // Binding 1 : Uniform buffer + vkTools::initializers::writeDescriptorSet( + compute.descriptorSet, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 1, + &compute.uniformBuffer.descriptor) + }; + + vkUpdateDescriptorSets(device, static_cast(computeWriteDescriptorSets.size()), computeWriteDescriptorSets.data(), 0, NULL); + + // Create pipelines + VkComputePipelineCreateInfo computePipelineCreateInfo = vkTools::initializers::computePipelineCreateInfo(compute.pipelineLayout, 0); + + // 1st pass + computePipelineCreateInfo.stage = loadShader(getAssetPath() + "shaders/computenbody/particle_calculate.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT); + + // Set shader parameters via specialization constants + struct SpecializationData { + uint32_t sharedDataSize; + float gravity; + float power; + float soften; + } specializationData; + + std::vector specializationMapEntries; + specializationMapEntries.push_back(vkTools::initializers::specializationMapEntry(0, offsetof(SpecializationData, sharedDataSize), sizeof(uint32_t))); + specializationMapEntries.push_back(vkTools::initializers::specializationMapEntry(1, offsetof(SpecializationData, gravity), sizeof(float))); + specializationMapEntries.push_back(vkTools::initializers::specializationMapEntry(2, offsetof(SpecializationData, power), sizeof(float))); + specializationMapEntries.push_back(vkTools::initializers::specializationMapEntry(3, offsetof(SpecializationData, soften), sizeof(float))); + + specializationData.sharedDataSize = std::min((uint32_t)1024, vulkanDevice->properties.limits.maxComputeSharedMemorySize); + specializationData.gravity = 0.0025f; + specializationData.power = 0.85f; + specializationData.soften = 0.15f; + + VkSpecializationInfo specializationInfo = + vkTools::initializers::specializationInfo(static_cast(specializationMapEntries.size()), specializationMapEntries.data(), sizeof(specializationData), &specializationData); + computePipelineCreateInfo.stage.pSpecializationInfo = &specializationInfo; + + VK_CHECK_RESULT(vkCreateComputePipelines(device, pipelineCache, 1, &computePipelineCreateInfo, nullptr, &compute.pipelineCalculate)); + + // 2nd pass + computePipelineCreateInfo.stage = loadShader(getAssetPath() + "shaders/computenbody/particle_integrate.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT); + VK_CHECK_RESULT(vkCreateComputePipelines(device, pipelineCache, 1, &computePipelineCreateInfo, nullptr, &compute.pipelineIntegrate)); + + // Separate command pool as queue family for compute may be different than graphics + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.compute; + cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &compute.commandPool)); + + // Create a command buffer for compute operations + VkCommandBufferAllocateInfo cmdBufAllocateInfo = + vkTools::initializers::commandBufferAllocateInfo( + compute.commandPool, + VK_COMMAND_BUFFER_LEVEL_PRIMARY, + 1); + + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &compute.commandBuffer)); + + // Fence for compute CB sync + VkFenceCreateInfo fenceCreateInfo = vkTools::initializers::fenceCreateInfo(VK_FENCE_CREATE_SIGNALED_BIT); + VK_CHECK_RESULT(vkCreateFence(device, &fenceCreateInfo, nullptr, &compute.fence)); + + // Build a single command buffer containing the compute dispatch commands + buildComputeCommandBuffer(); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + // Compute shader uniform buffer block + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &compute.uniformBuffer, + sizeof(compute.ubo)); + + // Map for host access + VK_CHECK_RESULT(compute.uniformBuffer.map()); + + // Vertex shader uniform buffer block + vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &graphics.uniformBuffer, + sizeof(graphics.ubo)); + + // Map for host access + VK_CHECK_RESULT(graphics.uniformBuffer.map()); + + updateGraphicsUniformBuffers(); + } + + void updateUniformBuffers() + { + compute.ubo.deltaT = paused ? 0.0f : frameTimer * 0.05f; + compute.ubo.destX = sin(glm::radians(timer * 360.0f)) * 0.75f; + compute.ubo.destY = 0.0f; + memcpy(compute.uniformBuffer.mapped, &compute.ubo, sizeof(compute.ubo)); + } + + void updateGraphicsUniformBuffers() + { + graphics.ubo.projection = camera.matrices.perspective; + graphics.ubo.view = camera.matrices.view; + memcpy(graphics.uniformBuffer.mapped, &graphics.ubo, sizeof(graphics.ubo)); + } + + void draw() + { + // Submit graphics commands + VulkanExampleBase::prepareFrame(); + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + + // Submit compute commands + vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX); + vkResetFences(device, 1, &compute.fence); + + VkSubmitInfo computeSubmitInfo = vkTools::initializers::submitInfo(); + computeSubmitInfo.commandBufferCount = 1; + computeSubmitInfo.pCommandBuffers = &compute.commandBuffer; + + VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + loadTextures(); + prepareStorageBuffers(); + prepareUniformBuffers(); + setupDescriptorSetLayout(); + preparePipelines(); + setupDescriptorPool(); + setupDescriptorSet(); + prepareCompute(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + updateUniformBuffers(); + } + + virtual void viewChanged() + { + updateGraphicsUniformBuffers(); + } +}; + +VULKAN_EXAMPLE_MAIN() \ No newline at end of file diff --git a/computenbody/computenbody.vcxproj b/computenbody/computenbody.vcxproj new file mode 100644 index 00000000..8d9dd6a8 --- /dev/null +++ b/computenbody/computenbody.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {6B4BC372-5897-40FB-91D4-421C2817F656} + Win32Proj + 8.1 + + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + true + $(SolutionDir)\bin\ + $(SolutionDir)\bin\intermediate\$(ProjectName)\$(ConfigurationName) + + + + WIN32;_DEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Disabled + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + true + Windows + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + WIN32;NDEBUG;_WINDOWS;VK_USE_PLATFORM_WIN32_KHR;_USE_MATH_DEFINES;NOMINMAX;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + ..\base;..\external\glm;..\external\gli;..\external\assimp;..\external;%(AdditionalIncludeDirectories) + + + true + Windows + true + true + ..\libs\vulkan\vulkan-1.lib;..\libs\assimp\assimp.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/computenbody/computenbody.vcxproj.filters b/computenbody/computenbody.vcxproj.filters new file mode 100644 index 00000000..b8cdbc23 --- /dev/null +++ b/computenbody/computenbody.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {e28680da-cc95-413d-b6f0-0e1f9967ee88} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/data/shaders/computenbody/particle.frag b/data/shaders/computenbody/particle.frag new file mode 100644 index 00000000..23d6feac --- /dev/null +++ b/data/shaders/computenbody/particle.frag @@ -0,0 +1,17 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 0) uniform sampler2D samplerColorMap; +layout (binding = 1) uniform sampler2D samplerGradientRamp; + +layout (location = 0) in float inGradientPos; + +layout (location = 0) out vec4 outFragColor; + +void main () +{ + vec3 color = texture(samplerGradientRamp, vec2(inGradientPos, 0.0)).rgb; + outFragColor.rgb = texture(samplerColorMap, gl_PointCoord).rgb * color; +} diff --git a/data/shaders/computenbody/particle.frag.spv b/data/shaders/computenbody/particle.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..4b7bd1b5ea1087c36e4ce46945a61b9c22671cba GIT binary patch literal 1116 zcmY+CTTk0S5QPUq!mZp}E~P*M?NcR$5FjK35+IS6R7CK=qqQWK2@Q5+o1efR<5%?s zspng45hgmCJ!fWTX2+?F?u?s_=r>`%?A;b@RNQRZYTh?nx2@eQ@9rKP?vXKT6`x4X zobHXugD2OE3Cs&?!iGOrd`wMrtD?$guevy9lh$kn=QkHY77kqM@-WCATsKUEQ!7;jK|WXeYfmzf6*}E8f&B^xhV* zk~ef_cW`#KDPVB=4s-eq--mM_BcHz00(F+OUH04Xn>K%5*10CFF03e@?*Qiu*2O2} z(ZHHEHziM;`G&3J>$*ul^0@z{NJ2X)IB&*?&&hL_n#AGEiuPL^eNn)eZ%H1Fcfh|D zoNr9MRe5GdryiW0p_TpF5k~$Gd3sToIGmlK9qEhGhuRo5{z<9Ilgn=SFR`0bao#v9 z@IE+mE(pA*ty}c?T4=oKP8xGA3)~}it*X>p5zqm~Jj`;YDdu>2y4tiO?t}{Bo%KFrxe?y?pi)Msx3v~gV9Om8;*zL74;XezFTz1Bu*a3TEZgR2N76o!W^ZK2RoN*A``G%Z^pRHz^%#FE~)7m+Ab^wx5lQ81)-0y>Zw+mv&Nj$Z_Ye6|kFt|b z_OuxO&(T$q_M(2A_C|5fMte_o2l44=Q&*aCnjyN6B{!udaTXfkR$q$woz(s~4LV$^l+;^CjuA?K@u8T$lc^|JK#S z<_z+TsVF(Ysd<)lZU0MRzsjcGP3h4|=JK+$Bz0xXfe_~6%#YOsBM%rlR1SFTIaF`- zydmM>^DXe<9?19o)tz6T_)Ym7>UxeIh|A~W$T1y0g}k$$d$p(!~&;G%vw=5kgW zvo8KMX>$8{*)14C{TtHk37q=)>==yuTcr)3{WYbj= ubo.particleCount) + return; + + vec4 position = particles[index].pos; + vec4 velocity = particles[index].vel; + vec4 acceleration = vec4(0.0); + + for (int i = 0; i < ubo.particleCount; i += SHARED_DATA_SIZE) + { + if (i + gl_LocalInvocationID.x < ubo.particleCount) + { + sharedData[gl_LocalInvocationID.x] = particles[i + gl_LocalInvocationID.x].pos; + } + else + { + sharedData[gl_LocalInvocationID.x] = vec4(0.0); + } + + memoryBarrierShared(); + + for (int j = 0; j < gl_WorkGroupSize.x; j++) + { + vec4 other = sharedData[j]; + vec3 len = other.xyz - position.xyz; + acceleration.xyz += GRAVITY * len * other.w / pow(dot(len, len) + SOFTEN, POWER); + } + } + + particles[index].vel.xyz += ubo.deltaT * acceleration.xyz; + + // Gradient texture position + particles[index].vel.w += 0.1 * ubo.deltaT; + if (particles[index].vel.w > 1.0) + particles[index].vel.w -= 1.0; +} \ No newline at end of file diff --git a/data/shaders/computenbody/particle_calculate.comp.spv b/data/shaders/computenbody/particle_calculate.comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..dba203d3f8db43ea55b7cf6e827e1371ba9b4902 GIT binary patch literal 4560 zcmZ{mX>(Ln5QZ;ILO@gmSrmjCKm`#2BOr-D~FiFNRI?P})Am9cfiYxBB z;=bT6e)1!q@kjV0T&t||dG6fCVG2*`t?quiPoF-0&OMpj$k}a4GJ?O+$xlgrj88^l zC&}1kR4Ti=H*_x;t_>_$ykwCPXD7Kd(3o*aThfYb0ox0uijJ|M6P&|*`SHL+#)}+( zBaz23T9PqIS9fp6t)0EY#T|vALao?4ysa=$9O|w1-B;|d4WAl=t5g~6Ef*?-I}3xw z-o@>Ub`<*Wr-qhOYS13d8l}oWaW}FR%en^3yk~87P)(g^kHc&iV(6!?n%Gh;uJ;TT-qqook2KPOa2mF0QWbtkkeu+4m&m z&b}&oYck82>Tt;-P*ZL;a(y;W&JM~sh&^dr$F5>I&3O^}`qVcnQFp}Jv@eaBld|@B*_UPG&3&2{kc@~b?dvNYs&U+P{{g?B+gUe;y znaD2sZa(8&%QLX|j<9E16YK^8y+!{!(aqJ4 zU*3D;f)`-lgqjC(&c7My{^gyo?ROhJ@eQl%|D9(qocZH?x1vkfQy(ttkKR|&txw*1 z^m%Wq6LQOAzK>4C??pG>xQO46e)#xr%fmi^og}MPTK`LE_R@IsDUX0(PMlZ~_EGF4 z*|%~&cglNu3~UANLoDT6HHZ6jSj(R?Uv zu+8V27=C-^Td3VZ6@D{k1HUW3r4`iR_cIR2UrWW-ryV}uBsu4M|IY({BYqdgcrSbh zAKUiZ1((yO(`lbUpd!_ylmq1-E>bL~GUPoJc?=Quk1LgvK zN@MfL`9Qz>vA^1pX90R+o<-P^N8iFMHjlg<=r@n&qOIEf9M(+fT_hAjT{65z6eq0BfCm(fQkKSBo7o2>owH96O`>gJD=;o659^8m@xCebV zfIESEP>&kdqc_)h3!HrH^H%i6eQv;(iyRx#n{#Y}laGDghVDH14!)Xs$lF2O5B;9C zYvz%gfqvK6g1iIt0BiGZXdC0%yI1$+{-UP4(3@+z8%{oQ-h*yVd2?!e_ZoAW`&WFv zL0f@w1!UaIK6KYsdM^i%Mc^>cy-5Aa=;t81IpQq0q07ZtmeA$40?$voADG)*>XGYy zbaTa?%INM%K6<$wUEbP#!?eTq0J>ZcUv33C1muKc1Q-b-ei(fgi1-@1oNz?^0nW{Q zJApZkbNw9|XFmN80=XBm_}v+|3+bNrA|C?QxCc0IJn~^6AM+kTcdmToeiYr@VLz7H zuaegs`+za>`!nuw^nI!AciWc6J%R1MrULy3zpuwe2M%TXcoNQ<^t*l@`4nge z(Yr$#C+~>fS&O-!2F92xay)}x&oL&=`7E}au`Qq#i~>1ezl}AWbtF?o}PagRmkTcKe&eT|AqG#`;H}>oU?BwXVs hjBlE~`vJ(gZtVL<^wZ^V-};>6osW8y{uWyX{soixf5`v< literal 0 HcmV?d00001 diff --git a/data/shaders/computenbody/particle_integrate.comp b/data/shaders/computenbody/particle_integrate.comp new file mode 100644 index 00000000..e40df671 --- /dev/null +++ b/data/shaders/computenbody/particle_integrate.comp @@ -0,0 +1,35 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +struct Particle +{ + vec4 pos; + vec4 vel; +}; + +// Binding 0 : Position storage buffer +layout(std140, binding = 0) buffer Pos +{ + Particle particles[ ]; +}; + +layout (local_size_x = 256) in; + +layout (binding = 1) uniform UBO +{ + float deltaT; + float destX; + float destY; + int particleCount; +} ubo; + +void main() +{ + int index = int(gl_GlobalInvocationID); + vec4 position = particles[index].pos; + vec4 velocity = particles[index].vel; + position += ubo.deltaT * velocity; + particles[index].pos = position; +} \ No newline at end of file diff --git a/data/shaders/computenbody/particle_integrate.comp.spv b/data/shaders/computenbody/particle_integrate.comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..ba8097ec013c616f7c5a51627622e8a7bf527574 GIT binary patch literal 1696 zcmY+FYflqV5QY!E0is+)z#FxIf(jxc8WW>Q(WH=&h_^&PZI*UZSJ&N!E<_W*_y_z^ z{wlwi_&%pQrkrLvGwB`;_pDFoN#Vew(VVq((FPat(HUl6>e4(;FCGp=j()y;_?hR1o_P9iWS0^`W{zs_8zQKE>lTabL{ zZ8a}@-y(;`@jh1ob*OPeT(Ma@&c6?^vb!na{q8HjS!7NpO7id>JygA&nw_Yhe-Jl} zy@Gx8*r@m8rnFi=!;9=B&85@ls`$xK=31JG-AstCi}qx-8CRrp45KF4w1~KQ@tUY1 z!iU*87S}5FK@a#d_c9JTrY{XtA8)f-JUPLvZbh})Nvl7#gt{2A>IQDUm>q}=r`Z;kha^O#XV!@vlI=zD7 zKPgTw{O9$}yVDS1yC{y%4#+htPJQsYbnXlpT2w4|16G&Jy`U3!S)6&m=mDLX!Pccy z|Eh?CKhFd<@|t}~@`$J|Z+1*yMuQzy+$~|mVW$rIYJrh=t&oW$FME0*BC+Y)1J!M) z4m;zH@BxP&>-nGtJK?amKSKCDq9*&{`GBLdGv4!Io%av@ne5cKAmUDbNasGL3hcGA zG8@l680Wbvk{^tC?AJtKO=V?wiz4>RPO-u9WuJ8scJj~{y5(s|9?r8Y-SS}L4lK`# zWbEW&9`vUAT@$}8V((U$ecvf?i@z(KxL=B)uXPa^e&(|wnI3-@KKCSp;cNYaS^t}o d>EGh-OD5j-!XJsvgD?G&2OQtOdJvw7{sD=ha6141 literal 0 HcmV?d00001 diff --git a/vulkanExamples.sln b/vulkanExamples.sln index 3109b73a..8fe4ddcb 100644 --- a/vulkanExamples.sln +++ b/vulkanExamples.sln @@ -121,6 +121,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subpasses", "subpasses\subp EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssao", "ssao\ssao.vcxproj", "{43FCECF5-F5E1-45DB-972A-73942D459C0A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "computenbody", "computenbody\computenbody.vcxproj", "{6B4BC372-5897-40FB-91D4-421C2817F656}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9E815C67-731B-4559-938E-23CD771F0860}" + ProjectSection(SolutionItems) = preProject + CMakeLists.txt = CMakeLists.txt + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -299,6 +306,10 @@ Global {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Debug|x64.Build.0 = Debug|x64 {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Release|x64.ActiveCfg = Release|x64 {43FCECF5-F5E1-45DB-972A-73942D459C0A}.Release|x64.Build.0 = Release|x64 + {6B4BC372-5897-40FB-91D4-421C2817F656}.Debug|x64.ActiveCfg = Debug|x64 + {6B4BC372-5897-40FB-91D4-421C2817F656}.Debug|x64.Build.0 = Debug|x64 + {6B4BC372-5897-40FB-91D4-421C2817F656}.Release|x64.ActiveCfg = Release|x64 + {6B4BC372-5897-40FB-91D4-421C2817F656}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -320,5 +331,6 @@ Global {1FA0178C-F5E9-4B2E-A488-14F310F8DBD9} = {A8492D6D-5243-456E-8173-39B99F1FEA9C} {0CB44B34-A81F-4002-9AC7-E0EEA55D8A60} = {460EE42F-4178-49EF-9AC0-415599B80303} {8418A364-3D1C-4938-A2CC-C1D1433039F2} = {6B47BC47-0394-429E-9441-867EC23DFCD4} + {6B4BC372-5897-40FB-91D4-421C2817F656} = {6B47BC47-0394-429E-9441-867EC23DFCD4} EndGlobalSection EndGlobal