tor-browser

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

CommandEncoder.cpp (12592B)


      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 "CommandEncoder.h"
      7 
      8 #include "Buffer.h"
      9 #include "CommandBuffer.h"
     10 #include "ComputePassEncoder.h"
     11 #include "Device.h"
     12 #include "ExternalTexture.h"
     13 #include "RenderPassEncoder.h"
     14 #include "TextureView.h"
     15 #include "Utility.h"
     16 #include "ipc/WebGPUChild.h"
     17 #include "mozilla/dom/UnionTypes.h"
     18 #include "mozilla/dom/WebGPUBinding.h"
     19 #include "mozilla/webgpu/CanvasContext.h"
     20 #include "mozilla/webgpu/ffi/wgpu.h"
     21 
     22 namespace mozilla::webgpu {
     23 
     24 GPU_IMPL_CYCLE_COLLECTION(CommandEncoder, mParent, mExternalTextures)
     25 GPU_IMPL_JS_WRAP(CommandEncoder)
     26 
     27 void CommandEncoder::ConvertTextureDataLayoutToFFI(
     28    const dom::GPUTexelCopyBufferLayout& aLayout,
     29    ffi::WGPUTexelCopyBufferLayout* aLayoutFFI) {
     30  *aLayoutFFI = {};
     31  aLayoutFFI->offset = aLayout.mOffset;
     32 
     33  if (aLayout.mBytesPerRow.WasPassed()) {
     34    aLayoutFFI->bytes_per_row = &aLayout.mBytesPerRow.Value();
     35  } else {
     36    aLayoutFFI->bytes_per_row = nullptr;
     37  }
     38 
     39  if (aLayout.mRowsPerImage.WasPassed()) {
     40    aLayoutFFI->rows_per_image = &aLayout.mRowsPerImage.Value();
     41  } else {
     42    aLayoutFFI->rows_per_image = nullptr;
     43  }
     44 }
     45 
     46 void CommandEncoder::ConvertTextureCopyViewToFFI(
     47    const dom::GPUTexelCopyTextureInfo& aCopy,
     48    ffi::WGPUTexelCopyTextureInfo* aViewFFI) {
     49  *aViewFFI = {};
     50  aViewFFI->texture = aCopy.mTexture->GetId();
     51  aViewFFI->mip_level = aCopy.mMipLevel;
     52  const auto& origin = aCopy.mOrigin;
     53  if (origin.IsRangeEnforcedUnsignedLongSequence()) {
     54    const auto& seq = origin.GetAsRangeEnforcedUnsignedLongSequence();
     55    aViewFFI->origin.x = seq.Length() > 0 ? seq[0] : 0;
     56    aViewFFI->origin.y = seq.Length() > 1 ? seq[1] : 0;
     57    aViewFFI->origin.z = seq.Length() > 2 ? seq[2] : 0;
     58  } else if (origin.IsGPUOrigin3DDict()) {
     59    const auto& dict = origin.GetAsGPUOrigin3DDict();
     60    aViewFFI->origin.x = dict.mX;
     61    aViewFFI->origin.y = dict.mY;
     62    aViewFFI->origin.z = dict.mZ;
     63  } else {
     64    MOZ_CRASH("Unexpected origin type");
     65  }
     66  aViewFFI->aspect = ConvertTextureAspect(aCopy.mAspect);
     67 }
     68 
     69 static ffi::WGPUTexelCopyTextureInfo ConvertTextureCopyView(
     70    const dom::GPUTexelCopyTextureInfo& aCopy) {
     71  ffi::WGPUTexelCopyTextureInfo view = {};
     72  CommandEncoder::ConvertTextureCopyViewToFFI(aCopy, &view);
     73  return view;
     74 }
     75 
     76 CommandEncoder::CommandEncoder(Device* const aParent, RawId aId)
     77    : ObjectBase(aParent->GetChild(), aId,
     78                 ffi::wgpu_client_drop_command_encoder),
     79      ChildOf(aParent),
     80      mState(CommandEncoderState::Open) {}
     81 
     82 CommandEncoder::~CommandEncoder() = default;
     83 
     84 void CommandEncoder::TrackPresentationContext(
     85    WeakPtr<CanvasContext> aTargetContext) {
     86  if (aTargetContext) {
     87    mPresentationContexts.AppendElement(aTargetContext);
     88  }
     89 }
     90 
     91 void CommandEncoder::CopyBufferToBuffer(
     92    const Buffer& aSource, BufferAddress aSourceOffset,
     93    const Buffer& aDestination, BufferAddress aDestinationOffset,
     94    const dom::Optional<BufferAddress>& aSize) {
     95  // In Javascript, `size === undefined` means "copy from source offset to end
     96  // of buffer". wgpu_command_encoder_copy_buffer_to_buffer uses a value of
     97  // UINT64_MAX to encode this. If the requested copy size was UINT64_MAX, fudge
     98  // it to a different value that will still be rejected for misalignment on the
     99  // device timeline.
    100  BufferAddress size;
    101  if (aSize.WasPassed()) {
    102    if (aSize.Value() == std::numeric_limits<uint64_t>::max()) {
    103      size = std::numeric_limits<uint64_t>::max() - 4;
    104    } else {
    105      size = aSize.Value();
    106    }
    107  } else {
    108    size = std::numeric_limits<uint64_t>::max();
    109  }
    110 
    111  ffi::wgpu_command_encoder_copy_buffer_to_buffer(
    112      GetClient(), mParent->GetId(), GetId(), aSource.GetId(), aSourceOffset,
    113      aDestination.GetId(), aDestinationOffset, size);
    114 }
    115 
    116 void CommandEncoder::CopyBufferToTexture(
    117    const dom::GPUTexelCopyBufferInfo& aSource,
    118    const dom::GPUTexelCopyTextureInfo& aDestination,
    119    const dom::GPUExtent3D& aCopySize) {
    120  ffi::WGPUTexelCopyBufferLayout src_layout = {};
    121  CommandEncoder::ConvertTextureDataLayoutToFFI(aSource, &src_layout);
    122  ffi::wgpu_command_encoder_copy_buffer_to_texture(
    123      GetClient(), mParent->GetId(), GetId(), aSource.mBuffer->GetId(),
    124      &src_layout, ConvertTextureCopyView(aDestination),
    125      ConvertExtent(aCopySize));
    126 
    127  TrackPresentationContext(aDestination.mTexture->mTargetContext);
    128 }
    129 void CommandEncoder::CopyTextureToBuffer(
    130    const dom::GPUTexelCopyTextureInfo& aSource,
    131    const dom::GPUTexelCopyBufferInfo& aDestination,
    132    const dom::GPUExtent3D& aCopySize) {
    133  ffi::WGPUTexelCopyBufferLayout dstLayout = {};
    134  CommandEncoder::ConvertTextureDataLayoutToFFI(aDestination, &dstLayout);
    135  ffi::wgpu_command_encoder_copy_texture_to_buffer(
    136      GetClient(), mParent->GetId(), GetId(), ConvertTextureCopyView(aSource),
    137      aDestination.mBuffer->GetId(), &dstLayout, ConvertExtent(aCopySize));
    138 }
    139 void CommandEncoder::CopyTextureToTexture(
    140    const dom::GPUTexelCopyTextureInfo& aSource,
    141    const dom::GPUTexelCopyTextureInfo& aDestination,
    142    const dom::GPUExtent3D& aCopySize) {
    143  ffi::wgpu_command_encoder_copy_texture_to_texture(
    144      GetClient(), mParent->GetId(), GetId(), ConvertTextureCopyView(aSource),
    145      ConvertTextureCopyView(aDestination), ConvertExtent(aCopySize));
    146 
    147  TrackPresentationContext(aDestination.mTexture->mTargetContext);
    148 }
    149 
    150 void CommandEncoder::ClearBuffer(const Buffer& aBuffer, const uint64_t aOffset,
    151                                 const dom::Optional<uint64_t>& aSize) {
    152  uint64_t sizeVal = 0xdeaddead;
    153  uint64_t* size = nullptr;
    154  if (aSize.WasPassed()) {
    155    sizeVal = aSize.Value();
    156    size = &sizeVal;
    157  }
    158 
    159  ffi::wgpu_command_encoder_clear_buffer(GetClient(), mParent->GetId(), GetId(),
    160                                         aBuffer.GetId(), aOffset, size);
    161 }
    162 
    163 void CommandEncoder::PushDebugGroup(const nsAString& aString) {
    164  NS_ConvertUTF16toUTF8 marker(aString);
    165  ffi::wgpu_command_encoder_push_debug_group(GetClient(), mParent->GetId(),
    166                                             GetId(), &marker);
    167 }
    168 void CommandEncoder::PopDebugGroup() {
    169  ffi::wgpu_command_encoder_pop_debug_group(GetClient(), mParent->GetId(),
    170                                            GetId());
    171 }
    172 void CommandEncoder::InsertDebugMarker(const nsAString& aString) {
    173  NS_ConvertUTF16toUTF8 marker(aString);
    174  ffi::wgpu_command_encoder_insert_debug_marker(GetClient(), mParent->GetId(),
    175                                                GetId(), &marker);
    176 }
    177 
    178 already_AddRefed<ComputePassEncoder> CommandEncoder::BeginComputePass(
    179    const dom::GPUComputePassDescriptor& aDesc) {
    180  auto id = ffi::wgpu_client_make_compute_pass_encoder_id(GetClient());
    181  RefPtr<ComputePassEncoder> pass = new ComputePassEncoder(this, id, aDesc);
    182  pass->SetLabel(aDesc.mLabel);
    183  if (mState == CommandEncoderState::Ended) {
    184    // Because we do not call wgpu until the pass is ended, we need to generate
    185    // this error ourselves in order to report it at the correct time.
    186 
    187    const auto* message = "Encoding must not have ended";
    188    ffi::wgpu_report_validation_error(GetClient(), mParent->GetId(), message);
    189 
    190    pass->Invalidate();
    191  } else if (mState == CommandEncoderState::Locked) {
    192    // This is not sufficient to handle this case properly. Invalidity
    193    // needs to be transferred from the pass to the encoder when the pass
    194    // ends. Bug 1971650.
    195    pass->Invalidate();
    196  } else {
    197    mState = CommandEncoderState::Locked;
    198  }
    199  return pass.forget();
    200 }
    201 
    202 already_AddRefed<RenderPassEncoder> CommandEncoder::BeginRenderPass(
    203    const dom::GPURenderPassDescriptor& aDesc) {
    204  dom::GPURenderPassDescriptor desc{aDesc};
    205 
    206  auto coerceToViewInPlace =
    207      [](dom::OwningGPUTextureOrGPUTextureView& texOrView)
    208      -> RefPtr<TextureView> {
    209    RefPtr<TextureView> view;
    210    switch (texOrView.GetType()) {
    211      case dom::OwningGPUTextureOrGPUTextureView::Type::eGPUTexture: {
    212        dom::GPUTextureViewDescriptor defaultDesc{};
    213        RefPtr<Texture> tex = texOrView.GetAsGPUTexture();
    214        texOrView.SetAsGPUTextureView() = tex->CreateView(defaultDesc);
    215        break;
    216      }
    217 
    218      case dom::OwningGPUTextureOrGPUTextureView::Type::eGPUTextureView:
    219        // Nothing to do, great!
    220        break;
    221    }
    222    view = texOrView.GetAsGPUTextureView();
    223    return view;
    224  };
    225 
    226  for (auto& at : desc.mColorAttachments) {
    227    TrackPresentationContext(coerceToViewInPlace(at.mView)->GetTargetContext());
    228    if (at.mResolveTarget.WasPassed()) {
    229      TrackPresentationContext(
    230          coerceToViewInPlace(at.mResolveTarget.Value())->GetTargetContext());
    231    }
    232  }
    233  if (desc.mDepthStencilAttachment.WasPassed()) {
    234    coerceToViewInPlace(desc.mDepthStencilAttachment.Value().mView);
    235  }
    236 
    237  auto id = ffi::wgpu_client_make_render_pass_encoder_id(GetClient());
    238  RefPtr<RenderPassEncoder> pass = new RenderPassEncoder(this, id, desc);
    239  pass->SetLabel(desc.mLabel);
    240  if (mState == CommandEncoderState::Ended) {
    241    // Because we do not call wgpu until the pass is ended, we need to generate
    242    // this error ourselves in order to report it at the correct time.
    243 
    244    const auto* message = "Encoding must not have ended";
    245    ffi::wgpu_report_validation_error(GetClient(), mParent->GetId(), message);
    246 
    247    pass->Invalidate();
    248  } else if (mState == CommandEncoderState::Locked) {
    249    // This is not sufficient to handle this case properly. Invalidity
    250    // needs to be transferred from the pass to the encoder when the pass
    251    // ends. Bug 1971650.
    252    pass->Invalidate();
    253  } else {
    254    mState = CommandEncoderState::Locked;
    255  }
    256  return pass.forget();
    257 }
    258 
    259 void CommandEncoder::ResolveQuerySet(QuerySet& aQuerySet, uint32_t aFirstQuery,
    260                                     uint32_t aQueryCount,
    261                                     webgpu::Buffer& aDestination,
    262                                     uint64_t aDestinationOffset) {
    263  ffi::wgpu_command_encoder_resolve_query_set(
    264      GetClient(), mParent->GetId(), GetId(), aQuerySet.GetId(), aFirstQuery,
    265      aQueryCount, aDestination.GetId(), aDestinationOffset);
    266 }
    267 
    268 void CommandEncoder::EndComputePass(
    269    ffi::WGPURecordedComputePass& aPass, CanvasContextArray& aCanvasContexts,
    270    Span<RefPtr<ExternalTexture>> aExternalTextures) {
    271  if (mState != CommandEncoderState::Locked) {
    272    const auto* message = "Encoder is not currently locked";
    273    ffi::wgpu_report_validation_error(GetClient(), mParent->GetId(), message);
    274    return;
    275  }
    276  mState = CommandEncoderState::Open;
    277 
    278  for (const auto& context : aCanvasContexts) {
    279    TrackPresentationContext(context);
    280  }
    281  mExternalTextures.AppendElements(aExternalTextures);
    282 
    283  ffi::wgpu_compute_pass_finish(GetClient(), mParent->GetId(), GetId(), &aPass);
    284 }
    285 
    286 void CommandEncoder::EndRenderPass(
    287    ffi::WGPURecordedRenderPass& aPass, CanvasContextArray& aCanvasContexts,
    288    Span<RefPtr<ExternalTexture>> aExternalTextures) {
    289  if (mState != CommandEncoderState::Locked) {
    290    const auto* message = "Encoder is not currently locked";
    291    ffi::wgpu_report_validation_error(GetClient(), mParent->GetId(), message);
    292    return;
    293  }
    294  mState = CommandEncoderState::Open;
    295 
    296  for (const auto& context : aCanvasContexts) {
    297    TrackPresentationContext(context);
    298  }
    299  mExternalTextures.AppendElements(aExternalTextures);
    300 
    301  ffi::wgpu_render_pass_finish(GetClient(), mParent->GetId(), GetId(), &aPass);
    302 }
    303 
    304 already_AddRefed<CommandBuffer> CommandEncoder::Finish(
    305    const dom::GPUCommandBufferDescriptor& aDesc) {
    306  ffi::WGPUCommandBufferDescriptor desc = {};
    307 
    308  webgpu::StringHelper label(aDesc.mLabel);
    309  desc.label = label.Get();
    310 
    311  if (mState == CommandEncoderState::Locked) {
    312    // Most errors that could occur here will be raised by wgpu. But since we
    313    // don't tell wgpu about passes until they are ended, we need to raise an
    314    // error if the application left a pass open.
    315    const auto* message =
    316        "Encoder is locked by a previously created render/compute pass";
    317    ffi::wgpu_report_validation_error(GetClient(), mParent->GetId(), message);
    318  }
    319  RawId command_buffer_id = ffi::wgpu_command_encoder_finish(
    320      GetClient(), mParent->GetId(), GetId(), &desc);
    321 
    322  mState = CommandEncoderState::Ended;
    323 
    324  RefPtr<CommandBuffer> comb = new CommandBuffer(
    325      mParent, command_buffer_id, std::move(mPresentationContexts),
    326      std::move(mExternalTextures));
    327  comb->SetLabel(aDesc.mLabel);
    328  return comb.forget();
    329 }
    330 
    331 }  // namespace mozilla::webgpu