tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RenderPassEncoder.cpp (16274B)


      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 "RenderPassEncoder.h"
      7 
      8 #include "BindGroup.h"
      9 #include "CommandEncoder.h"
     10 #include "ExternalTexture.h"
     11 #include "RenderBundle.h"
     12 #include "RenderPipeline.h"
     13 #include "TextureView.h"
     14 #include "Utility.h"
     15 #include "mozilla/dom/WebGPUBinding.h"
     16 #include "mozilla/webgpu/ffi/wgpu.h"
     17 
     18 namespace mozilla::webgpu {
     19 
     20 GPU_IMPL_CYCLE_COLLECTION(RenderPassEncoder, mParent, mUsedBindGroups,
     21                          mUsedBuffers, mUsedPipelines, mUsedTextureViews,
     22                          mUsedRenderBundles)
     23 GPU_IMPL_JS_WRAP(RenderPassEncoder)
     24 
     25 void ffiWGPURenderPassDeleter::operator()(ffi::WGPURecordedRenderPass* raw) {
     26  if (raw) {
     27    ffi::wgpu_render_pass_destroy(raw);
     28  }
     29 }
     30 
     31 static ffi::WGPUStoreOp ConvertStoreOp(const dom::GPUStoreOp& aOp) {
     32  switch (aOp) {
     33    case dom::GPUStoreOp::Store:
     34      return ffi::WGPUStoreOp_Store;
     35    case dom::GPUStoreOp::Discard:
     36      return ffi::WGPUStoreOp_Discard;
     37  }
     38  MOZ_CRASH("bad GPUStoreOp");
     39 }
     40 
     41 static ffi::WGPUColor ConvertColor(const dom::Sequence<double>& aSeq) {
     42  ffi::WGPUColor color{
     43      .r = aSeq.SafeElementAt(0, 0.0),
     44      .g = aSeq.SafeElementAt(1, 0.0),
     45      .b = aSeq.SafeElementAt(2, 0.0),
     46      .a = aSeq.SafeElementAt(3, 1.0),
     47  };
     48  return color;
     49 }
     50 
     51 static ffi::WGPUColor ConvertColor(const dom::GPUColorDict& aColor) {
     52  ffi::WGPUColor color = {aColor.mR, aColor.mG, aColor.mB, aColor.mA};
     53  return color;
     54 }
     55 
     56 static ffi::WGPUColor ConvertColor(
     57    const dom::DoubleSequenceOrGPUColorDict& aColor) {
     58  if (aColor.IsDoubleSequence()) {
     59    return ConvertColor(aColor.GetAsDoubleSequence());
     60  }
     61  if (aColor.IsGPUColorDict()) {
     62    return ConvertColor(aColor.GetAsGPUColorDict());
     63  }
     64  MOZ_ASSERT_UNREACHABLE(
     65      "Unexpected dom::DoubleSequenceOrGPUColorDict variant");
     66  return ffi::WGPUColor();
     67 }
     68 static ffi::WGPUColor ConvertColor(
     69    const dom::OwningDoubleSequenceOrGPUColorDict& aColor) {
     70  if (aColor.IsDoubleSequence()) {
     71    return ConvertColor(aColor.GetAsDoubleSequence());
     72  }
     73  if (aColor.IsGPUColorDict()) {
     74    return ConvertColor(aColor.GetAsGPUColorDict());
     75  }
     76  MOZ_ASSERT_UNREACHABLE(
     77      "Unexpected dom::OwningDoubleSequenceOrGPUColorDict variant");
     78  return ffi::WGPUColor();
     79 }
     80 
     81 ffi::WGPURecordedRenderPass* BeginRenderPass(
     82    CommandEncoder* const aParent, const dom::GPURenderPassDescriptor& aDesc) {
     83  ffi::WGPURenderPassDescriptor desc = {};
     84 
     85  webgpu::StringHelper label(aDesc.mLabel);
     86  desc.label = label.Get();
     87 
     88  ffi::WGPURenderPassDepthStencilAttachment dsDesc = {};
     89  if (aDesc.mDepthStencilAttachment.WasPassed()) {
     90    const auto& dsa = aDesc.mDepthStencilAttachment.Value();
     91    // NOTE: We're assuming callers reified this to be a view.
     92    dsDesc.view = dsa.mView.GetAsGPUTextureView()->GetId();
     93 
     94    // -
     95 
     96    if (dsa.mDepthLoadOp.WasPassed()) {
     97      dsDesc.depth.load_op.tag =
     98          ffi::WGPUFfiOption_LoadOp_FfiOption_f32_Some_LoadOp_FfiOption_f32;
     99      switch (dsa.mDepthLoadOp.Value()) {
    100        case dom::GPULoadOp::Load:
    101          dsDesc.depth.load_op.some.tag =
    102              ffi::WGPULoadOp_FfiOption_f32_Load_FfiOption_f32;
    103          break;
    104        case dom::GPULoadOp::Clear:
    105          dsDesc.depth.load_op.some.clear_tag =
    106              ffi::WGPULoadOp_FfiOption_f32_Clear_FfiOption_f32;
    107          if (dsa.mDepthClearValue.WasPassed()) {
    108            dsDesc.depth.load_op.some.clear.tag =
    109                ffi::WGPUFfiOption_f32_Some_f32;
    110            dsDesc.depth.load_op.some.clear.some = dsa.mDepthClearValue.Value();
    111          } else {
    112            dsDesc.depth.load_op.some.clear.tag =
    113                ffi::WGPUFfiOption_f32_None_f32;
    114          }
    115          break;
    116      }
    117    } else {
    118      dsDesc.depth.load_op.tag =
    119          ffi::WGPUFfiOption_LoadOp_FfiOption_f32_None_LoadOp_FfiOption_f32;
    120    }
    121 
    122    if (dsa.mDepthStoreOp.WasPassed()) {
    123      dsDesc.depth.store_op.tag = ffi::WGPUFfiOption_StoreOp_Some_StoreOp;
    124      dsDesc.depth.store_op.some = ConvertStoreOp(dsa.mDepthStoreOp.Value());
    125    } else {
    126      dsDesc.depth.store_op.tag = ffi::WGPUFfiOption_StoreOp_None_StoreOp;
    127    }
    128 
    129    dsDesc.depth.read_only = dsa.mDepthReadOnly;
    130 
    131    // -
    132 
    133    if (dsa.mStencilLoadOp.WasPassed()) {
    134      dsDesc.stencil.load_op.tag =
    135          ffi::WGPUFfiOption_LoadOp_FfiOption_u32_Some_LoadOp_FfiOption_u32;
    136      switch (dsa.mStencilLoadOp.Value()) {
    137        case dom::GPULoadOp::Load:
    138          dsDesc.stencil.load_op.some.tag =
    139              ffi::WGPULoadOp_FfiOption_u32_Load_FfiOption_u32;
    140          break;
    141        case dom::GPULoadOp::Clear:
    142          dsDesc.stencil.load_op.some.clear_tag =
    143              ffi::WGPULoadOp_FfiOption_u32_Clear_FfiOption_u32;
    144          dsDesc.stencil.load_op.some.clear.tag =
    145              ffi::WGPUFfiOption_u32_Some_u32;
    146          dsDesc.stencil.load_op.some.clear.some = dsa.mStencilClearValue;
    147          break;
    148      }
    149    } else {
    150      dsDesc.stencil.load_op.tag =
    151          ffi::WGPUFfiOption_LoadOp_FfiOption_u32_None_LoadOp_FfiOption_u32;
    152    }
    153 
    154    if (dsa.mStencilStoreOp.WasPassed()) {
    155      dsDesc.stencil.store_op.tag = ffi::WGPUFfiOption_StoreOp_Some_StoreOp;
    156      dsDesc.stencil.store_op.some =
    157          ConvertStoreOp(dsa.mStencilStoreOp.Value());
    158    } else {
    159      dsDesc.stencil.store_op.tag = ffi::WGPUFfiOption_StoreOp_None_StoreOp;
    160    }
    161 
    162    dsDesc.stencil.read_only = dsa.mStencilReadOnly;
    163 
    164    // -
    165 
    166    desc.depth_stencil_attachment = &dsDesc;
    167  }
    168 
    169  AutoTArray<ffi::WGPUFfiRenderPassColorAttachment, WGPUMAX_COLOR_ATTACHMENTS>
    170      colorDescs;
    171 
    172  for (const auto& ca : aDesc.mColorAttachments) {
    173    ffi::WGPUFfiRenderPassColorAttachment cd = {};
    174    // NOTE: We're assuming callers reified this to be a view.
    175    cd.view = ca.mView.GetAsGPUTextureView()->GetId();
    176    cd.store_op = ConvertStoreOp(ca.mStoreOp);
    177 
    178    if (ca.mDepthSlice.WasPassed()) {
    179      cd.depth_slice.tag = ffi::WGPUFfiOption_u32_Some_u32;
    180      cd.depth_slice.some = ca.mDepthSlice.Value();
    181    } else {
    182      cd.depth_slice.tag = ffi::WGPUFfiOption_u32_None_u32;
    183    }
    184    if (ca.mResolveTarget.WasPassed()) {
    185      // NOTE: We're assuming callers reified this to be a view.
    186      cd.resolve_target =
    187          ca.mResolveTarget.Value().GetAsGPUTextureView()->GetId();
    188    }
    189 
    190    switch (ca.mLoadOp) {
    191      case dom::GPULoadOp::Load:
    192        cd.load_op.tag = ffi::WGPULoadOp_Color_Load_Color;
    193        break;
    194      case dom::GPULoadOp::Clear:
    195        cd.load_op.clear_tag = ffi::WGPULoadOp_Color_Clear_Color;
    196        if (ca.mClearValue.WasPassed()) {
    197          cd.load_op.clear = ConvertColor(ca.mClearValue.Value());
    198        } else {
    199          cd.load_op.clear = ffi::WGPUColor{0};
    200        }
    201        break;
    202    }
    203    colorDescs.AppendElement(cd);
    204  }
    205 
    206  desc.color_attachments = {colorDescs.Elements(), colorDescs.Length()};
    207 
    208  if (aDesc.mOcclusionQuerySet.WasPassed()) {
    209    desc.occlusion_query_set = aDesc.mOcclusionQuerySet.Value().GetId();
    210  }
    211 
    212  ffi::WGPUPassTimestampWrites passTimestampWrites = {};
    213  if (aDesc.mTimestampWrites.WasPassed()) {
    214    AssignPassTimestampWrites(aDesc.mTimestampWrites.Value(),
    215                              passTimestampWrites);
    216    desc.timestamp_writes = &passTimestampWrites;
    217  }
    218 
    219  return ffi::wgpu_command_encoder_begin_render_pass(&desc);
    220 }
    221 
    222 RenderPassEncoder::RenderPassEncoder(CommandEncoder* const aParent, RawId aId,
    223                                     const dom::GPURenderPassDescriptor& aDesc)
    224    : ObjectBase(aParent->GetChild(), aId,
    225                 ffi::wgpu_client_drop_render_pass_encoder),
    226      ChildOf(aParent),
    227      mPass(BeginRenderPass(aParent, aDesc)) {
    228  mValid = !!mPass;
    229  if (!mValid) {
    230    return;
    231  }
    232 
    233  // NOTE: We depend on callers ensuring that texture-or-view fields are reified
    234  // to views.
    235 
    236  for (const auto& at : aDesc.mColorAttachments) {
    237    mUsedTextureViews.AppendElement(at.mView.GetAsGPUTextureView());
    238  }
    239  if (aDesc.mDepthStencilAttachment.WasPassed()) {
    240    mUsedTextureViews.AppendElement(
    241        aDesc.mDepthStencilAttachment.Value().mView.GetAsGPUTextureView());
    242  }
    243 }
    244 
    245 RenderPassEncoder::~RenderPassEncoder() = default;
    246 
    247 void RenderPassEncoder::SetBindGroup(uint32_t aSlot,
    248                                     BindGroup* const aBindGroup,
    249                                     const uint32_t* aDynamicOffsets,
    250                                     size_t aDynamicOffsetsLength) {
    251  RawId bindGroup = 0;
    252  if (aBindGroup) {
    253    mUsedBindGroups.AppendElement(aBindGroup);
    254    mUsedCanvasContexts.AppendElements(aBindGroup->GetCanvasContexts());
    255    bindGroup = aBindGroup->GetId();
    256  }
    257  ffi::wgpu_recorded_render_pass_set_bind_group(
    258      mPass.get(), aSlot, bindGroup, {aDynamicOffsets, aDynamicOffsetsLength});
    259 }
    260 
    261 void RenderPassEncoder::SetBindGroup(
    262    uint32_t aSlot, BindGroup* const aBindGroup,
    263    const dom::Sequence<uint32_t>& aDynamicOffsets, ErrorResult& aRv) {
    264  if (!mValid) {
    265    return;
    266  }
    267  this->SetBindGroup(aSlot, aBindGroup, aDynamicOffsets.Elements(),
    268                     aDynamicOffsets.Length());
    269 }
    270 
    271 void RenderPassEncoder::SetBindGroup(
    272    uint32_t aSlot, BindGroup* const aBindGroup,
    273    const dom::Uint32Array& aDynamicOffsetsData,
    274    uint64_t aDynamicOffsetsDataStart, uint64_t aDynamicOffsetsDataLength,
    275    ErrorResult& aRv) {
    276  if (!mValid) {
    277    return;
    278  }
    279 
    280  auto dynamicOffsets =
    281      GetDynamicOffsetsFromArray(aDynamicOffsetsData, aDynamicOffsetsDataStart,
    282                                 aDynamicOffsetsDataLength, aRv);
    283 
    284  if (dynamicOffsets.isSome()) {
    285    this->SetBindGroup(aSlot, aBindGroup, dynamicOffsets->Elements(),
    286                       dynamicOffsets->Length());
    287  }
    288 }
    289 
    290 void RenderPassEncoder::SetPipeline(const RenderPipeline& aPipeline) {
    291  if (!mValid) {
    292    return;
    293  }
    294  mUsedPipelines.AppendElement(&aPipeline);
    295  ffi::wgpu_recorded_render_pass_set_pipeline(mPass.get(), aPipeline.GetId());
    296 }
    297 
    298 void RenderPassEncoder::SetIndexBuffer(const Buffer& aBuffer,
    299                                       const dom::GPUIndexFormat& aIndexFormat,
    300                                       uint64_t aOffset,
    301                                       const dom::Optional<uint64_t>& aSize) {
    302  if (!mValid) {
    303    return;
    304  }
    305  mUsedBuffers.AppendElement(&aBuffer);
    306  const auto iformat = aIndexFormat == dom::GPUIndexFormat::Uint32
    307                           ? ffi::WGPUIndexFormat_Uint32
    308                           : ffi::WGPUIndexFormat_Uint16;
    309  const uint64_t* sizeRef = aSize.WasPassed() ? &aSize.Value() : nullptr;
    310  ffi::wgpu_recorded_render_pass_set_index_buffer(mPass.get(), aBuffer.GetId(),
    311                                                  iformat, aOffset, sizeRef);
    312 }
    313 
    314 void RenderPassEncoder::SetVertexBuffer(uint32_t aSlot, const Buffer& aBuffer,
    315                                        uint64_t aOffset,
    316                                        const dom::Optional<uint64_t>& aSize) {
    317  if (!mValid) {
    318    return;
    319  }
    320  mUsedBuffers.AppendElement(&aBuffer);
    321 
    322  const uint64_t* sizeRef = aSize.WasPassed() ? &aSize.Value() : nullptr;
    323  ffi::wgpu_recorded_render_pass_set_vertex_buffer(
    324      mPass.get(), aSlot, aBuffer.GetId(), aOffset, sizeRef);
    325 }
    326 
    327 void RenderPassEncoder::Draw(uint32_t aVertexCount, uint32_t aInstanceCount,
    328                             uint32_t aFirstVertex, uint32_t aFirstInstance) {
    329  if (!mValid) {
    330    return;
    331  }
    332  ffi::wgpu_recorded_render_pass_draw(mPass.get(), aVertexCount, aInstanceCount,
    333                                      aFirstVertex, aFirstInstance);
    334 }
    335 
    336 void RenderPassEncoder::DrawIndexed(uint32_t aIndexCount,
    337                                    uint32_t aInstanceCount,
    338                                    uint32_t aFirstIndex, int32_t aBaseVertex,
    339                                    uint32_t aFirstInstance) {
    340  if (!mValid) {
    341    return;
    342  }
    343  ffi::wgpu_recorded_render_pass_draw_indexed(mPass.get(), aIndexCount,
    344                                              aInstanceCount, aFirstIndex,
    345                                              aBaseVertex, aFirstInstance);
    346 }
    347 
    348 void RenderPassEncoder::DrawIndirect(const Buffer& aIndirectBuffer,
    349                                     uint64_t aIndirectOffset) {
    350  if (!mValid) {
    351    return;
    352  }
    353  mUsedBuffers.AppendElement(&aIndirectBuffer);
    354  ffi::wgpu_recorded_render_pass_draw_indirect(
    355      mPass.get(), aIndirectBuffer.GetId(), aIndirectOffset);
    356 }
    357 
    358 void RenderPassEncoder::DrawIndexedIndirect(const Buffer& aIndirectBuffer,
    359                                            uint64_t aIndirectOffset) {
    360  if (!mValid) {
    361    return;
    362  }
    363  mUsedBuffers.AppendElement(&aIndirectBuffer);
    364  ffi::wgpu_recorded_render_pass_draw_indexed_indirect(
    365      mPass.get(), aIndirectBuffer.GetId(), aIndirectOffset);
    366 }
    367 
    368 void RenderPassEncoder::SetViewport(float x, float y, float width, float height,
    369                                    float minDepth, float maxDepth) {
    370  if (!mValid) {
    371    return;
    372  }
    373  ffi::wgpu_recorded_render_pass_set_viewport(mPass.get(), x, y, width, height,
    374                                              minDepth, maxDepth);
    375 }
    376 
    377 void RenderPassEncoder::SetScissorRect(uint32_t x, uint32_t y, uint32_t width,
    378                                       uint32_t height) {
    379  if (!mValid) {
    380    return;
    381  }
    382  ffi::wgpu_recorded_render_pass_set_scissor_rect(mPass.get(), x, y, width,
    383                                                  height);
    384 }
    385 
    386 void RenderPassEncoder::SetBlendConstant(
    387    const dom::DoubleSequenceOrGPUColorDict& color) {
    388  if (!mValid) {
    389    return;
    390  }
    391  ffi::WGPUColor aColor = ConvertColor(color);
    392  ffi::wgpu_recorded_render_pass_set_blend_constant(mPass.get(), &aColor);
    393 }
    394 
    395 void RenderPassEncoder::SetStencilReference(uint32_t reference) {
    396  if (!mValid) {
    397    return;
    398  }
    399  ffi::wgpu_recorded_render_pass_set_stencil_reference(mPass.get(), reference);
    400 }
    401 
    402 void RenderPassEncoder::BeginOcclusionQuery(uint32_t aQueryIndex) {
    403  if (!mValid) {
    404    return;
    405  }
    406  ffi::wgpu_recorded_render_pass_begin_occlusion_query(mPass.get(),
    407                                                       aQueryIndex);
    408 }
    409 
    410 void RenderPassEncoder::EndOcclusionQuery() {
    411  if (!mValid) {
    412    return;
    413  }
    414  ffi::wgpu_recorded_render_pass_end_occlusion_query(mPass.get());
    415 }
    416 
    417 void RenderPassEncoder::ExecuteBundles(
    418    const dom::Sequence<OwningNonNull<RenderBundle>>& aBundles) {
    419  if (!mValid) {
    420    return;
    421  }
    422  nsTArray<ffi::WGPURenderBundleId> renderBundles(aBundles.Length());
    423  for (const auto& bundle : aBundles) {
    424    mUsedRenderBundles.AppendElement(bundle);
    425    mUsedCanvasContexts.AppendElements(bundle->GetCanvasContexts());
    426    renderBundles.AppendElement(bundle->GetId());
    427  }
    428  ffi::wgpu_recorded_render_pass_execute_bundles(
    429      mPass.get(), {renderBundles.Elements(), renderBundles.Length()});
    430 }
    431 
    432 void RenderPassEncoder::PushDebugGroup(const nsAString& aString) {
    433  if (!mValid) {
    434    return;
    435  }
    436  const NS_ConvertUTF16toUTF8 utf8(aString);
    437  ffi::wgpu_recorded_render_pass_push_debug_group(mPass.get(), utf8.get(), 0);
    438 }
    439 void RenderPassEncoder::PopDebugGroup() {
    440  if (!mValid) {
    441    return;
    442  }
    443  ffi::wgpu_recorded_render_pass_pop_debug_group(mPass.get());
    444 }
    445 void RenderPassEncoder::InsertDebugMarker(const nsAString& aString) {
    446  if (!mValid) {
    447    return;
    448  }
    449  const NS_ConvertUTF16toUTF8 utf8(aString);
    450  ffi::wgpu_recorded_render_pass_insert_debug_marker(mPass.get(), utf8.get(),
    451                                                     0);
    452 }
    453 
    454 void RenderPassEncoder::End() {
    455  if (mParent->GetState() != CommandEncoderState::Locked) {
    456    const auto* message = "Encoding must not have ended";
    457    ffi::wgpu_report_validation_error(GetClient(),
    458                                      mParent->GetDevice()->GetId(), message);
    459  }
    460  if (!mValid) {
    461    return;
    462  }
    463  nsTArray<RefPtr<ExternalTexture>> externalTextures;
    464  for (const auto& bindGroup : mUsedBindGroups) {
    465    externalTextures.AppendElements(bindGroup->GetExternalTextures());
    466  }
    467  MOZ_ASSERT(!!mPass);
    468  mParent->EndRenderPass(*mPass, mUsedCanvasContexts, externalTextures);
    469 
    470  mValid = false;
    471  mPass.release();
    472  mUsedBindGroups.Clear();
    473  mUsedBuffers.Clear();
    474  mUsedPipelines.Clear();
    475  mUsedTextureViews.Clear();
    476  mUsedRenderBundles.Clear();
    477 }
    478 
    479 }  // namespace mozilla::webgpu