1 // Copyright 2018 The Amber 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 "src/dawn/engine_dawn.h"
16
17 #include <algorithm>
18 #include <cassert>
19 #include <cstdint>
20 #include <cstring>
21 #include <iostream>
22 #include <limits>
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include "amber/amber_dawn.h"
28 #include "dawn/dawncpp.h"
29 #include "src/dawn/pipeline_info.h"
30 #include "src/format.h"
31 #include "src/make_unique.h"
32 #include "src/sleep.h"
33
34 namespace amber {
35 namespace dawn {
36
37 namespace {
38
39 // The minimum multiple row pitch observed on Dawn on Metal. Increase this
40 // as needed for other Dawn backends.
41 static const uint32_t kMinimumImageRowPitch = 256;
42 static const float kLodMin = 0.0;
43 static const float kLodMax = 1000.0;
44 static const uint32_t kMaxColorAttachments = 4u;
45 static const uint32_t kMaxVertexInputs = 16u;
46 static const uint32_t kMaxVertexAttributes = 16u;
47 static const uint32_t kMaxDawnBindGroup = 4u;
48
49 // A DS for creating and setting the defaults for VertexInputDescriptor
50 // Copied from Dawn utils source code.
51 struct ComboVertexInputDescriptor {
ComboVertexInputDescriptoramber::dawn::__anonb40c6e430111::ComboVertexInputDescriptor52 ComboVertexInputDescriptor() {
53 ::dawn::VertexInputDescriptor* descriptor =
54 reinterpret_cast<::dawn::VertexInputDescriptor*>(this);
55
56 descriptor->indexFormat = ::dawn::IndexFormat::Uint32;
57 descriptor->bufferCount = 0;
58 descriptor->nextInChain = nullptr;
59
60 // Fill the default values for vertexBuffers and vertexAttributes in
61 // buffers.
62 ::dawn::VertexAttributeDescriptor vertexAttribute;
63 vertexAttribute.shaderLocation = 0;
64 vertexAttribute.offset = 0;
65 vertexAttribute.format = ::dawn::VertexFormat::Float;
66 for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) {
67 cAttributes[i] = vertexAttribute;
68 }
69 for (uint32_t i = 0; i < kMaxVertexBuffers; ++i) {
70 cBuffers[i].stride = 0;
71 cBuffers[i].stepMode = ::dawn::InputStepMode::Vertex;
72 cBuffers[i].attributeCount = 0;
73 cBuffers[i].attributes = nullptr;
74 }
75 // cBuffers[i].attributes points to somewhere in cAttributes.
76 // cBuffers[0].attributes points to &cAttributes[0] by default. Assuming
77 // cBuffers[0] has two attributes, then cBuffers[1].attributes should
78 // point to &cAttributes[2]. Likewise, if cBuffers[1] has 3 attributes,
79 // then cBuffers[2].attributes should point to &cAttributes[5].
80
81 // In amber-dawn, the vertex input descriptor is always created assuming
82 // these relationships are one to one i.e. cBuffers[i].attributes is always
83 // pointing to &cAttributes[i] and cBuffers[i].attributeCount == 1
84 cBuffers[0].attributes = &cAttributes[0];
85 descriptor->buffers = &cBuffers[0];
86 }
87
88 static const uint32_t kMaxVertexBuffers = 16u;
89 static const uint32_t kMaxVertexBufferStride = 2048u;
90 const void* nextInChain = nullptr;
91 ::dawn::IndexFormat indexFormat;
92 uint32_t bufferCount;
93 ::dawn::VertexBufferDescriptor const* buffers;
94
95 std::array<::dawn::VertexBufferDescriptor, kMaxVertexBuffers> cBuffers;
96 std::array<::dawn::VertexAttributeDescriptor, kMaxVertexAttributes>
97 cAttributes;
98 };
99
100 // This structure is a container for a few variables that are created during
101 // CreateRenderPipelineDescriptor and CreateRenderPassDescriptor and we want to
102 // make sure they don't go out of scope before we are done with them
103 struct DawnPipelineHelper {
104 Result CreateRenderPipelineDescriptor(
105 const RenderPipelineInfo& render_pipeline,
106 const ::dawn::Device& device,
107 const bool ignore_vertex_and_Index_buffers,
108 const PipelineData* pipeline_data);
109 Result CreateRenderPassDescriptor(
110 const RenderPipelineInfo& render_pipeline,
111 const ::dawn::Device& device,
112 const std::vector<::dawn::TextureView>& texture_view,
113 const ::dawn::LoadOp load_op);
114 ::dawn::RenderPipelineDescriptor renderPipelineDescriptor;
115 ::dawn::RenderPassDescriptor renderPassDescriptor;
116
117 ComboVertexInputDescriptor vertexInputDescriptor;
118 ::dawn::RasterizationStateDescriptor rasterizationState;
119
120 private:
121 ::dawn::ProgrammableStageDescriptor fragmentStage;
122 ::dawn::ProgrammableStageDescriptor vertexStage;
123 ::dawn::RenderPassColorAttachmentDescriptor
124 colorAttachmentsInfoPtr[kMaxColorAttachments];
125 ::dawn::RenderPassDepthStencilAttachmentDescriptor depthStencilAttachmentInfo;
126 std::array<::dawn::ColorStateDescriptor*, kMaxColorAttachments> colorStates;
127 ::dawn::DepthStencilStateDescriptor depthStencilState;
128 ::dawn::ColorStateDescriptor colorStatesDescriptor[kMaxColorAttachments];
129 ::dawn::ColorStateDescriptor colorStateDescriptor;
130 ::dawn::StencilStateFaceDescriptor stencil_front;
131 ::dawn::StencilStateFaceDescriptor stencil_back;
132 ::dawn::BlendDescriptor alpha_blend;
133 ::dawn::BlendDescriptor color_blend;
134 std::string vertexEntryPoint;
135 std::string fragmentEntryPoint;
136 std::array<::dawn::RenderPassColorAttachmentDescriptor, kMaxColorAttachments>
137 colorAttachmentsInfo;
138 ::dawn::TextureDescriptor depthStencilDescriptor;
139 ::dawn::Texture depthStencilTexture;
140 ::dawn::TextureView depthStencilView;
141 };
142
143 // Creates a device-side texture, and returns it through |result_ptr|.
144 // Assumes the device exists and is valid. Assumes result_ptr is not null.
145 // Returns a result code.
MakeTexture(const::dawn::Device & device,::dawn::TextureFormat format,uint32_t width,uint32_t height,::dawn::Texture * result_ptr)146 Result MakeTexture(const ::dawn::Device& device,
147 ::dawn::TextureFormat format,
148 uint32_t width,
149 uint32_t height,
150 ::dawn::Texture* result_ptr) {
151 assert(device);
152 assert(result_ptr);
153 assert(width * height > 0);
154 ::dawn::TextureDescriptor descriptor;
155 descriptor.dimension = ::dawn::TextureDimension::e2D;
156 descriptor.size.width = width;
157 descriptor.size.height = height;
158 descriptor.size.depth = 1;
159 descriptor.arrayLayerCount = 1;
160 descriptor.format = format;
161 descriptor.mipLevelCount = 1;
162 descriptor.sampleCount = 1;
163 descriptor.usage =
164 ::dawn::TextureUsage::CopySrc | ::dawn::TextureUsage::OutputAttachment;
165 // TODO(dneto): Get a better message by using the Dawn error callback.
166 *result_ptr = device.CreateTexture(&descriptor);
167 if (*result_ptr)
168 return {};
169 return Result("Dawn: Failed to allocate a framebuffer texture");
170 }
171
172 // Creates a device-side texture, and returns it through |result_ptr|.
173 // Assumes the device exists and is valid. Assumes result_ptr is not null.
174 // Returns a result code.
MakeDawnTexture(const::dawn::Device & device,::dawn::TextureFormat format,uint32_t width,uint32_t height)175 ::dawn::Texture MakeDawnTexture(const ::dawn::Device& device,
176 ::dawn::TextureFormat format,
177 uint32_t width,
178 uint32_t height) {
179 assert(device);
180 assert(width * height > 0);
181 ::dawn::TextureDescriptor descriptor;
182 descriptor.dimension = ::dawn::TextureDimension::e2D;
183 descriptor.size.width = width;
184 descriptor.size.height = height;
185 descriptor.size.depth = 1;
186 descriptor.arrayLayerCount = 1;
187 descriptor.format = format;
188 descriptor.mipLevelCount = 1;
189 descriptor.sampleCount = 1;
190 descriptor.usage =
191 ::dawn::TextureUsage::CopySrc | ::dawn::TextureUsage::OutputAttachment;
192
193 return device.CreateTexture(&descriptor);
194 }
195
196 // Result status object and data pointer resulting from a buffer mapping.
197 struct MapResult {
198 Result result;
199 const void* data = nullptr;
200 uint64_t dataLength = 0;
201 };
202
203 // Handles the update from an asynchronous buffer map request, updating the
204 // state of the MapResult object hidden inside the |userdata| parameter.
205 // On a successful mapping outcome, set the data pointer in the map result.
206 // Otherwise set the map result object to an error, and the data member is
207 // not changed.
HandleBufferMapCallback(DawnBufferMapAsyncStatus status,const void * data,uint64_t dataLength,void * userdata)208 void HandleBufferMapCallback(DawnBufferMapAsyncStatus status,
209 const void* data,
210 uint64_t dataLength,
211 void* userdata) {
212 MapResult& map_result = *reinterpret_cast<MapResult*>(userdata);
213 switch (status) {
214 case DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS:
215 map_result.data = data;
216 map_result.dataLength = dataLength;
217 break;
218 case DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR:
219 map_result.result = Result("Buffer map for reading failed: error");
220 break;
221 case DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN:
222 case DAWN_BUFFER_MAP_ASYNC_STATUS_FORCE32:
223 map_result.result = Result("Buffer map for reading failed: unknown");
224 break;
225 case DAWN_BUFFER_MAP_ASYNC_STATUS_DEVICE_LOST:
226 map_result.result = Result("Buffer map for reading failed: device lost");
227 break;
228 }
229 }
230
231 // Returns |value| but rounded up to a multiple of |alignment|. |alignment| is
232 // assumed to be a power of 2.
Align(uint32_t value,size_t alignment)233 uint32_t Align(uint32_t value, size_t alignment) {
234 assert(alignment <= std::numeric_limits<uint32_t>::max());
235 assert(alignment != 0);
236 uint32_t alignment32 = static_cast<uint32_t>(alignment);
237 return (value + (alignment32 - 1)) & ~(alignment32 - 1);
238 }
239
240 } // namespace
241
242 // Maps the given buffer. Assumes the buffer has usage bit
243 // ::dawn::BufferUsage::MapRead set. Returns a MapResult structure, with
244 // the status saved in the |result| member and the host pointer to the mapped
245 // data in the |data| member. Mapping a buffer can fail if the context is
246 // lost, for example. In the failure case, the |data| member will be null.
MapBuffer(const::dawn::Device & device,const::dawn::Buffer & buf)247 MapResult MapBuffer(const ::dawn::Device& device, const ::dawn::Buffer& buf) {
248 MapResult map_result;
249
250 buf.MapReadAsync(
251 HandleBufferMapCallback,
252 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&map_result)));
253 device.Tick();
254 // Wait until the callback has been processed. Use an exponential backoff
255 // interval, but cap it at one second intervals. But never loop forever.
256 const int max_iters = 100;
257 const int one_second_in_us = 1000000;
258 for (int iters = 0, interval = 1;
259 !map_result.data && map_result.result.IsSuccess();
260 iters++, interval = std::min(2 * interval, one_second_in_us)) {
261 device.Tick();
262 if (iters > max_iters) {
263 map_result.result = Result("MapBuffer timed out after 100 iterations");
264 break;
265 }
266 USleep(uint32_t(interval));
267 }
268 return map_result;
269 }
270
271 // Creates and returns a dawn BufferCopyView
272 // Copied from Dawn utils source code.
CreateBufferCopyView(::dawn::Buffer buffer,uint64_t offset,uint32_t rowPitch,uint32_t imageHeight)273 ::dawn::BufferCopyView CreateBufferCopyView(::dawn::Buffer buffer,
274 uint64_t offset,
275 uint32_t rowPitch,
276 uint32_t imageHeight) {
277 ::dawn::BufferCopyView bufferCopyView;
278 bufferCopyView.buffer = buffer;
279 bufferCopyView.offset = offset;
280 bufferCopyView.rowPitch = rowPitch;
281 bufferCopyView.imageHeight = imageHeight;
282
283 return bufferCopyView;
284 }
285
286 // Creates and returns a dawn TextureCopyView
287 // Copied from Dawn utils source code.
CreateTextureCopyView(::dawn::Texture texture,uint32_t mipLevel,uint32_t arrayLayer,::dawn::Origin3D origin)288 ::dawn::TextureCopyView CreateTextureCopyView(::dawn::Texture texture,
289 uint32_t mipLevel,
290 uint32_t arrayLayer,
291 ::dawn::Origin3D origin) {
292 ::dawn::TextureCopyView textureCopyView;
293 textureCopyView.texture = texture;
294 textureCopyView.mipLevel = mipLevel;
295 textureCopyView.arrayLayer = arrayLayer;
296 textureCopyView.origin = origin;
297
298 return textureCopyView;
299 }
300
MapDeviceTextureToHostBuffer(const RenderPipelineInfo & render_pipeline,const::dawn::Device & device)301 Result EngineDawn::MapDeviceTextureToHostBuffer(
302 const RenderPipelineInfo& render_pipeline,
303 const ::dawn::Device& device) {
304 const auto width = render_pipeline.pipeline->GetFramebufferWidth();
305 const auto height = render_pipeline.pipeline->GetFramebufferHeight();
306
307 const auto pixelSize = render_pipeline.pipeline->GetColorAttachments()[0]
308 .buffer->GetElementStride();
309 const auto dawn_row_pitch = Align(width * pixelSize, kMinimumImageRowPitch);
310 const auto size = height * dawn_row_pitch;
311 // Create a temporary buffer to hold the color attachment content and can
312 // be mapped
313 ::dawn::BufferDescriptor descriptor;
314 descriptor.size = size;
315 descriptor.usage =
316 ::dawn::BufferUsage::CopyDst | ::dawn::BufferUsage::MapRead;
317 ::dawn::Buffer copy_buffer = device.CreateBuffer(&descriptor);
318 ::dawn::BufferCopyView copy_buffer_view =
319 CreateBufferCopyView(copy_buffer, 0, dawn_row_pitch, 0);
320 ::dawn::Origin3D origin3D;
321 origin3D.x = 0;
322 origin3D.y = 0;
323 origin3D.z = 0;
324
325 for (uint32_t i = 0;
326 i < render_pipeline.pipeline->GetColorAttachments().size(); i++) {
327 ::dawn::TextureCopyView device_texture_view =
328 CreateTextureCopyView(textures_[i], 0, 0, origin3D);
329 ::dawn::Extent3D copySize = {width, height, 1};
330 auto encoder = device.CreateCommandEncoder();
331 encoder.CopyTextureToBuffer(&device_texture_view, ©_buffer_view,
332 ©Size);
333 auto commands = encoder.Finish();
334 auto queue = device.CreateQueue();
335 queue.Submit(1, &commands);
336
337 MapResult mapped_device_texture = MapBuffer(device, copy_buffer);
338 if (!mapped_device_texture.result.IsSuccess())
339 return mapped_device_texture.result;
340
341 auto& host_texture = render_pipeline.pipeline->GetColorAttachments()[i];
342 auto* values = host_texture.buffer->ValuePtr();
343 auto row_stride = pixelSize * width;
344 assert(row_stride * height == host_texture.buffer->GetSizeInBytes());
345 // Each Dawn row has enough data to fill the target row.
346 assert(dawn_row_pitch >= row_stride);
347 values->resize(host_texture.buffer->GetSizeInBytes());
348 // Copy the framebuffer contents back into the host-side
349 // framebuffer-buffer. In the Dawn buffer, the row stride is a multiple of
350 // kMinimumImageRowPitch bytes, so it might have padding therefore memcpy
351 // is done row by row.
352 for (uint h = 0; h < height; h++) {
353 std::memcpy(values->data() + h * row_stride,
354 static_cast<const uint8_t*>(mapped_device_texture.data) +
355 h * dawn_row_pitch,
356 row_stride);
357 }
358 // Always unmap the buffer at the end of the engine's command.
359 copy_buffer.Unmap();
360 }
361 return {};
362 }
363
MapDeviceBufferToHostBuffer(const ComputePipelineInfo & compute_pipeline,const::dawn::Device & device)364 Result EngineDawn::MapDeviceBufferToHostBuffer(
365 const ComputePipelineInfo& compute_pipeline,
366 const ::dawn::Device& device) {
367 for (uint32_t i = 0; i < compute_pipeline.pipeline->GetBuffers().size();
368 i++) {
369 auto& device_buffer = compute_pipeline.buffers[i];
370 auto& host_buffer = compute_pipeline.pipeline->GetBuffers()[i];
371
372 // Create a copy of device buffer to use it in a map read operation.
373 // It's not possible to simply set this bit on the existing buffers since:
374 // Device error: Only CopyDst is allowed with MapRead
375 ::dawn::BufferDescriptor descriptor;
376 descriptor.size = host_buffer.buffer->GetSizeInBytes();
377 descriptor.usage =
378 ::dawn::BufferUsage::CopyDst | ::dawn::BufferUsage::MapRead;
379 const auto copy_device_buffer = device.CreateBuffer(&descriptor);
380 const uint64_t source_offset = 0;
381 const uint64_t destination_offset = 0;
382 const uint64_t copy_size =
383 static_cast<uint64_t>(host_buffer.buffer->GetSizeInBytes());
384 auto encoder = device.CreateCommandEncoder();
385 encoder.CopyBufferToBuffer(device_buffer, source_offset, copy_device_buffer,
386 destination_offset, copy_size);
387 auto commands = encoder.Finish();
388 auto queue = device.CreateQueue();
389 queue.Submit(1, &commands);
390
391 MapResult mapped_device_buffer = MapBuffer(device, copy_device_buffer);
392 auto* values = host_buffer.buffer->ValuePtr();
393 values->resize(host_buffer.buffer->GetSizeInBytes());
394 std::memcpy(values->data(),
395 static_cast<const uint8_t*>(mapped_device_buffer.data),
396 copy_size);
397
398 copy_device_buffer.Unmap();
399 if (!mapped_device_buffer.result.IsSuccess())
400 return mapped_device_buffer.result;
401 }
402 return {};
403 }
404
405 // Creates a dawn buffer of |size| bytes with TransferDst and the given usage
406 // copied from Dawn utils source code
CreateBufferFromData(const::dawn::Device & device,const void * data,uint64_t size,::dawn::BufferUsage usage)407 ::dawn::Buffer CreateBufferFromData(const ::dawn::Device& device,
408 const void* data,
409 uint64_t size,
410 ::dawn::BufferUsage usage) {
411 ::dawn::BufferDescriptor descriptor;
412 descriptor.size = size;
413 descriptor.usage = usage | ::dawn::BufferUsage::CopyDst;
414
415 ::dawn::Buffer buffer = device.CreateBuffer(&descriptor);
416 if (data != nullptr)
417 buffer.SetSubData(0, size, reinterpret_cast<const uint8_t*>(data));
418 return buffer;
419 }
420
421 // Creates a bind group.
422 // Helpers to make creating bind groups look nicer:
423 //
424 // utils::MakeBindGroup(device, layout, {
425 // {0, mySampler},
426 // {1, myBuffer, offset, size},
427 // {3, myTexture}
428 // });
429
430 // Structure with one constructor per-type of bindings, so that the
431 // initializer_list accepts bindings with the right type and no extra
432 // information.
433 struct BindingInitializationHelper {
BindingInitializationHelperamber::dawn::BindingInitializationHelper434 BindingInitializationHelper(uint32_t binding,
435 const ::dawn::Buffer& buffer,
436 uint64_t offset,
437 uint64_t size)
438 : binding(binding), buffer(buffer), offset(offset), size(size) {}
439
GetAsBindingamber::dawn::BindingInitializationHelper440 ::dawn::BindGroupBinding GetAsBinding() const {
441 ::dawn::BindGroupBinding result;
442 result.binding = binding;
443 result.sampler = sampler;
444 result.textureView = textureView;
445 result.buffer = buffer;
446 result.offset = offset;
447 result.size = size;
448 return result;
449 }
450
451 uint32_t binding;
452 ::dawn::Sampler sampler;
453 ::dawn::TextureView textureView;
454 ::dawn::Buffer buffer;
455 uint64_t offset = 0;
456 uint64_t size = 0;
457 };
458
MakeBindGroup(const::dawn::Device & device,const::dawn::BindGroupLayout & layout,const std::vector<BindingInitializationHelper> & bindingsInitializer)459 ::dawn::BindGroup MakeBindGroup(
460 const ::dawn::Device& device,
461 const ::dawn::BindGroupLayout& layout,
462 const std::vector<BindingInitializationHelper>& bindingsInitializer) {
463 std::vector<::dawn::BindGroupBinding> bindings;
464 for (const BindingInitializationHelper& helper : bindingsInitializer) {
465 bindings.push_back(helper.GetAsBinding());
466 }
467
468 ::dawn::BindGroupDescriptor descriptor;
469 descriptor.layout = layout;
470 descriptor.bindingCount = bindings.size();
471 descriptor.bindings = bindings.data();
472
473 return device.CreateBindGroup(&descriptor);
474 }
475
476 // Creates a bind group layout.
477 // Copied from Dawn utils source code.
MakeBindGroupLayout(const::dawn::Device & device,const std::vector<::dawn::BindGroupLayoutBinding> & bindingsInitializer)478 ::dawn::BindGroupLayout MakeBindGroupLayout(
479 const ::dawn::Device& device,
480 const std::vector<::dawn::BindGroupLayoutBinding>& bindingsInitializer) {
481 constexpr ::dawn::ShaderStage kNoStages{};
482
483 std::vector<::dawn::BindGroupLayoutBinding> bindings;
484 for (const ::dawn::BindGroupLayoutBinding& binding : bindingsInitializer) {
485 if (binding.visibility != kNoStages) {
486 bindings.push_back(binding);
487 }
488 }
489
490 ::dawn::BindGroupLayoutDescriptor descriptor;
491 descriptor.bindingCount = static_cast<uint32_t>(bindings.size());
492 descriptor.bindings = bindings.data();
493 return device.CreateBindGroupLayout(&descriptor);
494 }
495
496 // Creates a basic pipeline layout.
497 // Copied from Dawn utils source code.
MakeBasicPipelineLayout(const::dawn::Device & device,std::vector<::dawn::BindGroupLayout> bindingInitializer)498 ::dawn::PipelineLayout MakeBasicPipelineLayout(
499 const ::dawn::Device& device,
500 std::vector<::dawn::BindGroupLayout> bindingInitializer) {
501 ::dawn::PipelineLayoutDescriptor descriptor;
502 descriptor.bindGroupLayoutCount = bindingInitializer.size();
503 descriptor.bindGroupLayouts = bindingInitializer.data();
504 return device.CreatePipelineLayout(&descriptor);
505 }
506
507 // Converts an Amber format to a Dawn texture format, and sends the result out
508 // through |dawn_format_ptr|. If the conversion fails, return an error
509 // result.
GetDawnTextureFormat(const::amber::Format & amber_format,::dawn::TextureFormat * dawn_format_ptr)510 Result GetDawnTextureFormat(const ::amber::Format& amber_format,
511 ::dawn::TextureFormat* dawn_format_ptr) {
512 if (!dawn_format_ptr)
513 return Result("Internal error: format pointer argument is null");
514 ::dawn::TextureFormat& dawn_format = *dawn_format_ptr;
515
516 switch (amber_format.GetFormatType()) {
517 // TODO(dneto): These are all the formats that Dawn currently knows about.
518 case FormatType::kR8G8B8A8_UNORM:
519 dawn_format = ::dawn::TextureFormat::RGBA8Unorm;
520 break;
521 case FormatType::kR8G8_UNORM:
522 dawn_format = ::dawn::TextureFormat::RG8Unorm;
523 break;
524 case FormatType::kR8_UNORM:
525 dawn_format = ::dawn::TextureFormat::R8Unorm;
526 break;
527 case FormatType::kR8G8B8A8_UINT:
528 dawn_format = ::dawn::TextureFormat::RGBA8Uint;
529 break;
530 case FormatType::kR8G8_UINT:
531 dawn_format = ::dawn::TextureFormat::RG8Uint;
532 break;
533 case FormatType::kR8_UINT:
534 dawn_format = ::dawn::TextureFormat::R8Uint;
535 break;
536 case FormatType::kB8G8R8A8_UNORM:
537 dawn_format = ::dawn::TextureFormat::BGRA8Unorm;
538 break;
539 case FormatType::kD32_SFLOAT_S8_UINT:
540 dawn_format = ::dawn::TextureFormat::Depth24PlusStencil8;
541 break;
542 default:
543 return Result(
544 "Amber format " +
545 std::to_string(static_cast<uint32_t>(amber_format.GetFormatType())) +
546 " is invalid for Dawn");
547 }
548
549 return {};
550 }
551 // Converts an Amber format to a Dawn Vertex format, and sends the result out
552 // through |dawn_format_ptr|. If the conversion fails, return an error
553 // result.
554 // TODO(sarahM0): support other ::dawn::VertexFormat
GetDawnVertexFormat(const::amber::Format & amber_format,::dawn::VertexFormat * dawn_format_ptr)555 Result GetDawnVertexFormat(const ::amber::Format& amber_format,
556 ::dawn::VertexFormat* dawn_format_ptr) {
557 ::dawn::VertexFormat& dawn_format = *dawn_format_ptr;
558 switch (amber_format.GetFormatType()) {
559 case FormatType::kR32_SFLOAT:
560 dawn_format = ::dawn::VertexFormat::Float;
561 break;
562 case FormatType::kR32G32_SFLOAT:
563 dawn_format = ::dawn::VertexFormat::Float2;
564 break;
565 case FormatType::kR32G32B32_SFLOAT:
566 dawn_format = ::dawn::VertexFormat::Float3;
567 break;
568 case FormatType::kR32G32B32A32_SFLOAT:
569 dawn_format = ::dawn::VertexFormat::Float4;
570 break;
571 case FormatType::kR8G8_SNORM:
572 dawn_format = ::dawn::VertexFormat::Char2Norm;
573 break;
574 case FormatType::kR8G8B8A8_UNORM:
575 dawn_format = ::dawn::VertexFormat::UChar4Norm;
576 break;
577 case FormatType::kR8G8B8A8_SNORM:
578 dawn_format = ::dawn::VertexFormat::Char4Norm;
579 break;
580 default:
581 return Result(
582 "Amber vertex format " +
583 std::to_string(static_cast<uint32_t>(amber_format.GetFormatType())) +
584 " is invalid for Dawn or is not supported in amber-dawn");
585 }
586 return {};
587 }
588
589 // Converts an Amber format to a Dawn Index format, and sends the result out
590 // through |dawn_format_ptr|. If the conversion fails, return an error
591 // result.
GetDawnIndexFormat(const::amber::Format & amber_format,::dawn::IndexFormat * dawn_format_ptr)592 Result GetDawnIndexFormat(const ::amber::Format& amber_format,
593 ::dawn::IndexFormat* dawn_format_ptr) {
594 ::dawn::IndexFormat& dawn_format = *dawn_format_ptr;
595 switch (amber_format.GetFormatType()) {
596 case FormatType::kR16_UINT:
597 dawn_format = ::dawn::IndexFormat::Uint16;
598 break;
599 case FormatType::kR32_UINT:
600 dawn_format = ::dawn::IndexFormat::Uint32;
601 break;
602 default:
603 return Result(
604 "Amber index format " +
605 std::to_string(static_cast<uint32_t>(amber_format.GetFormatType())) +
606 " is invalid for Dawn");
607 }
608 return {};
609 }
610
611 // Converts an Amber topology to a Dawn topology, and sends the result out
612 // through |dawn_topology_ptr|. It the conversion fails, return an error result.
GetDawnTopology(const::amber::Topology & amber_topology,::dawn::PrimitiveTopology * dawn_topology_ptr)613 Result GetDawnTopology(const ::amber::Topology& amber_topology,
614 ::dawn::PrimitiveTopology* dawn_topology_ptr) {
615 ::dawn::PrimitiveTopology& dawn_topology = *dawn_topology_ptr;
616 switch (amber_topology) {
617 case Topology::kPointList:
618 dawn_topology = ::dawn::PrimitiveTopology::PointList;
619 break;
620 case Topology::kLineList:
621 dawn_topology = ::dawn::PrimitiveTopology::LineList;
622 break;
623 case Topology::kLineStrip:
624 dawn_topology = ::dawn::PrimitiveTopology::LineStrip;
625 break;
626 case Topology::kTriangleList:
627 dawn_topology = ::dawn::PrimitiveTopology::TriangleList;
628 break;
629 case Topology::kTriangleStrip:
630 dawn_topology = ::dawn::PrimitiveTopology::TriangleStrip;
631 break;
632 default:
633 return Result("Amber PrimitiveTopology " +
634 std::to_string(static_cast<uint32_t>(amber_topology)) +
635 " is not supported in Dawn");
636 }
637 return {};
638 }
639
GetDawnCompareOp(::amber::CompareOp op)640 ::dawn::CompareFunction GetDawnCompareOp(::amber::CompareOp op) {
641 switch (op) {
642 case CompareOp::kNever:
643 return ::dawn::CompareFunction::Never;
644 case CompareOp::kLess:
645 return ::dawn::CompareFunction::Less;
646 case CompareOp::kEqual:
647 return ::dawn::CompareFunction::Equal;
648 case CompareOp::kLessOrEqual:
649 return ::dawn::CompareFunction::LessEqual;
650 case CompareOp::kGreater:
651 return ::dawn::CompareFunction::Greater;
652 case CompareOp::kNotEqual:
653 return ::dawn::CompareFunction::NotEqual;
654 case CompareOp::kGreaterOrEqual:
655 return ::dawn::CompareFunction::GreaterEqual;
656 case CompareOp::kAlways:
657 return ::dawn::CompareFunction::Always;
658 default:
659 return ::dawn::CompareFunction::Never;
660 }
661 }
662
GetDawnStencilOp(::amber::StencilOp op)663 ::dawn::StencilOperation GetDawnStencilOp(::amber::StencilOp op) {
664 switch (op) {
665 case StencilOp::kKeep:
666 return ::dawn::StencilOperation::Keep;
667 case StencilOp::kZero:
668 return ::dawn::StencilOperation::Zero;
669 case StencilOp::kReplace:
670 return ::dawn::StencilOperation::Replace;
671 case StencilOp::kIncrementAndClamp:
672 return ::dawn::StencilOperation::IncrementClamp;
673 case StencilOp::kDecrementAndClamp:
674 return ::dawn::StencilOperation::DecrementClamp;
675 case StencilOp::kInvert:
676 return ::dawn::StencilOperation::Invert;
677 case StencilOp::kIncrementAndWrap:
678 return ::dawn::StencilOperation::IncrementWrap;
679 case StencilOp::kDecrementAndWrap:
680 return ::dawn::StencilOperation::DecrementWrap;
681 default:
682 return ::dawn::StencilOperation::Keep;
683 }
684 }
685
GetDawnBlendFactor(::amber::BlendFactor factor)686 ::dawn::BlendFactor GetDawnBlendFactor(::amber::BlendFactor factor) {
687 switch (factor) {
688 case BlendFactor::kZero:
689 return ::dawn::BlendFactor::Zero;
690 case BlendFactor::kOne:
691 return ::dawn::BlendFactor::One;
692 case BlendFactor::kSrcColor:
693 return ::dawn::BlendFactor::SrcColor;
694 case BlendFactor::kOneMinusSrcColor:
695 return ::dawn::BlendFactor::OneMinusSrcColor;
696 case BlendFactor::kDstColor:
697 return ::dawn::BlendFactor::DstColor;
698 case BlendFactor::kOneMinusDstColor:
699 return ::dawn::BlendFactor::OneMinusDstColor;
700 case BlendFactor::kSrcAlpha:
701 return ::dawn::BlendFactor::SrcAlpha;
702 case BlendFactor::kOneMinusSrcAlpha:
703 return ::dawn::BlendFactor::OneMinusSrcAlpha;
704 case BlendFactor::kDstAlpha:
705 return ::dawn::BlendFactor::DstAlpha;
706 case BlendFactor::kOneMinusDstAlpha:
707 return ::dawn::BlendFactor::OneMinusDstAlpha;
708 case BlendFactor::kSrcAlphaSaturate:
709 return ::dawn::BlendFactor::SrcAlphaSaturated;
710 default:
711 assert(false && "Dawn::Unknown BlendFactor");
712 return ::dawn::BlendFactor::One;
713 }
714 }
715
GetDawnBlendOperation(BlendOp op)716 ::dawn::BlendOperation GetDawnBlendOperation(BlendOp op) {
717 switch (op) {
718 case BlendOp::kAdd:
719 return ::dawn::BlendOperation::Add;
720 case BlendOp::kSubtract:
721 return ::dawn::BlendOperation::Subtract;
722 case BlendOp::kReverseSubtract:
723 return ::dawn::BlendOperation::ReverseSubtract;
724 case BlendOp::kMin:
725 return ::dawn::BlendOperation::Min;
726 case BlendOp::kMax:
727 return ::dawn::BlendOperation::Max;
728 default:
729 assert(false && "Dawn::Unknown BlendOp");
730 return ::dawn::BlendOperation::Add;
731 }
732 }
733
GetDawnColorWriteMask(uint8_t amber_color_write_mask)734 ::dawn::ColorWriteMask GetDawnColorWriteMask(uint8_t amber_color_write_mask) {
735 if (amber_color_write_mask == 0x00000000)
736 return ::dawn::ColorWriteMask::None;
737 else if (amber_color_write_mask == 0x00000001)
738 return ::dawn::ColorWriteMask::Red;
739 else if (amber_color_write_mask == 0x00000002)
740 return ::dawn::ColorWriteMask::Green;
741 else if (amber_color_write_mask == 0x00000004)
742 return ::dawn::ColorWriteMask::Blue;
743 else if (amber_color_write_mask == 0x00000008)
744 return ::dawn::ColorWriteMask::Alpha;
745 else if (amber_color_write_mask == 0x0000000F)
746 return ::dawn::ColorWriteMask::All;
747 else
748 assert(false && "Dawn::Unknown ColorWriteMask");
749 return ::dawn::ColorWriteMask::All;
750 }
751
GetDawnFrontFace(FrontFace amber_front_face)752 ::dawn::FrontFace GetDawnFrontFace(FrontFace amber_front_face) {
753 return amber_front_face == FrontFace::kClockwise ? ::dawn::FrontFace::CW
754 : ::dawn::FrontFace::CCW;
755 }
756
GetDawnCullMode(CullMode amber_cull_mode)757 ::dawn::CullMode GetDawnCullMode(CullMode amber_cull_mode) {
758 switch (amber_cull_mode) {
759 case CullMode::kNone:
760 return ::dawn::CullMode::None;
761 case CullMode::kFront:
762 return ::dawn::CullMode::Front;
763 case CullMode::kBack:
764 return ::dawn::CullMode::Back;
765 default:
766 assert(false && "Dawn::Unknown CullMode");
767 return ::dawn::CullMode::None;
768 }
769 }
770
EngineDawn()771 EngineDawn::EngineDawn() : Engine() {}
772
773 EngineDawn::~EngineDawn() = default;
774
Initialize(EngineConfig * config,Delegate *,const std::vector<std::string> &,const std::vector<std::string> &,const std::vector<std::string> &)775 Result EngineDawn::Initialize(EngineConfig* config,
776 Delegate*,
777 const std::vector<std::string>&,
778 const std::vector<std::string>&,
779 const std::vector<std::string>&) {
780 if (device_)
781 return Result("Dawn:Initialize device_ already exists");
782
783 if (!config)
784 return Result("Dawn::Initialize config is null");
785 DawnEngineConfig* dawn_config = static_cast<DawnEngineConfig*>(config);
786 if (dawn_config->device == nullptr)
787 return Result("Dawn:Initialize device is a null pointer");
788
789 device_ = dawn_config->device;
790
791 return {};
792 }
793
CreatePipeline(::amber::Pipeline * pipeline)794 Result EngineDawn::CreatePipeline(::amber::Pipeline* pipeline) {
795 if (!device_) {
796 return Result("Dawn::CreatePipeline: device is not created");
797 }
798 std::unordered_map<ShaderType, ::dawn::ShaderModule, CastHash<ShaderType>>
799 module_for_type;
800 ::dawn::ShaderModuleDescriptor descriptor;
801 descriptor.nextInChain = nullptr;
802
803 for (const auto& shader_info : pipeline->GetShaders()) {
804 ShaderType type = shader_info.GetShaderType();
805 const std::vector<uint32_t>& code = shader_info.GetData();
806 descriptor.code = code.data();
807 descriptor.codeSize = uint32_t(code.size());
808
809 auto shader = device_->CreateShaderModule(&descriptor);
810 if (!shader) {
811 return Result("Dawn::CreatePipeline: failed to create shader");
812 }
813 if (module_for_type.count(type)) {
814 return Result("Dawn::CreatePipeline: module for type already exists");
815 }
816 module_for_type[type] = shader;
817 }
818
819 switch (pipeline->GetType()) {
820 case PipelineType::kCompute: {
821 auto& module = module_for_type[kShaderTypeCompute];
822 if (!module)
823 return Result("Dawn::CreatePipeline: no compute shader provided");
824
825 pipeline_map_[pipeline].compute_pipeline.reset(
826 new ComputePipelineInfo(pipeline, module));
827 Result result =
828 AttachBuffers(pipeline_map_[pipeline].compute_pipeline.get());
829 if (!result.IsSuccess())
830 return result;
831 break;
832 }
833
834 case PipelineType::kGraphics: {
835 // TODO(dneto): Handle other shader types as well. They are optional.
836 auto& vs = module_for_type[kShaderTypeVertex];
837 auto& fs = module_for_type[kShaderTypeFragment];
838 if (!vs) {
839 return Result(
840 "Dawn::CreatePipeline: no vertex shader provided for graphics "
841 "pipeline");
842 }
843 if (!fs) {
844 return Result(
845 "Dawn::CreatePipeline: no fragment shader provided for graphics "
846 "pipeline");
847 }
848
849 pipeline_map_[pipeline].render_pipeline.reset(
850 new RenderPipelineInfo(pipeline, vs, fs));
851 Result result = AttachBuffersAndTextures(
852 pipeline_map_[pipeline].render_pipeline.get());
853 if (!result.IsSuccess())
854 return result;
855
856 break;
857 }
858 }
859
860 return {};
861 }
862
DoClearColor(const ClearColorCommand * command)863 Result EngineDawn::DoClearColor(const ClearColorCommand* command) {
864 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
865 if (!render_pipeline)
866 return Result("ClearColor invoked on invalid or missing render pipeline");
867
868 render_pipeline->clear_color_value = ::dawn::Color{
869 command->GetR(), command->GetG(), command->GetB(), command->GetA()};
870
871 return {};
872 }
873
DoClearStencil(const ClearStencilCommand * command)874 Result EngineDawn::DoClearStencil(const ClearStencilCommand* command) {
875 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
876 if (!render_pipeline)
877 return Result("ClearStencil invoked on invalid or missing render pipeline");
878
879 render_pipeline->clear_stencil_value = command->GetValue();
880 return {};
881 }
882
DoClearDepth(const ClearDepthCommand * command)883 Result EngineDawn::DoClearDepth(const ClearDepthCommand* command) {
884 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
885 if (!render_pipeline)
886 return Result("ClearDepth invoked on invalid or missing render pipeline");
887
888 render_pipeline->clear_depth_value = command->GetValue();
889 return {};
890 }
891
DoClear(const ClearCommand * command)892 Result EngineDawn::DoClear(const ClearCommand* command) {
893 Result result;
894 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
895 if (!render_pipeline)
896 return Result("Clear invoked on invalid or missing render pipeline");
897
898 DawnPipelineHelper helper;
899 result = helper.CreateRenderPipelineDescriptor(*render_pipeline, *device_,
900 false, nullptr);
901 if (!result.IsSuccess())
902 return result;
903 result = helper.CreateRenderPassDescriptor(
904 *render_pipeline, *device_, texture_views_, ::dawn::LoadOp::Clear);
905 if (!result.IsSuccess())
906 return result;
907
908 ::dawn::RenderPassDescriptor* renderPassDescriptor =
909 &helper.renderPassDescriptor;
910 ::dawn::CommandEncoder encoder = device_->CreateCommandEncoder();
911 ::dawn::RenderPassEncoder pass =
912 encoder.BeginRenderPass(renderPassDescriptor);
913 pass.EndPass();
914
915 ::dawn::CommandBuffer commands = encoder.Finish();
916 ::dawn::Queue queue = device_->CreateQueue();
917 queue.Submit(1, &commands);
918
919 result = MapDeviceTextureToHostBuffer(*render_pipeline, *device_);
920
921 return result;
922 }
923
924 // Creates a Dawn render pipeline descriptor for the given pipeline on the given
925 // device. When |ignore_vertex_and_Index_buffers| is true, ignores the vertex
926 // and index buffers attached to |render_pipeline| and instead configures the
927 // resulting descriptor to have a single vertex buffer with an attribute format
928 // of Float4 and input stride of 4*sizeof(float)
CreateRenderPipelineDescriptor(const RenderPipelineInfo & render_pipeline,const::dawn::Device & device,const bool ignore_vertex_and_Index_buffers,const PipelineData * pipeline_data)929 Result DawnPipelineHelper::CreateRenderPipelineDescriptor(
930 const RenderPipelineInfo& render_pipeline,
931 const ::dawn::Device& device,
932 const bool ignore_vertex_and_Index_buffers,
933 const PipelineData* pipeline_data) {
934 Result result;
935
936 auto* amber_format =
937 render_pipeline.pipeline->GetColorAttachments()[0].buffer->GetFormat();
938 if (!amber_format)
939 return Result("Color attachment 0 has no format!");
940 ::dawn::TextureFormat fb_format{};
941 result = GetDawnTextureFormat(*amber_format, &fb_format);
942 if (!result.IsSuccess())
943 return result;
944
945 ::dawn::TextureFormat depth_stencil_format{};
946 auto* depthBuffer = render_pipeline.pipeline->GetDepthBuffer().buffer;
947 if (depthBuffer) {
948 auto* amber_depth_stencil_format = depthBuffer->GetFormat();
949 if (!amber_depth_stencil_format)
950 return Result("The depth/stencil attachment has no format!");
951 result = GetDawnTextureFormat(*amber_depth_stencil_format,
952 &depth_stencil_format);
953 if (!result.IsSuccess())
954 return result;
955 } else {
956 depth_stencil_format = ::dawn::TextureFormat::Depth24PlusStencil8;
957 }
958
959 renderPipelineDescriptor.layout =
960 MakeBasicPipelineLayout(device, render_pipeline.bind_group_layouts);
961
962 renderPipelineDescriptor.primitiveTopology =
963 ::dawn::PrimitiveTopology::TriangleList;
964 renderPipelineDescriptor.sampleCount = 1;
965
966 // Lookup shaders' entrypoints
967 for (const auto& shader_info : render_pipeline.pipeline->GetShaders()) {
968 if (shader_info.GetShaderType() == kShaderTypeVertex) {
969 vertexEntryPoint = shader_info.GetEntryPoint();
970 } else if (shader_info.GetShaderType() == kShaderTypeFragment) {
971 fragmentEntryPoint = shader_info.GetEntryPoint();
972 } else {
973 return Result(
974 "CreateRenderPipelineDescriptor: An unknown shader is attached to "
975 "the render pipeline");
976 }
977 }
978 // Fill the default values for vertexInput (buffers and attributes).
979 // assuming #buffers == #attributes
980 vertexInputDescriptor.bufferCount =
981 render_pipeline.pipeline->GetVertexBuffers().size();
982
983 for (uint32_t i = 0; i < kMaxVertexInputs; ++i) {
984 if (ignore_vertex_and_Index_buffers) {
985 if (i == 0) {
986 vertexInputDescriptor.bufferCount = 1;
987 vertexInputDescriptor.cBuffers[0].attributeCount = 1;
988 vertexInputDescriptor.cBuffers[0].stride = 4 * sizeof(float);
989 vertexInputDescriptor.cBuffers[0].attributes =
990 &vertexInputDescriptor.cAttributes[0];
991
992 vertexInputDescriptor.cAttributes[0].shaderLocation = 0;
993 vertexInputDescriptor.cAttributes[0].format =
994 ::dawn::VertexFormat::Float4;
995 }
996 } else {
997 if (i < render_pipeline.pipeline->GetVertexBuffers().size()) {
998 vertexInputDescriptor.cBuffers[i].attributeCount = 1;
999 vertexInputDescriptor.cBuffers[i].stride =
1000 render_pipeline.pipeline->GetVertexBuffers()[i]
1001 .buffer->GetElementStride();
1002 vertexInputDescriptor.cBuffers[i].stepMode =
1003 ::dawn::InputStepMode::Vertex;
1004 vertexInputDescriptor.cBuffers[i].attributes =
1005 &vertexInputDescriptor.cAttributes[i];
1006
1007 vertexInputDescriptor.cAttributes[i].shaderLocation = i;
1008 auto* amber_vertex_format =
1009 render_pipeline.pipeline->GetVertexBuffers()[i].buffer->GetFormat();
1010 result = GetDawnVertexFormat(
1011 *amber_vertex_format, &vertexInputDescriptor.cAttributes[i].format);
1012 if (!result.IsSuccess())
1013 return result;
1014 }
1015 }
1016 }
1017
1018 // set index buffer format
1019 if (render_pipeline.pipeline->GetIndexBuffer()) {
1020 auto* amber_index_format =
1021 render_pipeline.pipeline->GetIndexBuffer()->GetFormat();
1022 GetDawnIndexFormat(*amber_index_format, &vertexInputDescriptor.indexFormat);
1023 if (!result.IsSuccess())
1024 return result;
1025 }
1026
1027 renderPipelineDescriptor.vertexInput =
1028 reinterpret_cast<::dawn::VertexInputDescriptor*>(&vertexInputDescriptor);
1029
1030 // Set defaults for the vertex stage descriptor.
1031 vertexStage.module = render_pipeline.vertex_shader;
1032 vertexStage.entryPoint = vertexEntryPoint.c_str();
1033 renderPipelineDescriptor.vertexStage = vertexStage;
1034
1035 // Set defaults for the fragment stage descriptor.
1036 fragmentStage.module = render_pipeline.fragment_shader;
1037 fragmentStage.entryPoint = fragmentEntryPoint.c_str();
1038 renderPipelineDescriptor.fragmentStage = std::move(&fragmentStage);
1039
1040 // Set defaults for the rasterization state descriptor.
1041 if (pipeline_data == nullptr) {
1042 rasterizationState.frontFace = ::dawn::FrontFace::CCW;
1043 rasterizationState.cullMode = ::dawn::CullMode::None;
1044 rasterizationState.depthBias = 0;
1045 rasterizationState.depthBiasSlopeScale = 0.0;
1046 rasterizationState.depthBiasClamp = 0.0;
1047 renderPipelineDescriptor.rasterizationState = &rasterizationState;
1048 } else {
1049 rasterizationState.frontFace =
1050 GetDawnFrontFace(pipeline_data->GetFrontFace());
1051 rasterizationState.cullMode = GetDawnCullMode(pipeline_data->GetCullMode());
1052 rasterizationState.depthBias = pipeline_data->GetEnableDepthBias();
1053 rasterizationState.depthBiasSlopeScale =
1054 pipeline_data->GetDepthBiasSlopeFactor();
1055 rasterizationState.depthBiasClamp = pipeline_data->GetDepthBiasClamp();
1056 renderPipelineDescriptor.rasterizationState = &rasterizationState;
1057 }
1058
1059 // Set defaults for the color state descriptors.
1060 if (pipeline_data == nullptr) {
1061 renderPipelineDescriptor.colorStateCount =
1062 render_pipeline.pipeline->GetColorAttachments().size();
1063 alpha_blend.operation = ::dawn::BlendOperation::Add;
1064 alpha_blend.srcFactor = ::dawn::BlendFactor::One;
1065 alpha_blend.dstFactor = ::dawn::BlendFactor::Zero;
1066 colorStateDescriptor.writeMask = ::dawn::ColorWriteMask::All;
1067 colorStateDescriptor.format = fb_format;
1068 colorStateDescriptor.alphaBlend = alpha_blend;
1069 colorStateDescriptor.colorBlend = alpha_blend;
1070 } else {
1071 renderPipelineDescriptor.colorStateCount =
1072 render_pipeline.pipeline->GetColorAttachments().size();
1073
1074 alpha_blend.operation =
1075 GetDawnBlendOperation(pipeline_data->GetColorBlendOp());
1076 alpha_blend.srcFactor =
1077 GetDawnBlendFactor(pipeline_data->GetSrcAlphaBlendFactor());
1078 alpha_blend.dstFactor =
1079 GetDawnBlendFactor(pipeline_data->GetDstAlphaBlendFactor());
1080
1081 color_blend.operation =
1082 GetDawnBlendOperation(pipeline_data->GetAlphaBlendOp());
1083 color_blend.srcFactor =
1084 GetDawnBlendFactor(pipeline_data->GetSrcColorBlendFactor());
1085 color_blend.dstFactor =
1086 GetDawnBlendFactor(pipeline_data->GetDstAlphaBlendFactor());
1087
1088 colorStateDescriptor.writeMask =
1089 GetDawnColorWriteMask(pipeline_data->GetColorWriteMask());
1090
1091 colorStateDescriptor.format = fb_format;
1092 colorStateDescriptor.alphaBlend = alpha_blend;
1093 colorStateDescriptor.colorBlend = color_blend;
1094 }
1095
1096 for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
1097 ::dawn::TextureFormat fb_format{};
1098 {
1099 if (i < render_pipeline.pipeline->GetColorAttachments().size()) {
1100 auto* amber_format = render_pipeline.pipeline->GetColorAttachments()[i]
1101 .buffer->GetFormat();
1102 if (!amber_format)
1103 return Result(
1104 "AttachBuffersAndTextures: One Color attachment has no "
1105 "format!");
1106 result = GetDawnTextureFormat(*amber_format, &fb_format);
1107 if (!result.IsSuccess())
1108 return result;
1109 } else {
1110 fb_format = ::dawn::TextureFormat::RGBA8Unorm;
1111 }
1112 }
1113 colorStatesDescriptor[i] = colorStateDescriptor;
1114 colorStates[i] = &colorStatesDescriptor[i];
1115 colorStates[i]->format = fb_format;
1116 }
1117 renderPipelineDescriptor.colorStates = colorStates[0];
1118
1119 // Set defaults for the depth stencil state descriptors.
1120 if (pipeline_data == nullptr) {
1121 stencil_front.compare = ::dawn::CompareFunction::Always;
1122 stencil_front.failOp = ::dawn::StencilOperation::Keep;
1123 stencil_front.depthFailOp = ::dawn::StencilOperation::Keep;
1124 stencil_front.passOp = ::dawn::StencilOperation::Keep;
1125 depthStencilState.depthWriteEnabled = false;
1126 depthStencilState.depthCompare = ::dawn::CompareFunction::Always;
1127 depthStencilState.stencilBack = stencil_front;
1128 depthStencilState.stencilFront = stencil_front;
1129 depthStencilState.stencilReadMask = 0xff;
1130 depthStencilState.stencilWriteMask = 0xff;
1131 depthStencilState.format = depth_stencil_format;
1132 renderPipelineDescriptor.depthStencilState = &depthStencilState;
1133 } else {
1134 stencil_front.compare =
1135 GetDawnCompareOp(pipeline_data->GetFrontCompareOp());
1136 stencil_front.failOp = GetDawnStencilOp(pipeline_data->GetFrontFailOp());
1137 stencil_front.depthFailOp =
1138 GetDawnStencilOp(pipeline_data->GetFrontDepthFailOp());
1139 stencil_front.passOp = GetDawnStencilOp(pipeline_data->GetFrontPassOp());
1140
1141 stencil_back.compare = GetDawnCompareOp(pipeline_data->GetBackCompareOp());
1142 stencil_back.failOp = GetDawnStencilOp(pipeline_data->GetBackFailOp());
1143 stencil_back.depthFailOp =
1144 GetDawnStencilOp(pipeline_data->GetBackDepthFailOp());
1145 stencil_back.passOp = GetDawnStencilOp(pipeline_data->GetBackPassOp());
1146
1147 depthStencilState.depthWriteEnabled = pipeline_data->GetEnableDepthWrite();
1148 depthStencilState.depthCompare =
1149 GetDawnCompareOp(pipeline_data->GetDepthCompareOp());
1150 depthStencilState.stencilFront = stencil_front;
1151 depthStencilState.stencilBack = stencil_back;
1152 // WebGPU doesn't support separate front and back stencil mask, they has to
1153 // be the same
1154 depthStencilState.stencilReadMask =
1155 (pipeline_data->GetFrontCompareMask() ==
1156 pipeline_data->GetBackCompareMask())
1157 ? pipeline_data->GetFrontCompareMask()
1158 : 0xff;
1159 depthStencilState.stencilWriteMask = (pipeline_data->GetBackWriteMask() ==
1160 pipeline_data->GetFrontWriteMask())
1161 ? pipeline_data->GetBackWriteMask()
1162 : 0xff;
1163 depthStencilState.format = depth_stencil_format;
1164 renderPipelineDescriptor.depthStencilState = &depthStencilState;
1165 }
1166
1167 return {};
1168 }
1169
CreateRenderPassDescriptor(const RenderPipelineInfo & render_pipeline,const::dawn::Device & device,const std::vector<::dawn::TextureView> & texture_view,const::dawn::LoadOp load_op)1170 Result DawnPipelineHelper::CreateRenderPassDescriptor(
1171 const RenderPipelineInfo& render_pipeline,
1172 const ::dawn::Device& device,
1173 const std::vector<::dawn::TextureView>& texture_view,
1174 const ::dawn::LoadOp load_op) {
1175 for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
1176 colorAttachmentsInfo[i].loadOp = load_op;
1177 colorAttachmentsInfo[i].storeOp = ::dawn::StoreOp::Store;
1178 colorAttachmentsInfo[i].clearColor = render_pipeline.clear_color_value;
1179 }
1180
1181 depthStencilAttachmentInfo.clearDepth = render_pipeline.clear_depth_value;
1182 depthStencilAttachmentInfo.clearStencil = render_pipeline.clear_stencil_value;
1183 depthStencilAttachmentInfo.depthLoadOp = load_op;
1184 depthStencilAttachmentInfo.depthStoreOp = ::dawn::StoreOp::Store;
1185 depthStencilAttachmentInfo.stencilLoadOp = load_op;
1186 depthStencilAttachmentInfo.stencilStoreOp = ::dawn::StoreOp::Store;
1187
1188 renderPassDescriptor.colorAttachmentCount =
1189 render_pipeline.pipeline->GetColorAttachments().size();
1190 uint32_t colorAttachmentIndex = 0;
1191 for (const ::dawn::TextureView& colorAttachment : texture_view) {
1192 if (colorAttachment.Get() != nullptr) {
1193 colorAttachmentsInfo[colorAttachmentIndex].attachment = colorAttachment;
1194 colorAttachmentsInfoPtr[colorAttachmentIndex] =
1195 colorAttachmentsInfo[colorAttachmentIndex];
1196 }
1197 ++colorAttachmentIndex;
1198 }
1199 renderPassDescriptor.colorAttachments = colorAttachmentsInfoPtr;
1200
1201 ::dawn::TextureFormat depth_stencil_format{};
1202 auto* depthBuffer = render_pipeline.pipeline->GetDepthBuffer().buffer;
1203 if (depthBuffer) {
1204 auto* amber_depth_stencil_format = depthBuffer->GetFormat();
1205 if (!amber_depth_stencil_format)
1206 return Result("The depth/stencil attachment has no format!");
1207 Result result = GetDawnTextureFormat(*amber_depth_stencil_format,
1208 &depth_stencil_format);
1209 if (!result.IsSuccess())
1210 return result;
1211 } else {
1212 depth_stencil_format = ::dawn::TextureFormat::Depth24PlusStencil8;
1213 }
1214
1215 depthStencilDescriptor.dimension = ::dawn::TextureDimension::e2D;
1216 depthStencilDescriptor.size.width =
1217 render_pipeline.pipeline->GetFramebufferWidth();
1218 depthStencilDescriptor.size.height =
1219 render_pipeline.pipeline->GetFramebufferHeight();
1220 depthStencilDescriptor.size.depth = 1;
1221 depthStencilDescriptor.arrayLayerCount = 1;
1222 depthStencilDescriptor.sampleCount = 1;
1223 depthStencilDescriptor.format = depth_stencil_format;
1224 depthStencilDescriptor.mipLevelCount = 1;
1225 depthStencilDescriptor.usage =
1226 ::dawn::TextureUsage::OutputAttachment | ::dawn::TextureUsage::CopySrc;
1227 depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
1228 depthStencilView = depthStencilTexture.CreateView();
1229
1230 if (depthStencilView.Get() != nullptr) {
1231 depthStencilAttachmentInfo.attachment = depthStencilView;
1232 renderPassDescriptor.depthStencilAttachment = &depthStencilAttachmentInfo;
1233 } else {
1234 renderPassDescriptor.depthStencilAttachment = nullptr;
1235 }
1236
1237 return {};
1238 }
1239
DoDrawRect(const DrawRectCommand * command)1240 Result EngineDawn::DoDrawRect(const DrawRectCommand* command) {
1241 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
1242 if (!render_pipeline)
1243 return Result("DrawRect invoked on invalid or missing render pipeline");
1244 if (render_pipeline->vertex_buffers.size() > 1)
1245 return Result(
1246 "DrawRect invoked on a render pipeline with more than one "
1247 "VERTEX_DATA attached");
1248
1249 float x = command->GetX();
1250 float y = command->GetY();
1251 float rectangleWidth = command->GetWidth();
1252 float rectangleHeight = command->GetHeight();
1253
1254 const uint32_t frameWidth = render_pipeline->pipeline->GetFramebufferWidth();
1255 const uint32_t frameHeight =
1256 render_pipeline->pipeline->GetFramebufferHeight();
1257
1258 if (command->IsOrtho()) {
1259 x = ((x / frameWidth) * 2.0f) - 1.0f;
1260 y = ((y / frameHeight) * 2.0f) - 1.0f;
1261 rectangleWidth = (rectangleWidth / frameWidth) * 2.0f;
1262 rectangleHeight = (rectangleHeight / frameHeight) * 2.0f;
1263 }
1264
1265 static const uint32_t indexData[3 * 2] = {
1266 0, 1, 2, 0, 2, 3,
1267 };
1268 auto index_buffer = CreateBufferFromData(
1269 *device_, indexData, sizeof(indexData), ::dawn::BufferUsage::Index);
1270
1271 const float vertexData[4 * 4] = {
1272 // Bottom left
1273 x,
1274 y + rectangleHeight,
1275 0.0f,
1276 1.0f,
1277 // Top left
1278 x,
1279 y,
1280 0.0f,
1281 1.0f,
1282 // Top right
1283 x + rectangleWidth,
1284 y,
1285 0.0f,
1286 1.0f,
1287 // Bottom right
1288 x + rectangleWidth,
1289 y + rectangleHeight,
1290 0.0f,
1291 1.0f,
1292 };
1293
1294 auto vertex_buffer = CreateBufferFromData(
1295 *device_, vertexData, sizeof(vertexData), ::dawn::BufferUsage::Vertex);
1296 DawnPipelineHelper helper;
1297 helper.CreateRenderPipelineDescriptor(*render_pipeline, *device_, true,
1298 command->GetPipelineData());
1299 helper.CreateRenderPassDescriptor(*render_pipeline, *device_, texture_views_,
1300 ::dawn::LoadOp::Load);
1301 ::dawn::RenderPipelineDescriptor* renderPipelineDescriptor =
1302 &helper.renderPipelineDescriptor;
1303 ::dawn::RenderPassDescriptor* renderPassDescriptor =
1304 &helper.renderPassDescriptor;
1305
1306 const ::dawn::RenderPipeline pipeline =
1307 device_->CreateRenderPipeline(renderPipelineDescriptor);
1308 ::dawn::CommandEncoder encoder = device_->CreateCommandEncoder();
1309 ::dawn::RenderPassEncoder pass =
1310 encoder.BeginRenderPass(renderPassDescriptor);
1311 pass.SetPipeline(pipeline);
1312 for (uint32_t i = 0; i < render_pipeline->bind_groups.size(); i++) {
1313 if (render_pipeline->bind_groups[i]) {
1314 pass.SetBindGroup(i, render_pipeline->bind_groups[i], 0, nullptr);
1315 }
1316 }
1317 pass.SetVertexBuffer(0, vertex_buffer, 0);
1318 pass.SetIndexBuffer(index_buffer, 0);
1319 pass.DrawIndexed(6, 1, 0, 0, 0);
1320 pass.EndPass();
1321
1322 ::dawn::CommandBuffer commands = encoder.Finish();
1323 ::dawn::Queue queue = device_->CreateQueue();
1324 queue.Submit(1, &commands);
1325
1326 Result result = MapDeviceTextureToHostBuffer(*render_pipeline, *device_);
1327
1328 return result;
1329 }
1330
DoDrawGrid(const DrawGridCommand * command)1331 Result EngineDawn::DoDrawGrid(const DrawGridCommand* command) {
1332 return Result("DRAW_GRID not implemented on Dawn");
1333 }
1334
DoDrawArrays(const DrawArraysCommand * command)1335 Result EngineDawn::DoDrawArrays(const DrawArraysCommand* command) {
1336 Result result;
1337
1338 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
1339 if (!render_pipeline)
1340 return Result("DrawArrays invoked on invalid or missing render pipeline");
1341
1342 if (command->IsIndexed()) {
1343 if (!render_pipeline->index_buffer)
1344 return Result("DrawArrays: Draw indexed is used without given indices");
1345 } else {
1346 std::vector<uint32_t> indexData;
1347 for (uint32_t i = 0;
1348 i < command->GetFirstVertexIndex() + command->GetVertexCount(); i++) {
1349 indexData.emplace_back(i);
1350 }
1351 render_pipeline->index_buffer = CreateBufferFromData(
1352 *device_, indexData.data(), indexData.size() * sizeof(uint32_t),
1353 ::dawn::BufferUsage::Index);
1354 }
1355
1356 uint32_t instance_count = command->GetInstanceCount();
1357 if (instance_count == 0 && command->GetVertexCount() != 0)
1358 instance_count = 1;
1359
1360 DawnPipelineHelper helper;
1361 result = helper.CreateRenderPipelineDescriptor(
1362 *render_pipeline, *device_, false, command->GetPipelineData());
1363 if (!result.IsSuccess())
1364 return result;
1365 result = helper.CreateRenderPassDescriptor(
1366 *render_pipeline, *device_, texture_views_, ::dawn::LoadOp::Load);
1367 if (!result.IsSuccess())
1368 return result;
1369
1370 ::dawn::RenderPipelineDescriptor* renderPipelineDescriptor =
1371 &helper.renderPipelineDescriptor;
1372 ::dawn::RenderPassDescriptor* renderPassDescriptor =
1373 &helper.renderPassDescriptor;
1374
1375 result = GetDawnTopology(command->GetTopology(),
1376 &renderPipelineDescriptor->primitiveTopology);
1377 if (!result.IsSuccess())
1378 return result;
1379
1380 const ::dawn::RenderPipeline pipeline =
1381 device_->CreateRenderPipeline(renderPipelineDescriptor);
1382 ::dawn::CommandEncoder encoder = device_->CreateCommandEncoder();
1383 ::dawn::RenderPassEncoder pass =
1384 encoder.BeginRenderPass(renderPassDescriptor);
1385 pass.SetPipeline(pipeline);
1386 for (uint32_t i = 0; i < render_pipeline->bind_groups.size(); i++) {
1387 if (render_pipeline->bind_groups[i]) {
1388 pass.SetBindGroup(i, render_pipeline->bind_groups[i], 0, nullptr);
1389 }
1390 }
1391 // TODO(sarahM0): figure out what are startSlot, count and offsets
1392 for (uint32_t i = 0; i < render_pipeline->vertex_buffers.size(); i++) {
1393 pass.SetVertexBuffer(i, /* slot */
1394 render_pipeline->vertex_buffers[i], /* buffer */
1395 0); /* offsets */
1396 }
1397 // TODO(sarahM0): figure out what this offset means
1398 pass.SetIndexBuffer(render_pipeline->index_buffer, /* buffer */
1399 0); /* offset*/
1400 pass.DrawIndexed(command->GetVertexCount(), /* indexCount */
1401 instance_count, /* instanceCount */
1402 0, /* firstIndex */
1403 command->GetFirstVertexIndex(), /* baseVertex */
1404 0 /* firstInstance */);
1405
1406 pass.EndPass();
1407 ::dawn::CommandBuffer commands = encoder.Finish();
1408 ::dawn::Queue queue = device_->CreateQueue();
1409 queue.Submit(1, &commands);
1410
1411 result = MapDeviceTextureToHostBuffer(*render_pipeline, *device_);
1412
1413 return result;
1414 }
1415
DoCompute(const ComputeCommand * command)1416 Result EngineDawn::DoCompute(const ComputeCommand* command) {
1417 Result result;
1418
1419 ComputePipelineInfo* compute_pipeline = GetComputePipeline(command);
1420 if (!compute_pipeline)
1421 return Result("DoComput: invoked on invalid or missing compute pipeline");
1422
1423 ::dawn::ComputePipelineDescriptor computePipelineDescriptor;
1424 computePipelineDescriptor.layout = MakeBasicPipelineLayout(
1425 device_->Get(), compute_pipeline->bind_group_layouts);
1426
1427 ::dawn::ProgrammableStageDescriptor pipelineStageDescriptor;
1428 pipelineStageDescriptor.module = compute_pipeline->compute_shader;
1429 pipelineStageDescriptor.entryPoint = "main";
1430 computePipelineDescriptor.computeStage = pipelineStageDescriptor;
1431 ::dawn::ComputePipeline pipeline =
1432 device_->CreateComputePipeline(&computePipelineDescriptor);
1433 ::dawn::CommandEncoder encoder = device_->CreateCommandEncoder();
1434 ::dawn::ComputePassEncoder pass = encoder.BeginComputePass();
1435 pass.SetPipeline(pipeline);
1436 for (uint32_t i = 0; i < compute_pipeline->bind_groups.size(); i++) {
1437 if (compute_pipeline->bind_groups[i]) {
1438 pass.SetBindGroup(i, compute_pipeline->bind_groups[i], 0, nullptr);
1439 }
1440 }
1441 pass.Dispatch(command->GetX(), command->GetY(), command->GetZ());
1442 pass.EndPass();
1443 // Finish recording the command buffer. It only has one command.
1444 auto command_buffer = encoder.Finish();
1445 // Submit the command.
1446 auto queue = device_->CreateQueue();
1447 queue.Submit(1, &command_buffer);
1448 // Copy result back
1449 result = MapDeviceBufferToHostBuffer(*compute_pipeline, *device_);
1450
1451 return result;
1452 }
1453
DoEntryPoint(const EntryPointCommand *)1454 Result EngineDawn::DoEntryPoint(const EntryPointCommand*) {
1455 return Result("Dawn: Entry point must be \"main\" in Dawn");
1456 }
1457
DoPatchParameterVertices(const PatchParameterVerticesCommand *)1458 Result EngineDawn::DoPatchParameterVertices(
1459 const PatchParameterVerticesCommand*) {
1460 return Result("Dawn: PatchParameterVertices is not supported in Dawn");
1461 }
1462
DoBuffer(const BufferCommand * command)1463 Result EngineDawn::DoBuffer(const BufferCommand* command) {
1464 Result result;
1465
1466 ::dawn::Buffer* dawn_buffer = nullptr;
1467
1468 const auto descriptor_set = command->GetDescriptorSet();
1469 const auto binding = command->GetBinding();
1470
1471 RenderPipelineInfo* render_pipeline = GetRenderPipeline(command);
1472 if (render_pipeline) {
1473 auto where = render_pipeline->buffer_map.find({descriptor_set, binding});
1474 if (where != render_pipeline->buffer_map.end()) {
1475 const auto dawn_buffer_index = where->second;
1476 dawn_buffer = &render_pipeline->buffers[dawn_buffer_index];
1477 }
1478 }
1479
1480 ComputePipelineInfo* compute_pipeline = GetComputePipeline(command);
1481 if (compute_pipeline) {
1482 auto where = compute_pipeline->buffer_map.find({descriptor_set, binding});
1483 if (where != compute_pipeline->buffer_map.end()) {
1484 const auto dawn_buffer_index = where->second;
1485 dawn_buffer = &compute_pipeline->buffers[dawn_buffer_index];
1486 }
1487 }
1488
1489 if (!render_pipeline && !compute_pipeline)
1490 return Result("DoBuffer: invoked on invalid or missing pipeline");
1491 if (!command->IsSSBO() && !command->IsUniform())
1492 return Result("DoBuffer: only supports SSBO and uniform buffer type");
1493 if (!dawn_buffer) {
1494 return Result("DoBuffer: no Dawn buffer at descriptor set " +
1495 std::to_string(descriptor_set) + " and binding " +
1496 std::to_string(binding));
1497 }
1498
1499 Buffer* amber_buffer = command->GetBuffer();
1500 if (amber_buffer) {
1501 amber_buffer->SetDataWithOffset(command->GetValues(), command->GetOffset());
1502
1503 dawn_buffer->SetSubData(0, amber_buffer->GetMaxSizeInBytes(),
1504 amber_buffer->ValuePtr()->data());
1505 }
1506
1507 return {};
1508 }
1509
AttachBuffersAndTextures(RenderPipelineInfo * render_pipeline)1510 Result EngineDawn::AttachBuffersAndTextures(
1511 RenderPipelineInfo* render_pipeline) {
1512 Result result;
1513 const uint32_t width = render_pipeline->pipeline->GetFramebufferWidth();
1514 const uint32_t height = render_pipeline->pipeline->GetFramebufferHeight();
1515
1516 // Create textures and texture views if we haven't already
1517 std::vector<int32_t> seen_idx(
1518 render_pipeline->pipeline->GetColorAttachments().size(), -1);
1519 for (auto info : render_pipeline->pipeline->GetColorAttachments()) {
1520 if (info.location >=
1521 render_pipeline->pipeline->GetColorAttachments().size())
1522 return Result("color attachment locations must be sequential from 0");
1523 if (seen_idx[info.location] != -1) {
1524 return Result("duplicate attachment location: " +
1525 std::to_string(info.location));
1526 }
1527 seen_idx[info.location] = static_cast<int32_t>(info.location);
1528 }
1529
1530 if (textures_.size() == 0) {
1531 for (uint32_t i = 0; i < kMaxColorAttachments; i++) {
1532 ::dawn::TextureFormat fb_format{};
1533
1534 if (i < render_pipeline->pipeline->GetColorAttachments().size()) {
1535 auto* amber_format = render_pipeline->pipeline->GetColorAttachments()[i]
1536 .buffer->GetFormat();
1537 if (!amber_format)
1538 return Result(
1539 "AttachBuffersAndTextures: One Color attachment has no "
1540 "format!");
1541 result = GetDawnTextureFormat(*amber_format, &fb_format);
1542 if (!result.IsSuccess())
1543 return result;
1544 } else {
1545 fb_format = ::dawn::TextureFormat::RGBA8Unorm;
1546 }
1547
1548 textures_.emplace_back(
1549 MakeDawnTexture(*device_, fb_format, width, height));
1550 texture_views_.emplace_back(textures_.back().CreateView());
1551 }
1552 }
1553
1554 // Attach depth-stencil texture
1555 auto* depthBuffer = render_pipeline->pipeline->GetDepthBuffer().buffer;
1556 if (depthBuffer) {
1557 if (!depth_stencil_texture_) {
1558 auto* amber_depth_stencil_format = depthBuffer->GetFormat();
1559 if (!amber_depth_stencil_format)
1560 return Result(
1561 "AttachBuffersAndTextures: The depth/stencil attachment has no "
1562 "format!");
1563 ::dawn::TextureFormat depth_stencil_format{};
1564 result = GetDawnTextureFormat(*amber_depth_stencil_format,
1565 &depth_stencil_format);
1566 if (!result.IsSuccess())
1567 return result;
1568
1569 result = MakeTexture(*device_, depth_stencil_format, width, height,
1570 &depth_stencil_texture_);
1571 if (!result.IsSuccess())
1572 return result;
1573 render_pipeline->depth_stencil_texture = depth_stencil_texture_;
1574 } else {
1575 render_pipeline->depth_stencil_texture = depth_stencil_texture_;
1576 }
1577 }
1578
1579 // Attach index buffer
1580 if (render_pipeline->pipeline->GetIndexBuffer()) {
1581 render_pipeline->index_buffer = CreateBufferFromData(
1582 *device_,
1583 render_pipeline->pipeline->GetIndexBuffer()->ValuePtr()->data(),
1584 render_pipeline->pipeline->GetIndexBuffer()->GetSizeInBytes(),
1585 ::dawn::BufferUsage::Index);
1586 }
1587
1588 // Attach vertex buffers
1589 for (auto& vertex_info : render_pipeline->pipeline->GetVertexBuffers()) {
1590 render_pipeline->vertex_buffers.emplace_back(CreateBufferFromData(
1591 *device_, vertex_info.buffer->ValuePtr()->data(),
1592 vertex_info.buffer->GetSizeInBytes(), ::dawn::BufferUsage::Vertex));
1593 }
1594
1595 // Do not attach pushConstants
1596 if (render_pipeline->pipeline->GetPushConstantBuffer().buffer != nullptr) {
1597 return Result(
1598 "AttachBuffersAndTextures: Dawn does not support push constants!");
1599 }
1600
1601 ::dawn::ShaderStage kAllStages =
1602 ::dawn::ShaderStage::Vertex | ::dawn::ShaderStage::Fragment;
1603 std::vector<std::vector<BindingInitializationHelper>> bindingInitalizerHelper(
1604 kMaxDawnBindGroup);
1605 std::vector<std::vector<::dawn::BindGroupLayoutBinding>> layouts_info(
1606 kMaxDawnBindGroup);
1607 uint32_t max_descriptor_set = 0;
1608
1609 // Attach storage/uniform buffers
1610 ::dawn::BindGroupLayoutBinding empty_layout_info = {};
1611
1612 if (!render_pipeline->pipeline->GetBuffers().empty()) {
1613 std::vector<uint32_t> max_binding_seen(kMaxDawnBindGroup, -1);
1614 for (auto& buf_info : render_pipeline->pipeline->GetBuffers()) {
1615 while (layouts_info[buf_info.descriptor_set].size() <= buf_info.binding)
1616 layouts_info[buf_info.descriptor_set].push_back(empty_layout_info);
1617 }
1618 }
1619
1620 for (const auto& buf_info : render_pipeline->pipeline->GetBuffers()) {
1621 ::dawn::BufferUsage bufferUsage;
1622 ::dawn::BindingType bindingType;
1623 switch (buf_info.type) {
1624 case BufferType::kStorage: {
1625 bufferUsage = ::dawn::BufferUsage::Storage;
1626 bindingType = ::dawn::BindingType::StorageBuffer;
1627 break;
1628 }
1629 case BufferType::kUniform: {
1630 bufferUsage = ::dawn::BufferUsage::Uniform;
1631 bindingType = ::dawn::BindingType::UniformBuffer;
1632 break;
1633 }
1634 default: {
1635 return Result("AttachBuffersAndTextures: unknown buffer type: " +
1636 std::to_string(static_cast<uint32_t>(buf_info.type)));
1637 break;
1638 }
1639 }
1640
1641 if (buf_info.descriptor_set > kMaxDawnBindGroup - 1) {
1642 return Result("AttachBuffers: Dawn has a maximum of " +
1643 std::to_string(kMaxDawnBindGroup) + " (descriptor sets)");
1644 }
1645
1646 render_pipeline->buffers.emplace_back(
1647 CreateBufferFromData(*device_, buf_info.buffer->ValuePtr()->data(),
1648 buf_info.buffer->GetMaxSizeInBytes(),
1649 bufferUsage | ::dawn::BufferUsage::CopySrc |
1650 ::dawn::BufferUsage::CopyDst));
1651
1652 render_pipeline->buffer_map[{buf_info.descriptor_set, buf_info.binding}] =
1653 render_pipeline->buffers.size() - 1;
1654
1655 render_pipeline->used_descriptor_set.insert(buf_info.descriptor_set);
1656 max_descriptor_set = std::max(max_descriptor_set, buf_info.descriptor_set);
1657
1658 ::dawn::BindGroupLayoutBinding layout_info;
1659 layout_info.binding = buf_info.binding;
1660 layout_info.visibility = kAllStages;
1661 layout_info.type = bindingType;
1662 layouts_info[buf_info.descriptor_set][buf_info.binding] = layout_info;
1663
1664 BindingInitializationHelper tempBinding = BindingInitializationHelper(
1665 buf_info.binding, render_pipeline->buffers.back(), 0,
1666 buf_info.buffer->GetMaxSizeInBytes());
1667 bindingInitalizerHelper[buf_info.descriptor_set].push_back(tempBinding);
1668 }
1669
1670 for (uint32_t i = 0; i < kMaxDawnBindGroup; i++) {
1671 if (layouts_info[i].size() > 0 && bindingInitalizerHelper[i].size() > 0) {
1672 ::dawn::BindGroupLayout bindGroupLayout =
1673 MakeBindGroupLayout(*device_, layouts_info[i]);
1674 render_pipeline->bind_group_layouts.push_back(bindGroupLayout);
1675
1676 ::dawn::BindGroup bindGroup =
1677 MakeBindGroup(*device_, render_pipeline->bind_group_layouts[i],
1678 bindingInitalizerHelper[i]);
1679 render_pipeline->bind_groups.push_back(bindGroup);
1680 } else if (i < max_descriptor_set) {
1681 ::dawn::BindGroupLayout bindGroupLayout =
1682 MakeBindGroupLayout(*device_, {});
1683 render_pipeline->bind_group_layouts.push_back(bindGroupLayout);
1684
1685 ::dawn::BindGroup bindGroup =
1686 MakeBindGroup(*device_, render_pipeline->bind_group_layouts[i],
1687 bindingInitalizerHelper[i]);
1688 render_pipeline->bind_groups.push_back(bindGroup);
1689 }
1690 }
1691 return {};
1692 }
1693
AttachBuffers(ComputePipelineInfo * compute_pipeline)1694 Result EngineDawn::AttachBuffers(ComputePipelineInfo* compute_pipeline) {
1695 Result result;
1696
1697 // Do not attach pushConstants
1698 if (compute_pipeline->pipeline->GetPushConstantBuffer().buffer != nullptr) {
1699 return Result("AttachBuffers: Dawn does not support push constants!");
1700 }
1701
1702 std::vector<std::vector<BindingInitializationHelper>> bindingInitalizerHelper(
1703 kMaxDawnBindGroup);
1704 std::vector<std::vector<::dawn::BindGroupLayoutBinding>> layouts_info(
1705 kMaxDawnBindGroup);
1706 uint32_t max_descriptor_set = 0;
1707
1708 // Attach storage/uniform buffers
1709 ::dawn::BindGroupLayoutBinding empty_layout_info = {};
1710
1711 if (!compute_pipeline->pipeline->GetBuffers().empty()) {
1712 std::vector<uint32_t> max_binding_seen(kMaxDawnBindGroup, -1);
1713 for (auto& buf_info : compute_pipeline->pipeline->GetBuffers()) {
1714 while (layouts_info[buf_info.descriptor_set].size() <= buf_info.binding)
1715 layouts_info[buf_info.descriptor_set].push_back(empty_layout_info);
1716 }
1717 }
1718
1719 for (const auto& buf_info : compute_pipeline->pipeline->GetBuffers()) {
1720 ::dawn::BufferUsage bufferUsage;
1721 ::dawn::BindingType bindingType;
1722 switch (buf_info.type) {
1723 case BufferType::kStorage: {
1724 bufferUsage = ::dawn::BufferUsage::Storage;
1725 bindingType = ::dawn::BindingType::StorageBuffer;
1726 break;
1727 }
1728 case BufferType::kUniform: {
1729 bufferUsage = ::dawn::BufferUsage::Uniform;
1730 bindingType = ::dawn::BindingType::UniformBuffer;
1731 break;
1732 }
1733 default: {
1734 return Result("AttachBuffers: unknown buffer type: " +
1735 std::to_string(static_cast<uint32_t>(buf_info.type)));
1736 break;
1737 }
1738 }
1739
1740 if (buf_info.descriptor_set > kMaxDawnBindGroup - 1) {
1741 return Result("AttachBuffers: Dawn has a maximum of " +
1742 std::to_string(kMaxDawnBindGroup) + " (descriptor sets)");
1743 }
1744
1745 compute_pipeline->buffers.emplace_back(
1746 CreateBufferFromData(*device_, buf_info.buffer->ValuePtr()->data(),
1747 buf_info.buffer->GetMaxSizeInBytes(),
1748 bufferUsage | ::dawn::BufferUsage::CopySrc |
1749 ::dawn::BufferUsage::CopyDst));
1750
1751 compute_pipeline->buffer_map[{buf_info.descriptor_set, buf_info.binding}] =
1752 compute_pipeline->buffers.size() - 1;
1753
1754 compute_pipeline->used_descriptor_set.insert(buf_info.descriptor_set);
1755 max_descriptor_set = std::max(max_descriptor_set, buf_info.descriptor_set);
1756
1757 ::dawn::BindGroupLayoutBinding layout_info;
1758 layout_info.binding = buf_info.binding;
1759 layout_info.visibility = ::dawn::ShaderStage::Compute;
1760 layout_info.type = bindingType;
1761 layouts_info[buf_info.descriptor_set][buf_info.binding] = layout_info;
1762
1763 BindingInitializationHelper tempBinding = BindingInitializationHelper(
1764 buf_info.binding, compute_pipeline->buffers.back(), 0,
1765 buf_info.buffer->GetMaxSizeInBytes());
1766 bindingInitalizerHelper[buf_info.descriptor_set].push_back(tempBinding);
1767 }
1768
1769 for (uint32_t i = 0; i < kMaxDawnBindGroup; i++) {
1770 if (layouts_info[i].size() > 0 && bindingInitalizerHelper[i].size() > 0) {
1771 ::dawn::BindGroupLayout bindGroupLayout =
1772 MakeBindGroupLayout(*device_, layouts_info[i]);
1773 compute_pipeline->bind_group_layouts.push_back(bindGroupLayout);
1774
1775 ::dawn::BindGroup bindGroup =
1776 MakeBindGroup(*device_, compute_pipeline->bind_group_layouts[i],
1777 bindingInitalizerHelper[i]);
1778 compute_pipeline->bind_groups.push_back(bindGroup);
1779 } else if (i < max_descriptor_set) {
1780 ::dawn::BindGroupLayout bindGroupLayout =
1781 MakeBindGroupLayout(*device_, {});
1782 compute_pipeline->bind_group_layouts.push_back(bindGroupLayout);
1783
1784 ::dawn::BindGroup bindGroup =
1785 MakeBindGroup(*device_, compute_pipeline->bind_group_layouts[i],
1786 bindingInitalizerHelper[i]);
1787 compute_pipeline->bind_groups.push_back(bindGroup);
1788 }
1789 }
1790
1791 return {};
1792 }
1793
1794 } // namespace dawn
1795 } // namespace amber
1796