Hash :
84e54d88
Author :
Date :
2024-07-22T14:39:22
WebGPU: Add command buffers and renderpass management Add a CommandBuffer class to serialize and replay WebGPU commands. Only Draw and SetPipeline are implemented in this patch. Manage render pass begin and end events due to framebuffer changes and swapping. Handle the color mask dirty bits so that a non-zero color mask will be used. All togther, this is enough to draw a triangle using a hard-coded shader without inputs. Bug: angleproject:0 Change-Id: I0fbf0296563c02c7f0774ad4197b83f4c93c22bb Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5731594 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Liza Burakova <liza@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
//
// Copyright 2024 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "libANGLE/renderer/wgpu/wgpu_command_buffer.h"
namespace rx
{
namespace webgpu
{
namespace
{
template <typename T>
const T *GetReferencedObject(std::unordered_set<T> &referenceList, const T &item)
{
auto iter = referenceList.insert(item).first;
return &(*iter);
}
// Get the packed command ID from the current command data
CommandID CurrentCommandID(const uint8_t *commandData)
{
return *reinterpret_cast<const CommandID *>(commandData);
}
// Get the command struct from the current command data and increment the command data to the next
// command
template <CommandID Command, typename CommandType = CommandTypeHelper<Command>::CommandType>
const CommandType &GetCommandAndIterate(const uint8_t **commandData)
{
constexpr size_t commandAndIdSize = sizeof(CommandID) + sizeof(CommandType);
const CommandType *command =
reinterpret_cast<const CommandType *>(*commandData + sizeof(CommandID));
*commandData += commandAndIdSize;
return *command;
}
} // namespace
CommandBuffer::CommandBuffer() {}
void CommandBuffer::draw(uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance)
{
DrawCommand *drawCommand = initCommand<CommandID::Draw>();
drawCommand->vertexCount = vertexCount;
drawCommand->instanceCount = instanceCount;
drawCommand->firstVertex = firstVertex;
drawCommand->firstInstance = firstInstance;
}
void CommandBuffer::setPipeline(wgpu::RenderPipeline pipeline)
{
SetPipelineCommand *setPiplelineCommand = initCommand<CommandID::SetPipeline>();
setPiplelineCommand->pipeline = GetReferencedObject(mReferencedRenderPipelines, pipeline);
}
void CommandBuffer::clear()
{
mCommandCount = 0;
if (!mCommandBlocks.empty())
{
// Only clear the command blocks that have been used
for (size_t cmdBlockIdx = 0; cmdBlockIdx <= mCurrentCommandBlock; cmdBlockIdx++)
{
mCommandBlocks[cmdBlockIdx]->clear();
}
}
mCurrentCommandBlock = 0;
mReferencedRenderPipelines.clear();
}
void CommandBuffer::recordCommands(wgpu::RenderPassEncoder encoder)
{
ASSERT(hasCommands());
ASSERT(!mCommandBlocks.empty());
// Make sure the last block is finalized
mCommandBlocks[mCurrentCommandBlock]->finalize();
for (size_t cmdBlockIdx = 0; cmdBlockIdx <= mCurrentCommandBlock; cmdBlockIdx++)
{
const CommandBlock *commandBlock = mCommandBlocks[cmdBlockIdx].get();
const uint8_t *currentCommand = commandBlock->mData;
while (CurrentCommandID(currentCommand) != CommandID::Invalid)
{
switch (CurrentCommandID(currentCommand))
{
case CommandID::Invalid:
UNREACHABLE();
return;
case CommandID::Draw:
{
const DrawCommand &drawCommand =
GetCommandAndIterate<CommandID::Draw>(¤tCommand);
encoder.Draw(drawCommand.vertexCount, drawCommand.instanceCount,
drawCommand.firstVertex, drawCommand.firstInstance);
break;
}
case CommandID::SetPipeline:
{
const SetPipelineCommand &setPiplelineCommand =
GetCommandAndIterate<CommandID::SetPipeline>(¤tCommand);
encoder.SetPipeline(*setPiplelineCommand.pipeline);
break;
}
default:
UNREACHABLE();
return;
}
}
}
}
void CommandBuffer::nextCommandBlock()
{
if (mCurrentCommandBlock + 1 < mCommandBlocks.size())
{
// There is already a command block allocated. Make sure it's been cleared and use it.
mCurrentCommandBlock++;
ASSERT(mCommandBlocks[mCurrentCommandBlock]->mCurrentPosition == 0);
ASSERT(mCommandBlocks[mCurrentCommandBlock]->mRemainingSize > 0);
}
else
{
std::unique_ptr<CommandBlock> newBlock = std::make_unique<CommandBlock>();
mCommandBlocks.push_back(std::move(newBlock));
mCurrentCommandBlock = mCommandBlocks.size() - 1;
}
}
void CommandBuffer::CommandBlock::clear()
{
mCurrentPosition = 0;
mRemainingSize = kCommandBlockInitialRemainingSize;
}
void CommandBuffer::CommandBlock::finalize()
{
// Don't move the current position to allow finalize to be called multiple times if needed
CommandID *nextCommandID = getDataAtCurrentPositionAndReserveSpace<CommandID>(0);
*nextCommandID = CommandID::Invalid;
mRemainingSize = 0;
}
} // namespace webgpu
} // namespace rx