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 <cstdio>
23 #include <cstdlib>
24 #include <vector>
25
26 wgpu::Device device;
27 wgpu::Queue queue;
28 wgpu::SwapChain swapchain;
29 wgpu::RenderPipeline pipeline;
30 wgpu::BindGroup bindGroup;
31 wgpu::Buffer ubo;
32
RandomFloat(float min,float max)33 float RandomFloat(float min, float max) {
34 float zeroOne = rand() / float(RAND_MAX);
35 return zeroOne * (max - min) + min;
36 }
37
38 constexpr size_t kNumTriangles = 10000;
39
40 // Aligned as minUniformBufferOffsetAlignment
41 struct alignas(256) ShaderData {
42 float scale;
43 float time;
44 float offsetX;
45 float offsetY;
46 float scalar;
47 float scalarOffset;
48 };
49
50 static std::vector<ShaderData> shaderData;
51
init()52 void init() {
53 device = CreateCppDawnDevice();
54
55 queue = device.GetQueue();
56 swapchain = GetSwapChain(device);
57 swapchain.Configure(GetPreferredSwapChainTextureFormat(), wgpu::TextureUsage::RenderAttachment,
58 640, 480);
59
60 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
61 [[block]] struct Constants {
62 scale : f32;
63 time : f32;
64 offsetX : f32;
65 offsetY : f32;
66 scalar : f32;
67 scalarOffset : f32;
68 };
69 [[group(0), binding(0)]] var<uniform> c : Constants;
70
71 struct VertexOut {
72 [[location(0)]] v_color : vec4<f32>;
73 [[builtin(position)]] Position : vec4<f32>;
74 };
75
76 [[stage(vertex)]] fn main([[builtin(vertex_index)]] VertexIndex : u32) -> VertexOut {
77 var positions : array<vec4<f32>, 3> = array<vec4<f32>, 3>(
78 vec4<f32>( 0.0, 0.1, 0.0, 1.0),
79 vec4<f32>(-0.1, -0.1, 0.0, 1.0),
80 vec4<f32>( 0.1, -0.1, 0.0, 1.0)
81 );
82
83 var colors : array<vec4<f32>, 3> = array<vec4<f32>, 3>(
84 vec4<f32>(1.0, 0.0, 0.0, 1.0),
85 vec4<f32>(0.0, 1.0, 0.0, 1.0),
86 vec4<f32>(0.0, 0.0, 1.0, 1.0)
87 );
88
89 var position : vec4<f32> = positions[VertexIndex];
90 var color : vec4<f32> = colors[VertexIndex];
91
92 // TODO(dawn:572): Revisit once modf has been reworked in WGSL.
93 var fade : f32 = c.scalarOffset + c.time * c.scalar / 10.0;
94 fade = fade - floor(fade);
95 if (fade < 0.5) {
96 fade = fade * 2.0;
97 } else {
98 fade = (1.0 - fade) * 2.0;
99 }
100
101 var xpos : f32 = position.x * c.scale;
102 var ypos : f32 = position.y * c.scale;
103 let angle : f32 = 3.14159 * 2.0 * fade;
104 let xrot : f32 = xpos * cos(angle) - ypos * sin(angle);
105 let yrot : f32 = xpos * sin(angle) + ypos * cos(angle);
106 xpos = xrot + c.offsetX;
107 ypos = yrot + c.offsetY;
108
109 var output : VertexOut;
110 output.v_color = vec4<f32>(fade, 1.0 - fade, 0.0, 1.0) + color;
111 output.Position = vec4<f32>(xpos, ypos, 0.0, 1.0);
112 return output;
113 })");
114
115 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
116 [[stage(fragment)]] fn main([[location(0)]] v_color : vec4<f32>)
117 -> [[location(0)]] vec4<f32> {
118 return v_color;
119 })");
120
121 wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
122 device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform, true}});
123
124 utils::ComboRenderPipelineDescriptor descriptor;
125 descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
126 descriptor.vertex.module = vsModule;
127 descriptor.cFragment.module = fsModule;
128 descriptor.cTargets[0].format = GetPreferredSwapChainTextureFormat();
129
130 pipeline = device.CreateRenderPipeline(&descriptor);
131
132 shaderData.resize(kNumTriangles);
133 for (auto& data : shaderData) {
134 data.scale = RandomFloat(0.2f, 0.4f);
135 data.time = 0.0;
136 data.offsetX = RandomFloat(-0.9f, 0.9f);
137 data.offsetY = RandomFloat(-0.9f, 0.9f);
138 data.scalar = RandomFloat(0.5f, 2.0f);
139 data.scalarOffset = RandomFloat(0.0f, 10.0f);
140 }
141
142 wgpu::BufferDescriptor bufferDesc;
143 bufferDesc.size = kNumTriangles * sizeof(ShaderData);
144 bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
145 ubo = device.CreateBuffer(&bufferDesc);
146
147 bindGroup = utils::MakeBindGroup(device, bgl, {{0, ubo, 0, sizeof(ShaderData)}});
148 }
149
frame()150 void frame() {
151 wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView();
152
153 static int f = 0;
154 f++;
155 for (auto& data : shaderData) {
156 data.time = f / 60.0f;
157 }
158 queue.WriteBuffer(ubo, 0, shaderData.data(), kNumTriangles * sizeof(ShaderData));
159
160 utils::ComboRenderPassDescriptor renderPass({backbufferView});
161 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
162 {
163 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
164 pass.SetPipeline(pipeline);
165
166 for (size_t i = 0; i < kNumTriangles; i++) {
167 uint32_t offset = i * sizeof(ShaderData);
168 pass.SetBindGroup(0, bindGroup, 1, &offset);
169 pass.Draw(3);
170 }
171
172 pass.EndPass();
173 }
174
175 wgpu::CommandBuffer commands = encoder.Finish();
176 queue.Submit(1, &commands);
177 swapchain.Present();
178 DoFlush();
179 fprintf(stderr, "frame %i\n", f);
180 }
181
main(int argc,const char * argv[])182 int main(int argc, const char* argv[]) {
183 if (!InitSample(argc, argv)) {
184 return 1;
185 }
186 init();
187
188 while (!ShouldQuit()) {
189 utils::ScopedAutoreleasePool pool;
190 frame();
191 utils::USleep(16000);
192 }
193
194 // TODO release stuff
195 }
196