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 // FramebufferWgpu.cpp:
7 // Implements the class methods for FramebufferWgpu.
8 //
9
10 #include "libANGLE/renderer/wgpu/FramebufferWgpu.h"
11 #include <__config>
12
13 #include "common/debug.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/formatutils.h"
16 #include "libANGLE/renderer/wgpu/BufferWgpu.h"
17 #include "libANGLE/renderer/wgpu/ContextWgpu.h"
18 #include "libANGLE/renderer/wgpu/wgpu_utils.h"
19
20 namespace rx
21 {
22
23 namespace
24 {
CompareColorRenderPassAttachments(const wgpu::RenderPassColorAttachment & attachment1,const wgpu::RenderPassColorAttachment & attachment2)25 bool CompareColorRenderPassAttachments(const wgpu::RenderPassColorAttachment &attachment1,
26 const wgpu::RenderPassColorAttachment &attachment2)
27 {
28
29 if (attachment1.nextInChain != nullptr || attachment2.nextInChain != nullptr)
30 {
31 return false;
32 }
33
34 return attachment1.view.Get() == attachment2.view.Get() &&
35 attachment1.depthSlice == attachment2.depthSlice &&
36 attachment1.resolveTarget.Get() == attachment2.resolveTarget.Get() &&
37 attachment1.loadOp == attachment2.loadOp && attachment1.storeOp == attachment2.storeOp &&
38 attachment1.clearValue.r == attachment2.clearValue.r &&
39 attachment1.clearValue.g == attachment2.clearValue.g &&
40 attachment1.clearValue.b == attachment2.clearValue.b &&
41 attachment1.clearValue.a == attachment2.clearValue.a;
42 }
43
CompareColorRenderPassAttachmentVectors(const std::vector<wgpu::RenderPassColorAttachment> & attachments1,const std::vector<wgpu::RenderPassColorAttachment> & attachments2)44 bool CompareColorRenderPassAttachmentVectors(
45 const std::vector<wgpu::RenderPassColorAttachment> &attachments1,
46 const std::vector<wgpu::RenderPassColorAttachment> &attachments2)
47 {
48 if (attachments1.size() != attachments2.size())
49 {
50 return false;
51 }
52
53 for (uint32_t i = 0; i < attachments1.size(); ++i)
54 {
55 if (!CompareColorRenderPassAttachments(attachments1[i], attachments2[i]))
56 {
57 return false;
58 }
59 }
60
61 return true;
62 }
63
CompareDepthStencilRenderPassAttachments(const wgpu::RenderPassDepthStencilAttachment & attachment1,const wgpu::RenderPassDepthStencilAttachment & attachment2)64 bool CompareDepthStencilRenderPassAttachments(
65 const wgpu::RenderPassDepthStencilAttachment &attachment1,
66 const wgpu::RenderPassDepthStencilAttachment &attachment2)
67 {
68 return attachment1.view.Get() == attachment2.view.Get() &&
69 attachment1.depthLoadOp == attachment2.depthLoadOp &&
70 attachment1.depthStoreOp == attachment2.depthStoreOp &&
71 attachment1.depthClearValue == attachment2.depthClearValue &&
72 attachment1.stencilLoadOp == attachment2.stencilLoadOp &&
73 attachment1.stencilStoreOp == attachment2.stencilStoreOp &&
74 attachment1.stencilClearValue == attachment2.stencilClearValue &&
75 attachment1.stencilReadOnly == attachment2.stencilReadOnly;
76 }
77 } // namespace
78
FramebufferWgpu(const gl::FramebufferState & state)79 FramebufferWgpu::FramebufferWgpu(const gl::FramebufferState &state) : FramebufferImpl(state) {}
80
~FramebufferWgpu()81 FramebufferWgpu::~FramebufferWgpu() {}
82
discard(const gl::Context * context,size_t count,const GLenum * attachments)83 angle::Result FramebufferWgpu::discard(const gl::Context *context,
84 size_t count,
85 const GLenum *attachments)
86 {
87 return angle::Result::Continue;
88 }
89
invalidate(const gl::Context * context,size_t count,const GLenum * attachments)90 angle::Result FramebufferWgpu::invalidate(const gl::Context *context,
91 size_t count,
92 const GLenum *attachments)
93 {
94 return angle::Result::Continue;
95 }
96
invalidateSub(const gl::Context * context,size_t count,const GLenum * attachments,const gl::Rectangle & area)97 angle::Result FramebufferWgpu::invalidateSub(const gl::Context *context,
98 size_t count,
99 const GLenum *attachments,
100 const gl::Rectangle &area)
101 {
102 return angle::Result::Continue;
103 }
104
clear(const gl::Context * context,GLbitfield mask)105 angle::Result FramebufferWgpu::clear(const gl::Context *context, GLbitfield mask)
106 {
107 bool clearColor = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT));
108 bool clearDepth = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT));
109 bool clearStencil = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_STENCIL_BUFFER_BIT));
110 // TODO(anglebug.com/8582): support clearing depth and stencil buffers.
111 ASSERT(!clearDepth && !clearStencil && clearColor);
112
113 ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);
114 gl::ColorF colorClearValue = context->getState().getColorClearValue();
115 gl::DrawBufferMask clearColorBuffers = mState.getEnabledDrawBuffers();
116 wgpu::Color clearValue;
117 clearValue.r = colorClearValue.red;
118 clearValue.g = colorClearValue.green;
119 clearValue.b = colorClearValue.blue;
120 clearValue.a = colorClearValue.alpha;
121 std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
122 for (size_t enabledDrawBuffer : clearColorBuffers)
123 {
124 colorAttachments.push_back(webgpu::CreateNewClearColorAttachment(
125 clearValue, wgpu::kDepthSliceUndefined,
126 mRenderTargetCache.getColorDraw(mState, enabledDrawBuffer)->getTexture()));
127 }
128
129 // Attempt to end a render pass if one has already been started.
130 ANGLE_UNUSED_VARIABLE(CompareDepthStencilRenderPassAttachments);
131
132 bool isActiveRenderPass =
133 !CompareColorRenderPassAttachmentVectors(mCurrentColorAttachments, colorAttachments) ||
134 contextWgpu->hasActiveRenderPass();
135
136 // If there is not currently an active render pass, merge clears with the deferred clears. This
137 // is to keep the clear paths simpler so they only need to consider the current or the deferred
138 // clears.
139 if (!isActiveRenderPass)
140 {
141 for (size_t enabledDrawBuffer : clearColorBuffers)
142 {
143 mDeferredClears.store(static_cast<uint32_t>(enabledDrawBuffer),
144 {clearValue, wgpu::kDepthSliceUndefined});
145 }
146 }
147
148 if (mDeferredClears.any())
149 {
150 if (isActiveRenderPass)
151 {
152 ANGLE_TRY(flushDeferredClears(contextWgpu));
153 }
154 else
155 {
156 for (size_t colorIndexGL : mDeferredClears.getColorMask())
157 {
158 RenderTargetWgpu *renderTarget =
159 mRenderTargetCache.getColorDraw(mState, colorIndexGL);
160 renderTarget->getImage()->stageClear(
161 renderTarget->getImage()->toGlLevel(renderTarget->getLevelIndex()),
162 mDeferredClears[colorIndexGL]);
163 }
164 mDeferredClears.reset();
165 }
166 return angle::Result::Continue;
167 }
168
169 if (isActiveRenderPass)
170 {
171 ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
172
173 mCurrentColorAttachments = std::move(colorAttachments);
174 mCurrentRenderPassDesc.colorAttachmentCount = mCurrentColorAttachments.size();
175 mCurrentRenderPassDesc.colorAttachments = mCurrentColorAttachments.data();
176 }
177
178 ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));
179 ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
180 ANGLE_TRY(contextWgpu->flush());
181 return angle::Result::Continue;
182 }
183
clearBufferfv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLfloat * values)184 angle::Result FramebufferWgpu::clearBufferfv(const gl::Context *context,
185 GLenum buffer,
186 GLint drawbuffer,
187 const GLfloat *values)
188 {
189 return angle::Result::Continue;
190 }
191
clearBufferuiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLuint * values)192 angle::Result FramebufferWgpu::clearBufferuiv(const gl::Context *context,
193 GLenum buffer,
194 GLint drawbuffer,
195 const GLuint *values)
196 {
197 return angle::Result::Continue;
198 }
199
clearBufferiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLint * values)200 angle::Result FramebufferWgpu::clearBufferiv(const gl::Context *context,
201 GLenum buffer,
202 GLint drawbuffer,
203 const GLint *values)
204 {
205 return angle::Result::Continue;
206 }
207
clearBufferfi(const gl::Context * context,GLenum buffer,GLint drawbuffer,GLfloat depth,GLint stencil)208 angle::Result FramebufferWgpu::clearBufferfi(const gl::Context *context,
209 GLenum buffer,
210 GLint drawbuffer,
211 GLfloat depth,
212 GLint stencil)
213 {
214 return angle::Result::Continue;
215 }
216
readPixels(const gl::Context * context,const gl::Rectangle & origArea,GLenum format,GLenum type,const gl::PixelPackState & pack,gl::Buffer * packBuffer,void * ptrOrOffset)217 angle::Result FramebufferWgpu::readPixels(const gl::Context *context,
218 const gl::Rectangle &origArea,
219 GLenum format,
220 GLenum type,
221 const gl::PixelPackState &pack,
222 gl::Buffer *packBuffer,
223 void *ptrOrOffset)
224 {
225 // Get the pointer to write to from the argument or the pack buffer
226 GLubyte *pixels = nullptr;
227 if (packBuffer != nullptr)
228 {
229 UNREACHABLE();
230 }
231 else
232 {
233 pixels = reinterpret_cast<GLubyte *>(ptrOrOffset);
234 }
235
236 // Clip read area to framebuffer.
237 const gl::Extents fbSize = getState().getReadPixelsAttachment(format)->getSize();
238 const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
239 gl::Rectangle clippedArea;
240 if (!ClipRectangle(origArea, fbRect, &clippedArea))
241 {
242 // nothing to read
243 return angle::Result::Continue;
244 }
245
246 ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);
247
248 ANGLE_TRY(flushDeferredClears(contextWgpu));
249
250 GLuint outputSkipBytes = 0;
251 PackPixelsParams params;
252 const angle::Format &angleFormat = GetFormatFromFormatType(format, type);
253 ANGLE_TRY(webgpu::ImageHelper::getReadPixelsParams(contextWgpu, pack, packBuffer, format, type,
254 origArea, clippedArea, ¶ms,
255 &outputSkipBytes));
256
257 RenderTargetWgpu *renderTarget = getReadPixelsRenderTarget(angleFormat);
258 ANGLE_TRY(
259 renderTarget->getImage()->readPixels(contextWgpu, params.area, params, angleFormat,
260 static_cast<uint8_t *>(pixels) + outputSkipBytes));
261
262 return angle::Result::Continue;
263 }
264
blit(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,GLbitfield mask,GLenum filter)265 angle::Result FramebufferWgpu::blit(const gl::Context *context,
266 const gl::Rectangle &sourceArea,
267 const gl::Rectangle &destArea,
268 GLbitfield mask,
269 GLenum filter)
270 {
271 return angle::Result::Continue;
272 }
273
checkStatus(const gl::Context * context) const274 gl::FramebufferStatus FramebufferWgpu::checkStatus(const gl::Context *context) const
275 {
276 return gl::FramebufferStatus::Complete();
277 }
278
syncState(const gl::Context * context,GLenum binding,const gl::Framebuffer::DirtyBits & dirtyBits,gl::Command command)279 angle::Result FramebufferWgpu::syncState(const gl::Context *context,
280 GLenum binding,
281 const gl::Framebuffer::DirtyBits &dirtyBits,
282 gl::Command command)
283 {
284 ASSERT(dirtyBits.any());
285
286 gl::DrawBufferMask dirtyColorAttachments;
287 for (size_t dirtyBit : dirtyBits)
288 {
289 switch (dirtyBit)
290 {
291 case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
292 case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS:
293 case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
294 case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS:
295 ANGLE_TRY(mRenderTargetCache.updateDepthStencilRenderTarget(context, mState));
296 break;
297 case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
298 ANGLE_TRY(mRenderTargetCache.update(context, mState, dirtyBits));
299 break;
300 case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
301 case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
302 case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
303 case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
304 case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
305 case gl::Framebuffer::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE:
306 case gl::Framebuffer::DIRTY_BIT_DEFAULT_LAYERS:
307 case gl::Framebuffer::DIRTY_BIT_FOVEATION:
308 break;
309 default:
310 {
311 static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
312 uint32_t colorIndexGL;
313 if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
314 {
315 colorIndexGL = static_cast<uint32_t>(
316 dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
317 }
318 else
319 {
320 ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 &&
321 dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX);
322 colorIndexGL = static_cast<uint32_t>(
323 dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0);
324 }
325
326 ANGLE_TRY(
327 mRenderTargetCache.updateColorRenderTarget(context, mState, colorIndexGL));
328
329 dirtyColorAttachments.set(colorIndexGL);
330 break;
331 }
332 }
333 }
334
335 // Like in Vulkan, defer clears for draw framebuffer ops as well as clears to read framebuffer
336 // attachments that are not taking part in a blit operation.
337 const bool isBlitCommand = command >= gl::Command::Blit && command <= gl::Command::BlitAll;
338 bool deferColorClears = binding == GL_DRAW_FRAMEBUFFER;
339 if (binding == GL_READ_FRAMEBUFFER && isBlitCommand)
340 {
341 uint32_t blitMask =
342 static_cast<uint32_t>(command) - static_cast<uint32_t>(gl::Command::Blit);
343 if ((blitMask & gl::CommandBlitBufferColor) == 0)
344 {
345 deferColorClears = true;
346 }
347 }
348
349 ANGLE_TRY(flushColorAttachmentUpdates(context, dirtyColorAttachments, deferColorClears));
350 return angle::Result::Continue;
351 }
352
getSamplePosition(const gl::Context * context,size_t index,GLfloat * xy) const353 angle::Result FramebufferWgpu::getSamplePosition(const gl::Context *context,
354 size_t index,
355 GLfloat *xy) const
356 {
357 return angle::Result::Continue;
358 }
359
getReadPixelsRenderTarget(const angle::Format & format) const360 RenderTargetWgpu *FramebufferWgpu::getReadPixelsRenderTarget(const angle::Format &format) const
361 {
362 if (format.hasDepthOrStencilBits())
363 {
364 return mRenderTargetCache.getDepthStencil();
365 }
366 return mRenderTargetCache.getColorRead(mState);
367 }
368
addNewColorAttachments(std::vector<wgpu::RenderPassColorAttachment> newColorAttachments)369 void FramebufferWgpu::addNewColorAttachments(
370 std::vector<wgpu::RenderPassColorAttachment> newColorAttachments)
371 {
372 mNewColorAttachments.insert(mCurrentColorAttachments.end(), newColorAttachments.begin(),
373 newColorAttachments.end());
374 }
375
flushOneColorAttachmentUpdate(const gl::Context * context,bool deferClears,uint32_t colorIndexGL)376 angle::Result FramebufferWgpu::flushOneColorAttachmentUpdate(const gl::Context *context,
377 bool deferClears,
378 uint32_t colorIndexGL)
379 {
380 ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);
381 RenderTargetWgpu *drawRenderTarget = nullptr;
382 RenderTargetWgpu *readRenderTarget = nullptr;
383
384 drawRenderTarget = mRenderTargetCache.getColorDraw(mState, colorIndexGL);
385 if (drawRenderTarget)
386 {
387 if (deferClears)
388 {
389 ANGLE_TRY(drawRenderTarget->getImage()->flushStagedUpdates(
390 contextWgpu, &mDeferredClears, colorIndexGL));
391 }
392 else
393 {
394 ANGLE_TRY(drawRenderTarget->getImage()->flushStagedUpdates(contextWgpu));
395 }
396 }
397
398 if (mState.getReadBufferState() != GL_NONE && mState.getReadIndex() == colorIndexGL)
399 {
400 readRenderTarget = mRenderTargetCache.getColorRead(mState);
401 if (readRenderTarget && readRenderTarget != drawRenderTarget)
402 {
403 ANGLE_TRY(readRenderTarget->getImage()->flushStagedUpdates(contextWgpu));
404 }
405 }
406
407 return angle::Result::Continue;
408 }
409
flushColorAttachmentUpdates(const gl::Context * context,gl::DrawBufferMask dirtyColorAttachments,bool deferClears)410 angle::Result FramebufferWgpu::flushColorAttachmentUpdates(const gl::Context *context,
411 gl::DrawBufferMask dirtyColorAttachments,
412 bool deferClears)
413 {
414 for (size_t colorIndexGL : dirtyColorAttachments)
415 {
416 ANGLE_TRY(flushOneColorAttachmentUpdate(context, deferClears,
417 static_cast<uint32_t>(colorIndexGL)));
418 }
419
420 // If we added any new color attachments, we start a render pass to fully flush the updates.
421 if (!mNewColorAttachments.empty() != mCurrentColorAttachments.size())
422 {
423 ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);
424 // Flush out a render pass if there is an active one.
425 ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
426 ANGLE_TRY(contextWgpu->flush());
427
428 mCurrentColorAttachments = mNewColorAttachments;
429 mNewColorAttachments.clear();
430 mCurrentRenderPassDesc.colorAttachmentCount = mCurrentColorAttachments.size();
431 mCurrentRenderPassDesc.colorAttachments = mCurrentColorAttachments.data();
432 ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));
433 }
434 return angle::Result::Continue;
435 }
436
flushDeferredClears(ContextWgpu * contextWgpu)437 angle::Result FramebufferWgpu::flushDeferredClears(ContextWgpu *contextWgpu)
438 {
439 if (mDeferredClears.empty())
440 {
441 return angle::Result::Continue;
442 }
443 ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
444 mCurrentColorAttachments.clear();
445 for (size_t colorIndexGL : mState.getColorAttachmentsMask())
446 {
447 if (!mDeferredClears.test(colorIndexGL))
448 {
449 continue;
450 }
451 mCurrentColorAttachments.push_back(webgpu::CreateNewClearColorAttachment(
452 mDeferredClears[colorIndexGL].clearColor, mDeferredClears[colorIndexGL].depthSlice,
453 mRenderTargetCache.getColorDraw(mState, colorIndexGL)->getTexture()));
454 }
455 mCurrentRenderPassDesc.colorAttachmentCount = mCurrentColorAttachments.size();
456 mCurrentRenderPassDesc.colorAttachments = mCurrentColorAttachments.data();
457 ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));
458 ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
459 ANGLE_TRY(contextWgpu->flush());
460
461 return angle::Result::Continue;
462 }
463 } // namespace rx
464