• 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/ScopedAutoreleasePool.h"
19 #include "utils/SystemUtils.h"
20 #include "utils/WGPUHelpers.h"
21 
22 #include <array>
23 #include <cstring>
24 #include <random>
25 
26 #include <glm/glm.hpp>
27 
28 wgpu::Device device;
29 wgpu::Queue queue;
30 wgpu::SwapChain swapchain;
31 wgpu::TextureView depthStencilView;
32 
33 wgpu::Buffer modelBuffer;
34 std::array<wgpu::Buffer, 2> particleBuffers;
35 
36 wgpu::RenderPipeline renderPipeline;
37 
38 wgpu::Buffer updateParams;
39 wgpu::ComputePipeline updatePipeline;
40 std::array<wgpu::BindGroup, 2> updateBGs;
41 
42 size_t pingpong = 0;
43 
44 static const uint32_t kNumParticles = 1000;
45 
46 struct Particle {
47     glm::vec2 pos;
48     glm::vec2 vel;
49 };
50 
51 struct SimParams {
52     float deltaT;
53     float rule1Distance;
54     float rule2Distance;
55     float rule3Distance;
56     float rule1Scale;
57     float rule2Scale;
58     float rule3Scale;
59     int particleCount;
60 };
61 
initBuffers()62 void initBuffers() {
63     glm::vec2 model[3] = {
64         {-0.01, -0.02},
65         {0.01, -0.02},
66         {0.00, 0.02},
67     };
68     modelBuffer =
69         utils::CreateBufferFromData(device, model, sizeof(model), wgpu::BufferUsage::Vertex);
70 
71     SimParams params = {0.04f, 0.1f, 0.025f, 0.025f, 0.02f, 0.05f, 0.005f, kNumParticles};
72     updateParams =
73         utils::CreateBufferFromData(device, &params, sizeof(params), wgpu::BufferUsage::Uniform);
74 
75     std::vector<Particle> initialParticles(kNumParticles);
76     {
77         std::mt19937 generator;
78         std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
79         for (auto& p : initialParticles) {
80             p.pos = glm::vec2(dist(generator), dist(generator));
81             p.vel = glm::vec2(dist(generator), dist(generator)) * 0.1f;
82         }
83     }
84 
85     for (size_t i = 0; i < 2; i++) {
86         wgpu::BufferDescriptor descriptor;
87         descriptor.size = sizeof(Particle) * kNumParticles;
88         descriptor.usage =
89             wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Storage;
90         particleBuffers[i] = device.CreateBuffer(&descriptor);
91 
92         queue.WriteBuffer(particleBuffers[i], 0,
93                           reinterpret_cast<uint8_t*>(initialParticles.data()),
94                           sizeof(Particle) * kNumParticles);
95     }
96 }
97 
initRender()98 void initRender() {
99     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
100         struct VertexIn {
101             [[location(0)]] a_particlePos : vec2<f32>;
102             [[location(1)]] a_particleVel : vec2<f32>;
103             [[location(2)]] a_pos : vec2<f32>;
104         };
105 
106         [[stage(vertex)]]
107         fn main(input : VertexIn) -> [[builtin(position)]] vec4<f32> {
108             var angle : f32 = -atan2(input.a_particleVel.x, input.a_particleVel.y);
109             var pos : vec2<f32> = vec2<f32>(
110                 (input.a_pos.x * cos(angle)) - (input.a_pos.y * sin(angle)),
111                 (input.a_pos.x * sin(angle)) + (input.a_pos.y * cos(angle)));
112             return vec4<f32>(pos + input.a_particlePos, 0.0, 1.0);
113         }
114     )");
115 
116     wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
117         [[stage(fragment)]]
118         fn main() -> [[location(0)]] vec4<f32> {
119             return vec4<f32>(1.0, 1.0, 1.0, 1.0);
120         }
121     )");
122 
123     depthStencilView = CreateDefaultDepthStencilView(device);
124 
125     utils::ComboRenderPipelineDescriptor descriptor;
126 
127     descriptor.vertex.module = vsModule;
128     descriptor.vertex.bufferCount = 2;
129     descriptor.cBuffers[0].arrayStride = sizeof(Particle);
130     descriptor.cBuffers[0].stepMode = wgpu::VertexStepMode::Instance;
131     descriptor.cBuffers[0].attributeCount = 2;
132     descriptor.cAttributes[0].offset = offsetof(Particle, pos);
133     descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x2;
134     descriptor.cAttributes[1].shaderLocation = 1;
135     descriptor.cAttributes[1].offset = offsetof(Particle, vel);
136     descriptor.cAttributes[1].format = wgpu::VertexFormat::Float32x2;
137     descriptor.cBuffers[1].arrayStride = sizeof(glm::vec2);
138     descriptor.cBuffers[1].attributeCount = 1;
139     descriptor.cBuffers[1].attributes = &descriptor.cAttributes[2];
140     descriptor.cAttributes[2].shaderLocation = 2;
141     descriptor.cAttributes[2].format = wgpu::VertexFormat::Float32x2;
142 
143     descriptor.cFragment.module = fsModule;
144     descriptor.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8);
145     descriptor.cTargets[0].format = GetPreferredSwapChainTextureFormat();
146 
147     renderPipeline = device.CreateRenderPipeline(&descriptor);
148 }
149 
initSim()150 void initSim() {
151     wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
152         struct Particle {
153             pos : vec2<f32>;
154             vel : vec2<f32>;
155         };
156         [[block]] struct SimParams {
157             deltaT : f32;
158             rule1Distance : f32;
159             rule2Distance : f32;
160             rule3Distance : f32;
161             rule1Scale : f32;
162             rule2Scale : f32;
163             rule3Scale : f32;
164             particleCount : u32;
165         };
166         [[block]] struct Particles {
167             particles : array<Particle>;
168         };
169         [[binding(0), group(0)]] var<uniform> params : SimParams;
170         [[binding(1), group(0)]] var<storage, read> particlesA : Particles;
171         [[binding(2), group(0)]] var<storage, read_write> particlesB : Particles;
172 
173         // https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
174         [[stage(compute), workgroup_size(1)]]
175         fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) {
176             var index : u32 = GlobalInvocationID.x;
177             if (index >= params.particleCount) {
178                 return;
179             }
180             var vPos : vec2<f32> = particlesA.particles[index].pos;
181             var vVel : vec2<f32> = particlesA.particles[index].vel;
182             var cMass : vec2<f32> = vec2<f32>(0.0, 0.0);
183             var cVel : vec2<f32> = vec2<f32>(0.0, 0.0);
184             var colVel : vec2<f32> = vec2<f32>(0.0, 0.0);
185             var cMassCount : u32 = 0u;
186             var cVelCount : u32 = 0u;
187             var pos : vec2<f32>;
188             var vel : vec2<f32>;
189 
190             for (var i : u32 = 0u; i < params.particleCount; i = i + 1u) {
191                 if (i == index) {
192                     continue;
193                 }
194 
195                 pos = particlesA.particles[i].pos.xy;
196                 vel = particlesA.particles[i].vel.xy;
197                 if (distance(pos, vPos) < params.rule1Distance) {
198                     cMass = cMass + pos;
199                     cMassCount = cMassCount + 1u;
200                 }
201                 if (distance(pos, vPos) < params.rule2Distance) {
202                     colVel = colVel - (pos - vPos);
203                 }
204                 if (distance(pos, vPos) < params.rule3Distance) {
205                     cVel = cVel + vel;
206                     cVelCount = cVelCount + 1u;
207                 }
208             }
209 
210             if (cMassCount > 0u) {
211                 cMass = (cMass / vec2<f32>(f32(cMassCount), f32(cMassCount))) - vPos;
212             }
213 
214             if (cVelCount > 0u) {
215                 cVel = cVel / vec2<f32>(f32(cVelCount), f32(cVelCount));
216             }
217             vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) +
218                 (cVel * params.rule3Scale);
219 
220             // clamp velocity for a more pleasing simulation
221             vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
222             // kinematic update
223             vPos = vPos + (vVel * params.deltaT);
224 
225             // Wrap around boundary
226             if (vPos.x < -1.0) {
227                 vPos.x = 1.0;
228             }
229             if (vPos.x > 1.0) {
230                 vPos.x = -1.0;
231             }
232             if (vPos.y < -1.0) {
233                 vPos.y = 1.0;
234             }
235             if (vPos.y > 1.0) {
236                 vPos.y = -1.0;
237             }
238 
239             // Write back
240             particlesB.particles[index].pos = vPos;
241             particlesB.particles[index].vel = vVel;
242             return;
243         }
244     )");
245 
246     auto bgl = utils::MakeBindGroupLayout(
247         device, {
248                     {0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform},
249                     {1, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
250                     {2, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
251                 });
252 
253     wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl);
254 
255     wgpu::ComputePipelineDescriptor csDesc;
256     csDesc.layout = pl;
257     csDesc.compute.module = module;
258     csDesc.compute.entryPoint = "main";
259     updatePipeline = device.CreateComputePipeline(&csDesc);
260 
261     for (uint32_t i = 0; i < 2; ++i) {
262         updateBGs[i] = utils::MakeBindGroup(
263             device, bgl,
264             {
265                 {0, updateParams, 0, sizeof(SimParams)},
266                 {1, particleBuffers[i], 0, kNumParticles * sizeof(Particle)},
267                 {2, particleBuffers[(i + 1) % 2], 0, kNumParticles * sizeof(Particle)},
268             });
269     }
270 }
271 
createCommandBuffer(const wgpu::TextureView backbufferView,size_t i)272 wgpu::CommandBuffer createCommandBuffer(const wgpu::TextureView backbufferView, size_t i) {
273     auto& bufferDst = particleBuffers[(i + 1) % 2];
274     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
275 
276     {
277         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
278         pass.SetPipeline(updatePipeline);
279         pass.SetBindGroup(0, updateBGs[i]);
280         pass.Dispatch(kNumParticles);
281         pass.EndPass();
282     }
283 
284     {
285         utils::ComboRenderPassDescriptor renderPass({backbufferView}, depthStencilView);
286         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
287         pass.SetPipeline(renderPipeline);
288         pass.SetVertexBuffer(0, bufferDst);
289         pass.SetVertexBuffer(1, modelBuffer);
290         pass.Draw(3, kNumParticles);
291         pass.EndPass();
292     }
293 
294     return encoder.Finish();
295 }
296 
init()297 void init() {
298     device = CreateCppDawnDevice();
299 
300     queue = device.GetQueue();
301     swapchain = GetSwapChain(device);
302     swapchain.Configure(GetPreferredSwapChainTextureFormat(), wgpu::TextureUsage::RenderAttachment,
303                         640, 480);
304 
305     initBuffers();
306     initRender();
307     initSim();
308 }
309 
frame()310 void frame() {
311     wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView();
312 
313     wgpu::CommandBuffer commandBuffer = createCommandBuffer(backbufferView, pingpong);
314     queue.Submit(1, &commandBuffer);
315     swapchain.Present();
316     DoFlush();
317 
318     pingpong = (pingpong + 1) % 2;
319 }
320 
main(int argc,const char * argv[])321 int main(int argc, const char* argv[]) {
322     if (!InitSample(argc, argv)) {
323         return 1;
324     }
325     init();
326 
327     while (!ShouldQuit()) {
328         utils::ScopedAutoreleasePool pool;
329         frame();
330         utils::USleep(16000);
331     }
332 
333     // TODO release stuff
334 }
335