• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params,
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