1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/webgpu/gpu_render_pipeline.h"
6 
7 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
8 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_blend_descriptor.h"
9 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_color_state_descriptor.h"
10 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_depth_stencil_state_descriptor.h"
11 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_rasterization_state_descriptor.h"
12 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pipeline_descriptor.h"
13 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_stencil_state_face_descriptor.h"
14 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_attribute_descriptor.h"
15 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_buffer_layout_descriptor.h"
16 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_vertex_state_descriptor.h"
17 #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
18 #include "third_party/blink/renderer/modules/webgpu/gpu_bind_group_layout.h"
19 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
20 #include "third_party/blink/renderer/modules/webgpu/gpu_pipeline_layout.h"
21 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
22 #include "third_party/blink/renderer/platform/bindings/script_state.h"
23 
24 namespace blink {
25 
26 namespace {
27 
AsDawnType(const GPUBlendDescriptor * webgpu_desc)28 WGPUBlendDescriptor AsDawnType(const GPUBlendDescriptor* webgpu_desc) {
29   DCHECK(webgpu_desc);
30 
31   WGPUBlendDescriptor dawn_desc = {};
32   dawn_desc.dstFactor = AsDawnEnum<WGPUBlendFactor>(webgpu_desc->dstFactor());
33   dawn_desc.srcFactor = AsDawnEnum<WGPUBlendFactor>(webgpu_desc->srcFactor());
34   dawn_desc.operation =
35       AsDawnEnum<WGPUBlendOperation>(webgpu_desc->operation());
36 
37   return dawn_desc;
38 }
39 
40 }  // anonymous namespace
41 
AsDawnType(const GPUColorStateDescriptor * webgpu_desc)42 WGPUColorStateDescriptor AsDawnType(
43     const GPUColorStateDescriptor* webgpu_desc) {
44   DCHECK(webgpu_desc);
45 
46   WGPUColorStateDescriptor dawn_desc = {};
47   dawn_desc.nextInChain = nullptr;
48   dawn_desc.alphaBlend = AsDawnType(webgpu_desc->alphaBlend());
49   dawn_desc.colorBlend = AsDawnType(webgpu_desc->colorBlend());
50   dawn_desc.writeMask =
51       AsDawnEnum<WGPUColorWriteMask>(webgpu_desc->writeMask());
52   dawn_desc.format = AsDawnEnum<WGPUTextureFormat>(webgpu_desc->format());
53 
54   return dawn_desc;
55 }
56 
57 namespace {
58 
AsDawnType(const GPUStencilStateFaceDescriptor * webgpu_desc)59 WGPUStencilStateFaceDescriptor AsDawnType(
60     const GPUStencilStateFaceDescriptor* webgpu_desc) {
61   DCHECK(webgpu_desc);
62 
63   WGPUStencilStateFaceDescriptor dawn_desc = {};
64   dawn_desc.compare = AsDawnEnum<WGPUCompareFunction>(webgpu_desc->compare());
65   dawn_desc.depthFailOp =
66       AsDawnEnum<WGPUStencilOperation>(webgpu_desc->depthFailOp());
67   dawn_desc.failOp = AsDawnEnum<WGPUStencilOperation>(webgpu_desc->failOp());
68   dawn_desc.passOp = AsDawnEnum<WGPUStencilOperation>(webgpu_desc->passOp());
69 
70   return dawn_desc;
71 }
72 
AsDawnType(const GPUDepthStencilStateDescriptor * webgpu_desc)73 WGPUDepthStencilStateDescriptor AsDawnType(
74     const GPUDepthStencilStateDescriptor* webgpu_desc) {
75   DCHECK(webgpu_desc);
76 
77   WGPUDepthStencilStateDescriptor dawn_desc = {};
78   dawn_desc.nextInChain = nullptr;
79   dawn_desc.depthCompare =
80       AsDawnEnum<WGPUCompareFunction>(webgpu_desc->depthCompare());
81   dawn_desc.depthWriteEnabled = webgpu_desc->depthWriteEnabled();
82   dawn_desc.format = AsDawnEnum<WGPUTextureFormat>(webgpu_desc->format());
83   dawn_desc.stencilBack = AsDawnType(webgpu_desc->stencilBack());
84   dawn_desc.stencilFront = AsDawnType(webgpu_desc->stencilFront());
85   dawn_desc.stencilReadMask = webgpu_desc->stencilReadMask();
86   dawn_desc.stencilWriteMask = webgpu_desc->stencilWriteMask();
87 
88   return dawn_desc;
89 }
90 
91 using WGPUVertexStateInfo = std::tuple<WGPUVertexStateDescriptor,
92                                        Vector<WGPUVertexBufferLayoutDescriptor>,
93                                        Vector<WGPUVertexAttributeDescriptor>>;
94 
GPUVertexStateAsWGPUVertexState(v8::Isolate * isolate,const GPUVertexStateDescriptor * descriptor,ExceptionState & exception_state)95 WGPUVertexStateInfo GPUVertexStateAsWGPUVertexState(
96     v8::Isolate* isolate,
97     const GPUVertexStateDescriptor* descriptor,
98     ExceptionState& exception_state) {
99   WGPUVertexStateDescriptor dawn_desc = {};
100   dawn_desc.indexFormat =
101       AsDawnEnum<WGPUIndexFormat>(descriptor->indexFormat());
102   dawn_desc.vertexBufferCount = 0;
103   dawn_desc.vertexBuffers = nullptr;
104 
105   Vector<WGPUVertexBufferLayoutDescriptor> dawn_vertex_buffers;
106   Vector<WGPUVertexAttributeDescriptor> dawn_vertex_attributes;
107 
108   if (descriptor->hasVertexBuffers()) {
109     // TODO(crbug.com/951629): Use a sequence of nullable descriptors.
110     v8::Local<v8::Value> vertex_buffers_value =
111         descriptor->vertexBuffers().V8Value();
112     if (!vertex_buffers_value->IsArray()) {
113       exception_state.ThrowTypeError("vertexBuffers must be an array");
114 
115       return std::make_tuple(dawn_desc, std::move(dawn_vertex_buffers),
116                              std::move(dawn_vertex_attributes));
117     }
118 
119     v8::Local<v8::Context> context = isolate->GetCurrentContext();
120     v8::Local<v8::Array> vertex_buffers = vertex_buffers_value.As<v8::Array>();
121 
122     // First we collect all the descriptors but we don't set
123     // WGPUVertexBufferLayoutDescriptor::attributes
124     // TODO(cwallez@chromium.org): Should we validate the Length() first so we
125     // don't risk creating HUGE vectors of WGPUVertexBufferLayoutDescriptor from
126     // the sparse array?
127     for (uint32_t i = 0; i < vertex_buffers->Length(); ++i) {
128       // This array can be sparse. Skip empty slots.
129       v8::MaybeLocal<v8::Value> maybe_value = vertex_buffers->Get(context, i);
130       v8::Local<v8::Value> value;
131       if (!maybe_value.ToLocal(&value) || value.IsEmpty() ||
132           value->IsNullOrUndefined()) {
133         WGPUVertexBufferLayoutDescriptor dawn_vertex_buffer = {};
134         dawn_vertex_buffer.arrayStride = 0;
135         dawn_vertex_buffer.stepMode = WGPUInputStepMode_Vertex;
136         dawn_vertex_buffer.attributeCount = 0;
137         dawn_vertex_buffer.attributes = nullptr;
138         dawn_vertex_buffers.push_back(dawn_vertex_buffer);
139         continue;
140       }
141 
142       GPUVertexBufferLayoutDescriptor* vertex_buffer =
143           NativeValueTraits<GPUVertexBufferLayoutDescriptor>::NativeValue(
144               isolate, value, exception_state);
145       if (exception_state.HadException()) {
146         return std::make_tuple(dawn_desc, std::move(dawn_vertex_buffers),
147                                std::move(dawn_vertex_attributes));
148       }
149 
150       WGPUVertexBufferLayoutDescriptor dawn_vertex_buffer = {};
151       dawn_vertex_buffer.arrayStride = vertex_buffer->arrayStride();
152       dawn_vertex_buffer.stepMode =
153           AsDawnEnum<WGPUInputStepMode>(vertex_buffer->stepMode());
154       dawn_vertex_buffer.attributeCount =
155           static_cast<uint32_t>(vertex_buffer->attributes().size());
156       dawn_vertex_buffer.attributes = nullptr;
157       dawn_vertex_buffers.push_back(dawn_vertex_buffer);
158 
159       for (wtf_size_t j = 0; j < vertex_buffer->attributes().size(); ++j) {
160         const GPUVertexAttributeDescriptor* attribute =
161             vertex_buffer->attributes()[j];
162         WGPUVertexAttributeDescriptor dawn_vertex_attribute = {};
163         dawn_vertex_attribute.shaderLocation = attribute->shaderLocation();
164         dawn_vertex_attribute.offset = attribute->offset();
165         dawn_vertex_attribute.format =
166             AsDawnEnum<WGPUVertexFormat>(attribute->format());
167         dawn_vertex_attributes.push_back(dawn_vertex_attribute);
168       }
169     }
170 
171     // Set up pointers in DawnVertexBufferLayoutDescriptor::attributes only
172     // after we stopped appending to the vector so the pointers aren't
173     // invalidated.
174     uint32_t attributeIndex = 0;
175     for (WGPUVertexBufferLayoutDescriptor& buffer : dawn_vertex_buffers) {
176       if (buffer.attributeCount == 0) {
177         continue;
178       }
179       buffer.attributes = &dawn_vertex_attributes[attributeIndex];
180       attributeIndex += buffer.attributeCount;
181     }
182   }
183 
184   dawn_desc.vertexBufferCount =
185       static_cast<uint32_t>(dawn_vertex_buffers.size());
186   dawn_desc.vertexBuffers = dawn_vertex_buffers.data();
187 
188   return std::make_tuple(dawn_desc, std::move(dawn_vertex_buffers),
189                          std::move(dawn_vertex_attributes));
190 }
191 
AsDawnType(const GPURasterizationStateDescriptor * webgpu_desc)192 WGPURasterizationStateDescriptor AsDawnType(
193     const GPURasterizationStateDescriptor* webgpu_desc) {
194   DCHECK(webgpu_desc);
195 
196   WGPURasterizationStateDescriptor dawn_desc = {};
197   dawn_desc.nextInChain = nullptr;
198   dawn_desc.frontFace = AsDawnEnum<WGPUFrontFace>(webgpu_desc->frontFace());
199   dawn_desc.cullMode = AsDawnEnum<WGPUCullMode>(webgpu_desc->cullMode());
200   dawn_desc.depthBias = webgpu_desc->depthBias();
201   dawn_desc.depthBiasSlopeScale = webgpu_desc->depthBiasSlopeScale();
202   dawn_desc.depthBiasClamp = webgpu_desc->depthBiasClamp();
203 
204   return dawn_desc;
205 }
206 
207 }  // anonymous namespace
208 
209 // static
Create(ScriptState * script_state,GPUDevice * device,const GPURenderPipelineDescriptor * webgpu_desc)210 GPURenderPipeline* GPURenderPipeline::Create(
211     ScriptState* script_state,
212     GPUDevice* device,
213     const GPURenderPipelineDescriptor* webgpu_desc) {
214   DCHECK(device);
215   DCHECK(webgpu_desc);
216 
217   WGPURenderPipelineDescriptor dawn_desc = {};
218   dawn_desc.nextInChain = nullptr;
219   if (webgpu_desc->hasLayout()) {
220     dawn_desc.layout = AsDawnType(webgpu_desc->layout());
221   }
222   if (webgpu_desc->hasLabel()) {
223     dawn_desc.label = webgpu_desc->label().Utf8().data();
224   }
225 
226   OwnedProgrammableStageDescriptor vertex_stage_info =
227       AsDawnType(webgpu_desc->vertexStage());
228   dawn_desc.vertexStage = std::get<0>(vertex_stage_info);
229   OwnedProgrammableStageDescriptor fragment_stage_info;
230   if (webgpu_desc->hasFragmentStage()) {
231     fragment_stage_info = AsDawnType(webgpu_desc->fragmentStage());
232     dawn_desc.fragmentStage = &std::get<0>(fragment_stage_info);
233   } else {
234     dawn_desc.fragmentStage = nullptr;
235   }
236 
237   v8::Isolate* isolate = script_state->GetIsolate();
238   ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
239                                  "GPUVertexStateDescriptor");
240   WGPUVertexStateInfo vertex_state_info = GPUVertexStateAsWGPUVertexState(
241       isolate, webgpu_desc->vertexState(), exception_state);
242   dawn_desc.vertexState = &std::get<0>(vertex_state_info);
243 
244   if (exception_state.HadException()) {
245     return nullptr;
246   }
247 
248   dawn_desc.primitiveTopology =
249       AsDawnEnum<WGPUPrimitiveTopology>(webgpu_desc->primitiveTopology());
250 
251   WGPURasterizationStateDescriptor rasterization_state;
252   rasterization_state = AsDawnType(webgpu_desc->rasterizationState());
253   dawn_desc.rasterizationState = &rasterization_state;
254 
255   dawn_desc.sampleCount = webgpu_desc->sampleCount();
256 
257   WGPUDepthStencilStateDescriptor depth_stencil_state = {};
258   if (webgpu_desc->hasDepthStencilState()) {
259     depth_stencil_state = AsDawnType(webgpu_desc->depthStencilState());
260     dawn_desc.depthStencilState = &depth_stencil_state;
261   } else {
262     dawn_desc.depthStencilState = nullptr;
263   }
264 
265   std::unique_ptr<WGPUColorStateDescriptor[]> color_states =
266       AsDawnType(webgpu_desc->colorStates());
267   dawn_desc.colorStateCount =
268       static_cast<uint32_t>(webgpu_desc->colorStates().size());
269 
270   dawn_desc.colorStates = color_states.get();
271 
272   dawn_desc.sampleMask = webgpu_desc->sampleMask();
273   dawn_desc.alphaToCoverageEnabled = webgpu_desc->alphaToCoverageEnabled();
274 
275   return MakeGarbageCollected<GPURenderPipeline>(
276       device, device->GetProcs().deviceCreateRenderPipeline(device->GetHandle(),
277                                                             &dawn_desc));
278 }
279 
GPURenderPipeline(GPUDevice * device,WGPURenderPipeline render_pipeline)280 GPURenderPipeline::GPURenderPipeline(GPUDevice* device,
281                                      WGPURenderPipeline render_pipeline)
282     : DawnObject<WGPURenderPipeline>(device, render_pipeline) {}
283 
~GPURenderPipeline()284 GPURenderPipeline::~GPURenderPipeline() {
285   if (IsDawnControlClientDestroyed()) {
286     return;
287   }
288   GetProcs().renderPipelineRelease(GetHandle());
289 }
290 
getBindGroupLayout(uint32_t index)291 GPUBindGroupLayout* GPURenderPipeline::getBindGroupLayout(uint32_t index) {
292   return MakeGarbageCollected<GPUBindGroupLayout>(
293       device_, GetProcs().renderPipelineGetBindGroupLayout(GetHandle(), index));
294 }
295 
296 }  // namespace blink
297