1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "libANGLE/renderer/wgpu/wgpu_command_buffer.h"
8
9 namespace rx
10 {
11 namespace webgpu
12 {
13 namespace
14 {
15 template <typename T>
GetReferencedObject(std::unordered_set<T> & referenceList,const T & item)16 const T *GetReferencedObject(std::unordered_set<T> &referenceList, const T &item)
17 {
18 auto iter = referenceList.insert(item).first;
19 return &(*iter);
20 }
21
22 // Get the packed command ID from the current command data
CurrentCommandID(const uint8_t * commandData)23 CommandID CurrentCommandID(const uint8_t *commandData)
24 {
25 return *reinterpret_cast<const CommandID *>(commandData);
26 }
27
28 // Get the command struct from the current command data and increment the command data to the next
29 // command
30 template <CommandID Command, typename CommandType = CommandTypeHelper<Command>::CommandType>
GetCommandAndIterate(const uint8_t ** commandData)31 const CommandType &GetCommandAndIterate(const uint8_t **commandData)
32 {
33 constexpr size_t commandAndIdSize = sizeof(CommandID) + sizeof(CommandType);
34 const CommandType *command =
35 reinterpret_cast<const CommandType *>(*commandData + sizeof(CommandID));
36 *commandData += commandAndIdSize;
37 return *command;
38 }
39 } // namespace
40
CommandBuffer()41 CommandBuffer::CommandBuffer() {}
42
draw(uint32_t vertexCount,uint32_t instanceCount,uint32_t firstVertex,uint32_t firstInstance)43 void CommandBuffer::draw(uint32_t vertexCount,
44 uint32_t instanceCount,
45 uint32_t firstVertex,
46 uint32_t firstInstance)
47 {
48 DrawCommand *drawCommand = initCommand<CommandID::Draw>();
49 drawCommand->vertexCount = vertexCount;
50 drawCommand->instanceCount = instanceCount;
51 drawCommand->firstVertex = firstVertex;
52 drawCommand->firstInstance = firstInstance;
53 }
54
drawIndexed(uint32_t indexCount,uint32_t instanceCount,uint32_t firstIndex,int32_t baseVertex,uint32_t firstInstance)55 void CommandBuffer::drawIndexed(uint32_t indexCount,
56 uint32_t instanceCount,
57 uint32_t firstIndex,
58 int32_t baseVertex,
59 uint32_t firstInstance)
60 {
61 DrawIndexedCommand *drawIndexedCommand = initCommand<CommandID::DrawIndexed>();
62 drawIndexedCommand->indexCount = indexCount;
63 drawIndexedCommand->instanceCount = instanceCount;
64 drawIndexedCommand->firstIndex = firstIndex;
65 drawIndexedCommand->baseVertex = baseVertex;
66 drawIndexedCommand->firstInstance = firstInstance;
67 }
68
setBindGroup(uint32_t groupIndex,wgpu::BindGroup bindGroup)69 void CommandBuffer::setBindGroup(uint32_t groupIndex, wgpu::BindGroup bindGroup)
70 {
71 SetBindGroupCommand *setBindGroupCommand = initCommand<CommandID::SetBindGroup>();
72 setBindGroupCommand->groupIndex = groupIndex;
73 setBindGroupCommand->bindGroup = GetReferencedObject(mReferencedBindGroups, bindGroup);
74 }
75
setBlendConstant(float r,float g,float b,float a)76 void CommandBuffer::setBlendConstant(float r, float g, float b, float a)
77 {
78 SetBlendConstantCommand *setBlendConstantCommand = initCommand<CommandID::SetBlendConstant>();
79 setBlendConstantCommand->r = r;
80 setBlendConstantCommand->g = g;
81 setBlendConstantCommand->b = b;
82 setBlendConstantCommand->a = a;
83
84 mHasSetBlendConstantCommand = true;
85 }
86
setPipeline(wgpu::RenderPipeline pipeline)87 void CommandBuffer::setPipeline(wgpu::RenderPipeline pipeline)
88 {
89 SetPipelineCommand *setPiplelineCommand = initCommand<CommandID::SetPipeline>();
90 setPiplelineCommand->pipeline = GetReferencedObject(mReferencedRenderPipelines, pipeline);
91 }
92
setScissorRect(uint32_t x,uint32_t y,uint32_t width,uint32_t height)93 void CommandBuffer::setScissorRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
94 {
95 SetScissorRectCommand *setScissorRectCommand = initCommand<CommandID::SetScissorRect>();
96 setScissorRectCommand->x = x;
97 setScissorRectCommand->y = y;
98 setScissorRectCommand->width = width;
99 setScissorRectCommand->height = height;
100
101 mHasSetScissorCommand = true;
102 }
103
setViewport(float x,float y,float width,float height,float minDepth,float maxDepth)104 void CommandBuffer::setViewport(float x,
105 float y,
106 float width,
107 float height,
108 float minDepth,
109 float maxDepth)
110 {
111 SetViewportCommand *setViewportCommand = initCommand<CommandID::SetViewport>();
112 setViewportCommand->x = x;
113 setViewportCommand->y = y;
114 setViewportCommand->width = width;
115 setViewportCommand->height = height;
116 setViewportCommand->minDepth = minDepth;
117 setViewportCommand->maxDepth = maxDepth;
118
119 mHasSetViewportCommand = true;
120 }
121
setIndexBuffer(wgpu::Buffer buffer,wgpu::IndexFormat format,uint64_t offset,uint64_t size)122 void CommandBuffer::setIndexBuffer(wgpu::Buffer buffer,
123 wgpu::IndexFormat format,
124 uint64_t offset,
125 uint64_t size)
126 {
127 SetIndexBufferCommand *setIndexBufferCommand = initCommand<CommandID::SetIndexBuffer>();
128 setIndexBufferCommand->buffer = GetReferencedObject(mReferencedBuffers, buffer);
129 setIndexBufferCommand->format = format;
130 setIndexBufferCommand->offset = offset;
131 setIndexBufferCommand->size = size;
132 }
133
setVertexBuffer(uint32_t slot,wgpu::Buffer buffer,uint64_t offset,uint64_t size)134 void CommandBuffer::setVertexBuffer(uint32_t slot,
135 wgpu::Buffer buffer,
136 uint64_t offset,
137 uint64_t size)
138 {
139 SetVertexBufferCommand *setVertexBufferCommand = initCommand<CommandID::SetVertexBuffer>();
140 setVertexBufferCommand->slot = slot;
141 setVertexBufferCommand->buffer = GetReferencedObject(mReferencedBuffers, buffer);
142 setVertexBufferCommand->offset = offset;
143 setVertexBufferCommand->size = size;
144 }
145
clear()146 void CommandBuffer::clear()
147 {
148 mCommandCount = 0;
149
150 mHasSetScissorCommand = false;
151 mHasSetViewportCommand = false;
152 mHasSetBlendConstantCommand = false;
153
154 if (!mCommandBlocks.empty())
155 {
156 // Only clear the command blocks that have been used
157 for (size_t cmdBlockIdx = 0; cmdBlockIdx <= mCurrentCommandBlock; cmdBlockIdx++)
158 {
159 mCommandBlocks[cmdBlockIdx]->clear();
160 }
161 }
162 mCurrentCommandBlock = 0;
163
164 mReferencedRenderPipelines.clear();
165 mReferencedBuffers.clear();
166 }
167
recordCommands(wgpu::RenderPassEncoder encoder)168 void CommandBuffer::recordCommands(wgpu::RenderPassEncoder encoder)
169 {
170 ASSERT(hasCommands());
171 ASSERT(!mCommandBlocks.empty());
172
173 // Make sure the last block is finalized
174 mCommandBlocks[mCurrentCommandBlock]->finalize();
175
176 for (size_t cmdBlockIdx = 0; cmdBlockIdx <= mCurrentCommandBlock; cmdBlockIdx++)
177 {
178 const CommandBlock *commandBlock = mCommandBlocks[cmdBlockIdx].get();
179
180 const uint8_t *currentCommand = commandBlock->mData;
181 while (CurrentCommandID(currentCommand) != CommandID::Invalid)
182 {
183 switch (CurrentCommandID(currentCommand))
184 {
185 case CommandID::Invalid:
186 UNREACHABLE();
187 return;
188
189 case CommandID::Draw:
190 {
191 const DrawCommand &drawCommand =
192 GetCommandAndIterate<CommandID::Draw>(¤tCommand);
193 encoder.Draw(drawCommand.vertexCount, drawCommand.instanceCount,
194 drawCommand.firstVertex, drawCommand.firstInstance);
195 break;
196 }
197
198 case CommandID::DrawIndexed:
199 {
200 const DrawIndexedCommand &drawIndexedCommand =
201 GetCommandAndIterate<CommandID::DrawIndexed>(¤tCommand);
202 encoder.DrawIndexed(
203 drawIndexedCommand.indexCount, drawIndexedCommand.instanceCount,
204 drawIndexedCommand.firstIndex, drawIndexedCommand.baseVertex,
205 drawIndexedCommand.firstInstance);
206 break;
207 }
208
209 case CommandID::SetBindGroup:
210 {
211 const SetBindGroupCommand &setBindGroupCommand =
212 GetCommandAndIterate<CommandID::SetBindGroup>(¤tCommand);
213 encoder.SetBindGroup(setBindGroupCommand.groupIndex,
214 *setBindGroupCommand.bindGroup);
215 break;
216 }
217
218 case CommandID::SetBlendConstant:
219 {
220 const SetBlendConstantCommand &setBlendConstantCommand =
221 GetCommandAndIterate<CommandID::SetBlendConstant>(¤tCommand);
222 wgpu::Color color{setBlendConstantCommand.r, setBlendConstantCommand.g,
223 setBlendConstantCommand.b, setBlendConstantCommand.a};
224 encoder.SetBlendConstant(&color);
225 break;
226 }
227
228 case CommandID::SetIndexBuffer:
229 {
230 const SetIndexBufferCommand &setIndexBufferCommand =
231 GetCommandAndIterate<CommandID::SetIndexBuffer>(¤tCommand);
232 encoder.SetIndexBuffer(
233 *setIndexBufferCommand.buffer, setIndexBufferCommand.format,
234 setIndexBufferCommand.offset, setIndexBufferCommand.size);
235 break;
236 }
237
238 case CommandID::SetPipeline:
239 {
240 const SetPipelineCommand &setPiplelineCommand =
241 GetCommandAndIterate<CommandID::SetPipeline>(¤tCommand);
242 encoder.SetPipeline(*setPiplelineCommand.pipeline);
243 break;
244 }
245
246 case CommandID::SetScissorRect:
247 {
248 const SetScissorRectCommand &setScissorRectCommand =
249 GetCommandAndIterate<CommandID::SetScissorRect>(¤tCommand);
250 encoder.SetScissorRect(setScissorRectCommand.x, setScissorRectCommand.y,
251 setScissorRectCommand.width,
252 setScissorRectCommand.height);
253 break;
254 }
255
256 case CommandID::SetViewport:
257 {
258 const SetViewportCommand &setViewportCommand =
259 GetCommandAndIterate<CommandID::SetViewport>(¤tCommand);
260 encoder.SetViewport(setViewportCommand.x, setViewportCommand.y,
261 setViewportCommand.width, setViewportCommand.height,
262 setViewportCommand.minDepth, setViewportCommand.maxDepth);
263 break;
264 }
265
266 case CommandID::SetVertexBuffer:
267 {
268 const SetVertexBufferCommand &setVertexBufferCommand =
269 GetCommandAndIterate<CommandID::SetVertexBuffer>(¤tCommand);
270 encoder.SetVertexBuffer(
271 setVertexBufferCommand.slot, *setVertexBufferCommand.buffer,
272 setVertexBufferCommand.offset, setVertexBufferCommand.size);
273 break;
274 }
275
276 default:
277 UNREACHABLE();
278 return;
279 }
280 }
281 }
282 }
283
nextCommandBlock()284 void CommandBuffer::nextCommandBlock()
285 {
286 if (mCurrentCommandBlock + 1 < mCommandBlocks.size())
287 {
288 // There is already a command block allocated. Make sure it's been cleared and use it.
289 mCurrentCommandBlock++;
290 ASSERT(mCommandBlocks[mCurrentCommandBlock]->mCurrentPosition == 0);
291 ASSERT(mCommandBlocks[mCurrentCommandBlock]->mRemainingSize > 0);
292 }
293 else
294 {
295 std::unique_ptr<CommandBlock> newBlock = std::make_unique<CommandBlock>();
296 mCommandBlocks.push_back(std::move(newBlock));
297 mCurrentCommandBlock = mCommandBlocks.size() - 1;
298 }
299 }
300
clear()301 void CommandBuffer::CommandBlock::clear()
302 {
303 mCurrentPosition = 0;
304 mRemainingSize = kCommandBlockInitialRemainingSize;
305 }
306
finalize()307 void CommandBuffer::CommandBlock::finalize()
308 {
309 // Don't move the current position to allow finalize to be called multiple times if needed
310 CommandID *nextCommandID = getDataAtCurrentPositionAndReserveSpace<CommandID>(0);
311 *nextCommandID = CommandID::Invalid;
312 mRemainingSize = 0;
313 }
314
315 } // namespace webgpu
316 } // namespace rx
317