• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params, 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