1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/dom/WebGPUBinding.h"
7 #include "RenderPassEncoder.h"
8 #include "BindGroup.h"
9 #include "CommandEncoder.h"
10 #include "RenderBundle.h"
11 #include "RenderPipeline.h"
12 #include "mozilla/webgpu/ffi/wgpu.h"
13 
14 namespace mozilla {
15 namespace webgpu {
16 
GPU_IMPL_CYCLE_COLLECTION(RenderPassEncoder,mParent,mUsedBindGroups,mUsedBuffers,mUsedPipelines,mUsedTextureViews,mUsedRenderBundles)17 GPU_IMPL_CYCLE_COLLECTION(RenderPassEncoder, mParent, mUsedBindGroups,
18                           mUsedBuffers, mUsedPipelines, mUsedTextureViews,
19                           mUsedRenderBundles)
20 GPU_IMPL_JS_WRAP(RenderPassEncoder)
21 
22 ffi::WGPURenderPass* ScopedFfiRenderTraits::empty() { return nullptr; }
23 
release(ffi::WGPURenderPass * raw)24 void ScopedFfiRenderTraits::release(ffi::WGPURenderPass* raw) {
25   if (raw) {
26     ffi::wgpu_render_pass_destroy(raw);
27   }
28 }
29 
ConvertLoadOp(const dom::GPULoadOp & aOp)30 ffi::WGPULoadOp ConvertLoadOp(const dom::GPULoadOp& aOp) {
31   switch (aOp) {
32     case dom::GPULoadOp::Load:
33       return ffi::WGPULoadOp_Load;
34     default:
35       MOZ_CRASH("Unexpected load op");
36   }
37 }
38 
ConvertStoreOp(const dom::GPUStoreOp & aOp)39 ffi::WGPUStoreOp ConvertStoreOp(const dom::GPUStoreOp& aOp) {
40   switch (aOp) {
41     case dom::GPUStoreOp::Store:
42       return ffi::WGPUStoreOp_Store;
43     case dom::GPUStoreOp::Discard:
44       return ffi::WGPUStoreOp_Discard;
45     default:
46       MOZ_CRASH("Unexpected load op");
47   }
48 }
49 
ConvertColor(const dom::GPUColorDict & aColor)50 ffi::WGPUColor ConvertColor(const dom::GPUColorDict& aColor) {
51   ffi::WGPUColor color = {aColor.mR, aColor.mG, aColor.mB, aColor.mA};
52   return color;
53 }
54 
BeginRenderPass(RawId aEncoderId,const dom::GPURenderPassDescriptor & aDesc)55 ffi::WGPURenderPass* BeginRenderPass(
56     RawId aEncoderId, const dom::GPURenderPassDescriptor& aDesc) {
57   ffi::WGPURenderPassDescriptor desc = {};
58 
59   ffi::WGPURenderPassDepthStencilAttachment dsDesc = {};
60   if (aDesc.mDepthStencilAttachment.WasPassed()) {
61     const auto& dsa = aDesc.mDepthStencilAttachment.Value();
62     dsDesc.view = dsa.mView->mId;
63 
64     if (dsa.mDepthLoadValue.IsFloat()) {
65       dsDesc.depth.load_op = ffi::WGPULoadOp_Clear;
66       dsDesc.depth.clear_value = dsa.mDepthLoadValue.GetAsFloat();
67     }
68     if (dsa.mDepthLoadValue.IsGPULoadOp()) {
69       dsDesc.depth.load_op =
70           ConvertLoadOp(dsa.mDepthLoadValue.GetAsGPULoadOp());
71     }
72     dsDesc.depth.store_op = ConvertStoreOp(dsa.mDepthStoreOp);
73 
74     if (dsa.mStencilLoadValue.IsRangeEnforcedUnsignedLong()) {
75       dsDesc.stencil.load_op = ffi::WGPULoadOp_Clear;
76       dsDesc.stencil.clear_value =
77           dsa.mStencilLoadValue.GetAsRangeEnforcedUnsignedLong();
78     }
79     if (dsa.mStencilLoadValue.IsGPULoadOp()) {
80       dsDesc.stencil.load_op =
81           ConvertLoadOp(dsa.mStencilLoadValue.GetAsGPULoadOp());
82     }
83     dsDesc.stencil.store_op = ConvertStoreOp(dsa.mStencilStoreOp);
84 
85     desc.depth_stencil_attachment = &dsDesc;
86   }
87 
88   std::array<ffi::WGPURenderPassColorAttachment, WGPUMAX_COLOR_TARGETS>
89       colorDescs = {};
90   desc.color_attachments = colorDescs.data();
91   desc.color_attachments_length = aDesc.mColorAttachments.Length();
92 
93   for (size_t i = 0; i < aDesc.mColorAttachments.Length(); ++i) {
94     const auto& ca = aDesc.mColorAttachments[i];
95     ffi::WGPURenderPassColorAttachment& cd = colorDescs[i];
96     cd.view = ca.mView->mId;
97     cd.channel.store_op = ConvertStoreOp(ca.mStoreOp);
98 
99     if (ca.mResolveTarget.WasPassed()) {
100       cd.resolve_target = ca.mResolveTarget.Value().mId;
101     }
102     if (ca.mLoadValue.IsGPULoadOp()) {
103       cd.channel.load_op = ConvertLoadOp(ca.mLoadValue.GetAsGPULoadOp());
104     } else {
105       cd.channel.load_op = ffi::WGPULoadOp_Clear;
106       if (ca.mLoadValue.IsDoubleSequence()) {
107         const auto& seq = ca.mLoadValue.GetAsDoubleSequence();
108         if (seq.Length() >= 1) {
109           cd.channel.clear_value.r = seq[0];
110         }
111         if (seq.Length() >= 2) {
112           cd.channel.clear_value.g = seq[1];
113         }
114         if (seq.Length() >= 3) {
115           cd.channel.clear_value.b = seq[2];
116         }
117         if (seq.Length() >= 4) {
118           cd.channel.clear_value.a = seq[3];
119         }
120       }
121       if (ca.mLoadValue.IsGPUColorDict()) {
122         cd.channel.clear_value =
123             ConvertColor(ca.mLoadValue.GetAsGPUColorDict());
124       }
125     }
126   }
127 
128   return ffi::wgpu_command_encoder_begin_render_pass(aEncoderId, &desc);
129 }
130 
RenderPassEncoder(CommandEncoder * const aParent,const dom::GPURenderPassDescriptor & aDesc)131 RenderPassEncoder::RenderPassEncoder(CommandEncoder* const aParent,
132                                      const dom::GPURenderPassDescriptor& aDesc)
133     : ChildOf(aParent), mPass(BeginRenderPass(aParent->mId, aDesc)) {
134   for (const auto& at : aDesc.mColorAttachments) {
135     mUsedTextureViews.AppendElement(at.mView);
136   }
137   if (aDesc.mDepthStencilAttachment.WasPassed()) {
138     mUsedTextureViews.AppendElement(
139         aDesc.mDepthStencilAttachment.Value().mView);
140   }
141 }
142 
~RenderPassEncoder()143 RenderPassEncoder::~RenderPassEncoder() {
144   if (mValid) {
145     mValid = false;
146   }
147 }
148 
SetBindGroup(uint32_t aSlot,const BindGroup & aBindGroup,const dom::Sequence<uint32_t> & aDynamicOffsets)149 void RenderPassEncoder::SetBindGroup(
150     uint32_t aSlot, const BindGroup& aBindGroup,
151     const dom::Sequence<uint32_t>& aDynamicOffsets) {
152   if (mValid) {
153     mUsedBindGroups.AppendElement(&aBindGroup);
154     ffi::wgpu_render_pass_set_bind_group(mPass, aSlot, aBindGroup.mId,
155                                          aDynamicOffsets.Elements(),
156                                          aDynamicOffsets.Length());
157   }
158 }
159 
SetPipeline(const RenderPipeline & aPipeline)160 void RenderPassEncoder::SetPipeline(const RenderPipeline& aPipeline) {
161   if (mValid) {
162     mUsedPipelines.AppendElement(&aPipeline);
163     ffi::wgpu_render_pass_set_pipeline(mPass, aPipeline.mId);
164   }
165 }
166 
SetIndexBuffer(const Buffer & aBuffer,const dom::GPUIndexFormat & aIndexFormat,uint64_t aOffset,uint64_t aSize)167 void RenderPassEncoder::SetIndexBuffer(const Buffer& aBuffer,
168                                        const dom::GPUIndexFormat& aIndexFormat,
169                                        uint64_t aOffset, uint64_t aSize) {
170   if (mValid) {
171     mUsedBuffers.AppendElement(&aBuffer);
172     const auto iformat = aIndexFormat == dom::GPUIndexFormat::Uint32
173                              ? ffi::WGPUIndexFormat_Uint32
174                              : ffi::WGPUIndexFormat_Uint16;
175     ffi::wgpu_render_pass_set_index_buffer(mPass, aBuffer.mId, iformat, aOffset,
176                                            aSize);
177   }
178 }
179 
SetVertexBuffer(uint32_t aSlot,const Buffer & aBuffer,uint64_t aOffset,uint64_t aSize)180 void RenderPassEncoder::SetVertexBuffer(uint32_t aSlot, const Buffer& aBuffer,
181                                         uint64_t aOffset, uint64_t aSize) {
182   if (mValid) {
183     mUsedBuffers.AppendElement(&aBuffer);
184     ffi::wgpu_render_pass_set_vertex_buffer(mPass, aSlot, aBuffer.mId, aOffset,
185                                             aSize);
186   }
187 }
188 
Draw(uint32_t aVertexCount,uint32_t aInstanceCount,uint32_t aFirstVertex,uint32_t aFirstInstance)189 void RenderPassEncoder::Draw(uint32_t aVertexCount, uint32_t aInstanceCount,
190                              uint32_t aFirstVertex, uint32_t aFirstInstance) {
191   if (mValid) {
192     ffi::wgpu_render_pass_draw(mPass, aVertexCount, aInstanceCount,
193                                aFirstVertex, aFirstInstance);
194   }
195 }
196 
DrawIndexed(uint32_t aIndexCount,uint32_t aInstanceCount,uint32_t aFirstIndex,int32_t aBaseVertex,uint32_t aFirstInstance)197 void RenderPassEncoder::DrawIndexed(uint32_t aIndexCount,
198                                     uint32_t aInstanceCount,
199                                     uint32_t aFirstIndex, int32_t aBaseVertex,
200                                     uint32_t aFirstInstance) {
201   if (mValid) {
202     ffi::wgpu_render_pass_draw_indexed(mPass, aIndexCount, aInstanceCount,
203                                        aFirstIndex, aBaseVertex,
204                                        aFirstInstance);
205   }
206 }
207 
DrawIndirect(const Buffer & aIndirectBuffer,uint64_t aIndirectOffset)208 void RenderPassEncoder::DrawIndirect(const Buffer& aIndirectBuffer,
209                                      uint64_t aIndirectOffset) {
210   if (mValid) {
211     ffi::wgpu_render_pass_draw_indirect(mPass, aIndirectBuffer.mId,
212                                         aIndirectOffset);
213   }
214 }
215 
DrawIndexedIndirect(const Buffer & aIndirectBuffer,uint64_t aIndirectOffset)216 void RenderPassEncoder::DrawIndexedIndirect(const Buffer& aIndirectBuffer,
217                                             uint64_t aIndirectOffset) {
218   if (mValid) {
219     ffi::wgpu_render_pass_draw_indexed_indirect(mPass, aIndirectBuffer.mId,
220                                                 aIndirectOffset);
221   }
222 }
223 
SetViewport(float x,float y,float width,float height,float minDepth,float maxDepth)224 void RenderPassEncoder::SetViewport(float x, float y, float width, float height,
225                                     float minDepth, float maxDepth) {
226   if (mValid) {
227     ffi::wgpu_render_pass_set_viewport(mPass, x, y, width, height, minDepth,
228                                        maxDepth);
229   }
230 }
231 
SetScissorRect(uint32_t x,uint32_t y,uint32_t width,uint32_t height)232 void RenderPassEncoder::SetScissorRect(uint32_t x, uint32_t y, uint32_t width,
233                                        uint32_t height) {
234   if (mValid) {
235     ffi::wgpu_render_pass_set_scissor_rect(mPass, x, y, width, height);
236   }
237 }
238 
SetBlendConstant(const dom::DoubleSequenceOrGPUColorDict & color)239 void RenderPassEncoder::SetBlendConstant(
240     const dom::DoubleSequenceOrGPUColorDict& color) {
241   if (mValid) {
242     ffi::WGPUColor aColor = ConvertColor(color.GetAsGPUColorDict());
243     ffi::wgpu_render_pass_set_blend_constant(mPass, &aColor);
244   }
245 }
246 
SetStencilReference(uint32_t reference)247 void RenderPassEncoder::SetStencilReference(uint32_t reference) {
248   if (mValid) {
249     ffi::wgpu_render_pass_set_stencil_reference(mPass, reference);
250   }
251 }
252 
ExecuteBundles(const dom::Sequence<OwningNonNull<RenderBundle>> & aBundles)253 void RenderPassEncoder::ExecuteBundles(
254     const dom::Sequence<OwningNonNull<RenderBundle>>& aBundles) {
255   if (mValid) {
256     nsTArray<ffi::WGPURenderBundleId> renderBundles(aBundles.Length());
257     for (const auto& bundle : aBundles) {
258       mUsedRenderBundles.AppendElement(bundle);
259       renderBundles.AppendElement(bundle->mId);
260     }
261     ffi::wgpu_render_pass_execute_bundles(mPass, renderBundles.Elements(),
262                                           renderBundles.Length());
263   }
264 }
265 
PushDebugGroup(const nsAString & aString)266 void RenderPassEncoder::PushDebugGroup(const nsAString& aString) {
267   if (mValid) {
268     const NS_ConvertUTF16toUTF8 utf8(aString);
269     ffi::wgpu_render_pass_push_debug_group(mPass, utf8.get(), 0);
270   }
271 }
PopDebugGroup()272 void RenderPassEncoder::PopDebugGroup() {
273   if (mValid) {
274     ffi::wgpu_render_pass_pop_debug_group(mPass);
275   }
276 }
InsertDebugMarker(const nsAString & aString)277 void RenderPassEncoder::InsertDebugMarker(const nsAString& aString) {
278   if (mValid) {
279     const NS_ConvertUTF16toUTF8 utf8(aString);
280     ffi::wgpu_render_pass_insert_debug_marker(mPass, utf8.get(), 0);
281   }
282 }
283 
EndPass(ErrorResult & aRv)284 void RenderPassEncoder::EndPass(ErrorResult& aRv) {
285   if (mValid) {
286     mValid = false;
287     auto* pass = mPass.forget();
288     MOZ_ASSERT(pass);
289     mParent->EndRenderPass(*pass, aRv);
290   }
291 }
292 
293 }  // namespace webgpu
294 }  // namespace mozilla
295