1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "SampleUtils.h"
16
17 #include "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/DawnHelpers.h"
19 #include "utils/SystemUtils.h"
20
21 #include <array>
22 #include <cstring>
23 #include <random>
24
25 #include <glm/glm.hpp>
26
27 dawn::Device device;
28 dawn::Queue queue;
29 dawn::SwapChain swapchain;
30 dawn::TextureView depthStencilView;
31
32 dawn::Buffer modelBuffer;
33 std::array<dawn::Buffer, 2> particleBuffers;
34
35 dawn::RenderPipeline renderPipeline;
36
37 dawn::Buffer updateParams;
38 dawn::ComputePipeline updatePipeline;
39 std::array<dawn::BindGroup, 2> updateBGs;
40
41 size_t pingpong = 0;
42
43 static const uint32_t kNumParticles = 1000;
44
45 struct Particle {
46 glm::vec2 pos;
47 glm::vec2 vel;
48 };
49
50 struct SimParams {
51 float deltaT;
52 float rule1Distance;
53 float rule2Distance;
54 float rule3Distance;
55 float rule1Scale;
56 float rule2Scale;
57 float rule3Scale;
58 int particleCount;
59 };
60
initBuffers()61 void initBuffers() {
62 glm::vec2 model[3] = {
63 {-0.01, -0.02},
64 {0.01, -0.02},
65 {0.00, 0.02},
66 };
67 modelBuffer = utils::CreateBufferFromData(device, model, sizeof(model), dawn::BufferUsageBit::Vertex);
68
69 SimParams params = { 0.04f, 0.1f, 0.025f, 0.025f, 0.02f, 0.05f, 0.005f, kNumParticles };
70 updateParams = utils::CreateBufferFromData(device, ¶ms, sizeof(params), dawn::BufferUsageBit::Uniform);
71
72 std::vector<Particle> initialParticles(kNumParticles);
73 {
74 std::mt19937 generator;
75 std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
76 for (auto& p : initialParticles)
77 {
78 p.pos = glm::vec2(dist(generator), dist(generator));
79 p.vel = glm::vec2(dist(generator), dist(generator)) * 0.1f;
80 }
81 }
82
83 for (size_t i = 0; i < 2; i++) {
84 dawn::BufferDescriptor descriptor;
85 descriptor.size = sizeof(Particle) * kNumParticles;
86 descriptor.usage = dawn::BufferUsageBit::CopyDst | dawn::BufferUsageBit::Vertex |
87 dawn::BufferUsageBit::Storage;
88 particleBuffers[i] = device.CreateBuffer(&descriptor);
89
90 particleBuffers[i].SetSubData(0,
91 sizeof(Particle) * kNumParticles,
92 reinterpret_cast<uint8_t*>(initialParticles.data()));
93 }
94 }
95
initRender()96 void initRender() {
97 dawn::ShaderModule vsModule = utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"(
98 #version 450
99 layout(location = 0) in vec2 a_particlePos;
100 layout(location = 1) in vec2 a_particleVel;
101 layout(location = 2) in vec2 a_pos;
102 void main() {
103 float angle = -atan(a_particleVel.x, a_particleVel.y);
104 vec2 pos = vec2(a_pos.x * cos(angle) - a_pos.y * sin(angle),
105 a_pos.x * sin(angle) + a_pos.y * cos(angle));
106 gl_Position = vec4(pos + a_particlePos, 0, 1);
107 }
108 )");
109
110 dawn::ShaderModule fsModule =
111 utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"(
112 #version 450
113 layout(location = 0) out vec4 fragColor;
114 void main() {
115 fragColor = vec4(1.0);
116 }
117 )");
118
119 depthStencilView = CreateDefaultDepthStencilView(device);
120
121 utils::ComboRenderPipelineDescriptor descriptor(device);
122 descriptor.cVertexStage.module = vsModule;
123 descriptor.cFragmentStage.module = fsModule;
124
125 descriptor.cVertexInput.bufferCount = 2;
126 descriptor.cVertexInput.cBuffers[0].stride = sizeof(Particle);
127 descriptor.cVertexInput.cBuffers[0].stepMode = dawn::InputStepMode::Instance;
128 descriptor.cVertexInput.cBuffers[0].attributeCount = 2;
129 descriptor.cVertexInput.cAttributes[0].offset = offsetof(Particle, pos);
130 descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float2;
131 descriptor.cVertexInput.cAttributes[1].shaderLocation = 1;
132 descriptor.cVertexInput.cAttributes[1].offset = offsetof(Particle, vel);
133 descriptor.cVertexInput.cAttributes[1].format = dawn::VertexFormat::Float2;
134 descriptor.cVertexInput.cBuffers[1].stride = sizeof(glm::vec2);
135 descriptor.cVertexInput.cBuffers[1].attributeCount = 1;
136 descriptor.cVertexInput.cBuffers[1].attributes = &descriptor.cVertexInput.cAttributes[2];
137 descriptor.cVertexInput.cAttributes[2].shaderLocation = 2;
138 descriptor.cVertexInput.cAttributes[2].format = dawn::VertexFormat::Float2;
139 descriptor.depthStencilState = &descriptor.cDepthStencilState;
140 descriptor.cDepthStencilState.format = dawn::TextureFormat::Depth24PlusStencil8;
141 descriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat();
142
143 renderPipeline = device.CreateRenderPipeline(&descriptor);
144 }
145
initSim()146 void initSim() {
147 dawn::ShaderModule module = utils::CreateShaderModule(device, utils::ShaderStage::Compute, R"(
148 #version 450
149
150 struct Particle {
151 vec2 pos;
152 vec2 vel;
153 };
154
155 layout(std140, set = 0, binding = 0) uniform SimParams {
156 float deltaT;
157 float rule1Distance;
158 float rule2Distance;
159 float rule3Distance;
160 float rule1Scale;
161 float rule2Scale;
162 float rule3Scale;
163 int particleCount;
164 } params;
165
166 layout(std140, set = 0, binding = 1) buffer ParticlesA {
167 Particle particles[1000];
168 } particlesA;
169
170 layout(std140, set = 0, binding = 2) buffer ParticlesB {
171 Particle particles[1000];
172 } particlesB;
173
174 void main() {
175 // https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
176
177 uint index = gl_GlobalInvocationID.x;
178 if (index >= params.particleCount) { return; }
179
180 vec2 vPos = particlesA.particles[index].pos;
181 vec2 vVel = particlesA.particles[index].vel;
182
183 vec2 cMass = vec2(0.0, 0.0);
184 vec2 cVel = vec2(0.0, 0.0);
185 vec2 colVel = vec2(0.0, 0.0);
186 int cMassCount = 0;
187 int cVelCount = 0;
188
189 vec2 pos;
190 vec2 vel;
191 for (int i = 0; i < params.particleCount; ++i) {
192 if (i == index) { continue; }
193 pos = particlesA.particles[i].pos.xy;
194 vel = particlesA.particles[i].vel.xy;
195
196 if (distance(pos, vPos) < params.rule1Distance) {
197 cMass += pos;
198 cMassCount++;
199 }
200 if (distance(pos, vPos) < params.rule2Distance) {
201 colVel -= (pos - vPos);
202 }
203 if (distance(pos, vPos) < params.rule3Distance) {
204 cVel += vel;
205 cVelCount++;
206 }
207 }
208 if (cMassCount > 0) {
209 cMass = cMass / cMassCount - vPos;
210 }
211 if (cVelCount > 0) {
212 cVel = cVel / cVelCount;
213 }
214
215 vVel += cMass * params.rule1Scale + colVel * params.rule2Scale + cVel * params.rule3Scale;
216
217 // clamp velocity for a more pleasing simulation.
218 vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
219
220 // kinematic update
221 vPos += vVel * params.deltaT;
222
223 // Wrap around boundary
224 if (vPos.x < -1.0) vPos.x = 1.0;
225 if (vPos.x > 1.0) vPos.x = -1.0;
226 if (vPos.y < -1.0) vPos.y = 1.0;
227 if (vPos.y > 1.0) vPos.y = -1.0;
228
229 particlesB.particles[index].pos = vPos;
230
231 // Write back
232 particlesB.particles[index].vel = vVel;
233 }
234 )");
235
236 auto bgl = utils::MakeBindGroupLayout(
237 device, {
238 {0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer},
239 {1, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer},
240 {2, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer},
241 });
242
243 dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl);
244
245 dawn::ComputePipelineDescriptor csDesc;
246 csDesc.layout = pl;
247
248 dawn::PipelineStageDescriptor computeStage;
249 computeStage.module = module;
250 computeStage.entryPoint = "main";
251 csDesc.computeStage = &computeStage;
252
253 updatePipeline = device.CreateComputePipeline(&csDesc);
254
255 for (uint32_t i = 0; i < 2; ++i) {
256 updateBGs[i] = utils::MakeBindGroup(device, bgl, {
257 {0, updateParams, 0, sizeof(SimParams)},
258 {1, particleBuffers[i], 0, kNumParticles * sizeof(Particle)},
259 {2, particleBuffers[(i + 1) % 2], 0, kNumParticles * sizeof(Particle)},
260 });
261 }
262 }
263
createCommandBuffer(const dawn::Texture backbuffer,size_t i)264 dawn::CommandBuffer createCommandBuffer(const dawn::Texture backbuffer, size_t i) {
265 static const uint64_t zeroOffsets[1] = {0};
266 auto& bufferDst = particleBuffers[(i + 1) % 2];
267 dawn::CommandEncoder encoder = device.CreateCommandEncoder();
268
269 {
270 dawn::ComputePassEncoder pass = encoder.BeginComputePass();
271 pass.SetPipeline(updatePipeline);
272 pass.SetBindGroup(0, updateBGs[i], 0, nullptr);
273 pass.Dispatch(kNumParticles, 1, 1);
274 pass.EndPass();
275 }
276
277 {
278 utils::ComboRenderPassDescriptor renderPass({backbuffer.CreateDefaultView()},
279 depthStencilView);
280 dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
281 pass.SetPipeline(renderPipeline);
282 pass.SetVertexBuffers(0, 1, &bufferDst, zeroOffsets);
283 pass.SetVertexBuffers(1, 1, &modelBuffer, zeroOffsets);
284 pass.Draw(3, kNumParticles, 0, 0);
285 pass.EndPass();
286 }
287
288 return encoder.Finish();
289 }
290
init()291 void init() {
292 device = CreateCppDawnDevice();
293
294 queue = device.CreateQueue();
295 swapchain = GetSwapChain(device);
296 swapchain.Configure(GetPreferredSwapChainTextureFormat(),
297 dawn::TextureUsageBit::OutputAttachment, 640, 480);
298
299 initBuffers();
300 initRender();
301 initSim();
302 }
303
frame()304 void frame() {
305 dawn::Texture backbuffer = swapchain.GetNextTexture();
306
307 dawn::CommandBuffer commandBuffer = createCommandBuffer(backbuffer, pingpong);
308 queue.Submit(1, &commandBuffer);
309 swapchain.Present(backbuffer);
310 DoFlush();
311
312 pingpong = (pingpong + 1) % 2;
313 }
314
main(int argc,const char * argv[])315 int main(int argc, const char* argv[]) {
316 if (!InitSample(argc, argv)) {
317 return 1;
318 }
319 init();
320
321 while (!ShouldQuit()) {
322 frame();
323 utils::USleep(16000);
324 }
325
326 // TODO release stuff
327 }
328