blob: dfdaf4d70c993f33ed1017708e8b5abf6e78a587 [file] [log] [blame]
//
// 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.
//
#ifndef LIBANGLE_RENDERER_WGPU_WGPU_COMMAND_BUFFER_H_
#define LIBANGLE_RENDERER_WGPU_WGPU_COMMAND_BUFFER_H_
#ifdef UNSAFE_BUFFERS_BUILD
# pragma allow_unsafe_buffers
#endif
#include "common/debug.h"
#include "libANGLE/renderer/wgpu/wgpu_utils.h"
#include <webgpu/webgpu.h>
#include <unordered_set>
namespace rx
{
namespace webgpu
{
#define ANGLE_WGPU_COMMANDS_X(PROC) \
PROC(BeginOcclusionQuery) \
PROC(Draw) \
PROC(DrawIndexed) \
PROC(DrawIndexedIndirect) \
PROC(DrawIndirect) \
PROC(End) \
PROC(EndOcclusionQuery) \
PROC(ExecuteBundles) \
PROC(InsertDebugMarker) \
PROC(PixelLocalStorageBarrier) \
PROC(PopDebugGroup) \
PROC(PushDebugGroup) \
PROC(SetBindGroup) \
PROC(SetBlendConstant) \
PROC(SetIndexBuffer) \
PROC(SetLabel) \
PROC(SetPipeline) \
PROC(SetScissorRect) \
PROC(SetStencilReference) \
PROC(SetVertexBuffer) \
PROC(SetViewport) \
PROC(WriteTimestamp)
#define WGPU_DECLARE_COMMAND_ID(CMD) CMD,
enum class CommandID : uint8_t
{
Invalid = 0,
ANGLE_WGPU_COMMANDS_X(WGPU_DECLARE_COMMAND_ID)
};
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
struct BeginOcclusionQueryCommand
{
uint64_t pad;
};
struct DrawCommand
{
uint32_t vertexCount;
uint32_t instanceCount;
uint32_t firstVertex;
uint32_t firstInstance;
};
struct DrawIndexedCommand
{
uint32_t indexCount;
uint32_t instanceCount;
uint32_t firstIndex;
uint32_t baseVertex;
uint32_t firstInstance;
uint32_t pad;
};
struct DrawIndexedIndirectCommand
{
uint64_t pad;
};
struct DrawIndirectCommand
{
uint64_t pad;
};
struct EndCommand
{
uint64_t pad;
};
struct EndOcclusionQueryCommand
{
uint64_t pad;
};
struct ExecuteBundlesCommand
{
uint64_t pad;
};
struct InsertDebugMarkerCommand
{
uint64_t pad;
};
struct PixelLocalStorageBarrierCommand
{
uint64_t pad;
};
struct PopDebugGroupCommand
{
uint64_t pad;
};
struct PushDebugGroupCommand
{
uint64_t pad;
};
struct SetBindGroupCommand
{
uint32_t groupIndex;
uint32_t pad0;
union
{
WGPUBindGroup bindGroup;
uint64_t pad1; // Pad to 64 bits on 32-bit systems
};
};
struct SetBlendConstantCommand
{
float r;
float g;
float b;
float a;
};
struct SetIndexBufferCommand
{
union
{
WGPUBuffer buffer;
uint64_t pad0; // Pad to 64 bits on 32-bit systems
};
WGPUIndexFormat format;
uint32_t pad1;
uint64_t offset;
uint64_t size;
};
struct SetLabelCommand
{
uint64_t pad;
};
struct SetPipelineCommand
{
union
{
WGPURenderPipeline pipeline;
uint64_t padding; // Pad to 64 bits on 32-bit systems
};
};
struct SetScissorRectCommand
{
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
};
struct SetStencilReferenceCommand
{
uint32_t referenceValue;
uint32_t pad;
};
struct SetVertexBufferCommand
{
uint32_t slot;
uint32_t pad0;
union
{
WGPUBuffer buffer;
uint64_t pad1; // Pad to 64 bits on 32-bit systems
};
uint64_t offset;
uint64_t size;
};
struct SetViewportCommand
{
float x;
float y;
float width;
float height;
float minDepth;
float maxDepth;
};
struct WriteTimestampCommand
{
uint64_t pad;
};
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
#define VERIFY_COMMAND_8_BYTE_ALIGNMENT(CMD) \
static_assert((sizeof(CMD##Command) % 8) == 0, "Check " #CMD "Command alignment");
ANGLE_WGPU_COMMANDS_X(VERIFY_COMMAND_8_BYTE_ALIGNMENT)
#undef VERIFY_COMMAND_8_BYTE_ALIGNMENT
template <CommandID>
struct CommandTypeHelper;
#define ANGLE_WGPU_COMMAND_TYPE_HELPER(CMD) \
template <> \
struct CommandTypeHelper<CommandID::CMD> \
{ \
using CommandType = CMD##Command; \
};
ANGLE_WGPU_COMMANDS_X(ANGLE_WGPU_COMMAND_TYPE_HELPER)
#undef ANGLE_WGPU_COMMAND_TYPE_HELPER
static constexpr size_t kCommandBlockSize = 1 << 14; // 16kB
class CommandBuffer
{
public:
CommandBuffer();
void draw(uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance);
void drawIndexed(uint32_t indexCount,
uint32_t instanceCount,
uint32_t firstIndex,
int32_t baseVertex,
uint32_t firstInstance);
void setBindGroup(uint32_t groupIndex, BindGroupHandle bindGroup);
void setBlendConstant(float r, float g, float b, float a);
void setPipeline(RenderPipelineHandle pipeline);
void setScissorRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height);
void setStencilReference(uint32_t refVal);
void setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);
void setIndexBuffer(BufferHandle buffer,
WGPUIndexFormat format,
uint64_t offset,
uint64_t size);
void setVertexBuffer(uint32_t slot, BufferHandle buffer, uint64_t offset, uint64_t size);
void clear();
bool hasCommands() const { return mState.commandCount > 0; }
bool hasSetScissorCommand() const { return mState.hasSetScissorCommand; }
bool hasSetStencilRefCommand() const { return mState.hasSetStencilRefCommand; }
bool hasSetViewportCommand() const { return mState.hasSetViewportCommand; }
bool hasSetBlendConstantCommand() const { return mState.hasSetBlendConstantCommand; }
void recordCommands(const DawnProcTable *wgpu, RenderPassEncoderHandle encoder);
private:
struct CommandBlock
{
static constexpr size_t kCommandBlockDataSize = kCommandBlockSize - (sizeof(size_t) * 2);
uint8_t mData[kCommandBlockDataSize] = {0};
size_t mCurrentPosition = 0;
static constexpr size_t kCommandIDSize = sizeof(CommandID);
static constexpr size_t kCommandBlockInitialRemainingSize =
kCommandBlockDataSize - kCommandIDSize; // Leave room for one command ID at the end to
// signify the end of the list
size_t mRemainingSize = kCommandBlockInitialRemainingSize;
void clear();
void finalize();
template <typename T>
T *getDataAtCurrentPositionAndReserveSpace(size_t space)
{
T *data = reinterpret_cast<T *>(&mData[mCurrentPosition]);
ASSERT(mRemainingSize >= space);
mCurrentPosition += space;
mRemainingSize -= space;
return data;
}
};
static constexpr size_t kCommandBlockStructSize = sizeof(CommandBlock);
static_assert(kCommandBlockStructSize == kCommandBlockSize, "Size mismatch");
std::vector<std::unique_ptr<CommandBlock>> mCommandBlocks;
// State for the current commands held in mCommandBlocks. In a structure so that it can be
// easily reset by calling the constructor.
struct PerSubmissionData
{
size_t currentCommandBlock = 0;
size_t commandCount = 0;
bool hasSetScissorCommand = false;
bool hasSetStencilRefCommand = false;
bool hasSetViewportCommand = false;
bool hasSetBlendConstantCommand = false;
// std::unordered_set required because it does not move elements and stored command
// reference addresses in the set
std::unordered_set<RenderPipelineHandle> referencedRenderPipelines;
std::unordered_set<BufferHandle> referencedBuffers;
std::unordered_set<BindGroupHandle> referencedBindGroups;
};
PerSubmissionData mState;
void nextCommandBlock();
void ensureCommandSpace(size_t space)
{
if (mCommandBlocks.empty() ||
mCommandBlocks[mState.currentCommandBlock]->mRemainingSize < space)
{
nextCommandBlock();
}
}
template <CommandID Command, typename CommandType = CommandTypeHelper<Command>::CommandType>
CommandType *initCommand()
{
constexpr size_t allocationSize = sizeof(CommandID) + sizeof(CommandType);
ensureCommandSpace(allocationSize);
CommandBlock *commandBlock = mCommandBlocks[mState.currentCommandBlock].get();
uint8_t *idAndCommandStorage =
commandBlock->getDataAtCurrentPositionAndReserveSpace<uint8_t>(allocationSize);
CommandID *id = reinterpret_cast<CommandID *>(idAndCommandStorage);
*id = Command;
CommandType *commandStruct =
reinterpret_cast<CommandType *>(idAndCommandStorage + sizeof(CommandID));
mState.commandCount++;
return commandStruct;
}
};
} // namespace webgpu
} // namespace rx
#endif // LIBANGLE_RENDERER_WGPU_WGPU_COMMAND_BUFFER_H_