//
// 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.
//
// FramebufferWgpu.cpp:
//    Implements the class methods for FramebufferWgpu.
//

#include "libANGLE/renderer/wgpu/FramebufferWgpu.h"
#include <__config>

#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/wgpu/BufferWgpu.h"
#include "libANGLE/renderer/wgpu/ContextWgpu.h"
#include "libANGLE/renderer/wgpu/RenderTargetWgpu.h"
#include "libANGLE/renderer/wgpu/wgpu_utils.h"

namespace rx
{

namespace
{
bool CompareColorRenderPassAttachments(const wgpu::RenderPassColorAttachment &attachment1,
                                       const wgpu::RenderPassColorAttachment &attachment2)
{

    if (attachment1.nextInChain != nullptr || attachment2.nextInChain != nullptr)
    {
        return false;
    }

    return attachment1.view.Get() == attachment2.view.Get() &&
           attachment1.depthSlice == attachment2.depthSlice &&
           attachment1.resolveTarget.Get() == attachment2.resolveTarget.Get() &&
           attachment1.loadOp == attachment2.loadOp && attachment1.storeOp == attachment2.storeOp &&
           attachment1.clearValue.r == attachment2.clearValue.r &&
           attachment1.clearValue.g == attachment2.clearValue.g &&
           attachment1.clearValue.b == attachment2.clearValue.b &&
           attachment1.clearValue.a == attachment2.clearValue.a;
}

bool CompareColorRenderPassAttachmentVectors(
    const std::vector<wgpu::RenderPassColorAttachment> &attachments1,
    const std::vector<wgpu::RenderPassColorAttachment> &attachments2)
{
    if (attachments1.size() != attachments2.size())
    {
        return false;
    }

    for (uint32_t i = 0; i < attachments1.size(); ++i)
    {
        if (!CompareColorRenderPassAttachments(attachments1[i], attachments2[i]))
        {
            return false;
        }
    }

    return true;
}

bool CompareDepthStencilRenderPassAttachments(
    const wgpu::RenderPassDepthStencilAttachment &attachment1,
    const wgpu::RenderPassDepthStencilAttachment &attachment2)
{
    return attachment1.view.Get() == attachment2.view.Get() &&
           attachment1.depthLoadOp == attachment2.depthLoadOp &&
           attachment1.depthStoreOp == attachment2.depthStoreOp &&
           attachment1.depthClearValue == attachment2.depthClearValue &&
           attachment1.stencilLoadOp == attachment2.stencilLoadOp &&
           attachment1.stencilStoreOp == attachment2.stencilStoreOp &&
           attachment1.stencilClearValue == attachment2.stencilClearValue &&
           attachment1.stencilReadOnly == attachment2.stencilReadOnly;
}
}  // namespace

FramebufferWgpu::FramebufferWgpu(const gl::FramebufferState &state) : FramebufferImpl(state)
{
    mCurrentColorAttachmentFormats.fill(wgpu::TextureFormat::Undefined);
}

FramebufferWgpu::~FramebufferWgpu() {}

angle::Result FramebufferWgpu::discard(const gl::Context *context,
                                       size_t count,
                                       const GLenum *attachments)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::invalidate(const gl::Context *context,
                                          size_t count,
                                          const GLenum *attachments)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::invalidateSub(const gl::Context *context,
                                             size_t count,
                                             const GLenum *attachments,
                                             const gl::Rectangle &area)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::clear(const gl::Context *context, GLbitfield mask)
{
    bool clearColor   = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_COLOR_BUFFER_BIT));
    bool clearDepth   = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_DEPTH_BUFFER_BIT));
    bool clearStencil = IsMaskFlagSet(mask, static_cast<GLbitfield>(GL_STENCIL_BUFFER_BIT));

    ASSERT(clearDepth || clearStencil || clearColor);

    ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);

    gl::ColorF colorClearValue           = context->getState().getColorClearValue();
    gl::DrawBufferMask clearColorBuffers = mState.getEnabledDrawBuffers();
    wgpu::Color clearValue;
    clearValue.r = colorClearValue.red;
    clearValue.g = colorClearValue.green;
    clearValue.b = colorClearValue.blue;
    clearValue.a = colorClearValue.alpha;
    std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
    wgpu::RenderPassDepthStencilAttachment depthStencilAttachment;
    float depthValue      = 1;
    uint32_t stencilValue = 0;
    for (size_t enabledDrawBuffer : clearColorBuffers)
    {
        colorAttachments.push_back(webgpu::CreateNewClearColorAttachment(
            clearValue, wgpu::kDepthSliceUndefined,
            mRenderTargetCache.getColorDraw(mState, enabledDrawBuffer)->getTextureView()));
    }

    // Attempt to end a render pass if one has already been started.
    bool isActiveRenderPass =
        !CompareColorRenderPassAttachmentVectors(mCurrentColorAttachments, colorAttachments) ||
        contextWgpu->hasActiveRenderPass();

    if (clearDepth || clearStencil)
    {

        depthValue             = context->getState().getDepthClearValue();
        stencilValue           = static_cast<uint32_t>(context->getState().getStencilClearValue());
        depthStencilAttachment = webgpu::CreateNewDepthStencilAttachment(
            depthValue, stencilValue, mRenderTargetCache.getDepthStencil()->getTextureView(),
            clearDepth, clearStencil);

        isActiveRenderPass =
            isActiveRenderPass || !CompareDepthStencilRenderPassAttachments(
                                      depthStencilAttachment, mCurrentDepthStencilAttachment);
    }

    if (mDeferredClears.any())
    {
        // Merge the current clear command with any deferred clears. This is to keep the clear paths
        // simpler so they only need to consider the current or the deferred clears.
        mergeClearWithDeferredClears(clearValue, clearColorBuffers, depthValue, stencilValue,
                                     clearColor, clearDepth, clearStencil);
        if (isActiveRenderPass)
        {
            ANGLE_TRY(flushDeferredClears(contextWgpu));
        }
        else
        {
            for (size_t colorIndexGL : mDeferredClears.getColorMask())
            {
                RenderTargetWgpu *renderTarget =
                    mRenderTargetCache.getColorDraw(mState, colorIndexGL);
                webgpu::ClearValues deferredClearValue;
                deferredClearValue = mDeferredClears[colorIndexGL];
                if (mDeferredClears.hasDepth())
                {
                    deferredClearValue.depthValue = mDeferredClears.getDepthValue();
                }
                if (mDeferredClears.hasStencil())
                {
                    deferredClearValue.stencilValue = mDeferredClears.getStencilValue();
                }
                renderTarget->getImage()->stageClear(
                    renderTarget->getImage()->toGlLevel(renderTarget->getLevelIndex()),
                    deferredClearValue, false, false);
            }
            if (mDeferredClears.hasDepth() || mDeferredClears.hasStencil())
            {
                webgpu::ClearValues dsClearValue = {};
                dsClearValue.depthValue          = mDeferredClears.getDepthValue();
                dsClearValue.stencilValue        = mDeferredClears.getStencilValue();
                RenderTargetWgpu *renderTarget   = mRenderTargetCache.getDepthStencil();
                renderTarget->getImage()->stageClear(
                    renderTarget->getImage()->toGlLevel(renderTarget->getLevelIndex()),
                    dsClearValue, mDeferredClears.hasDepth(), mDeferredClears.hasStencil());
            }
            mDeferredClears.reset();
        }
        return angle::Result::Continue;
    }

    if (isActiveRenderPass)
    {
        ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
    }

    setUpForRenderPass(contextWgpu, (clearDepth || clearStencil), std::move(colorAttachments),
                       depthStencilAttachment);
    ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));
    ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::clearBufferfv(const gl::Context *context,
                                             GLenum buffer,
                                             GLint drawbuffer,
                                             const GLfloat *values)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::clearBufferuiv(const gl::Context *context,
                                              GLenum buffer,
                                              GLint drawbuffer,
                                              const GLuint *values)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::clearBufferiv(const gl::Context *context,
                                             GLenum buffer,
                                             GLint drawbuffer,
                                             const GLint *values)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::clearBufferfi(const gl::Context *context,
                                             GLenum buffer,
                                             GLint drawbuffer,
                                             GLfloat depth,
                                             GLint stencil)
{
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::readPixels(const gl::Context *context,
                                          const gl::Rectangle &origArea,
                                          GLenum format,
                                          GLenum type,
                                          const gl::PixelPackState &pack,
                                          gl::Buffer *packBuffer,
                                          void *ptrOrOffset)
{
    // Get the pointer to write to from the argument or the pack buffer
    GLubyte *pixels = nullptr;
    if (packBuffer != nullptr)
    {
        UNREACHABLE();
    }
    else
    {
        pixels = reinterpret_cast<GLubyte *>(ptrOrOffset);
    }

    // Clip read area to framebuffer.
    const gl::Extents fbSize = getState().getReadPixelsAttachment(format)->getSize();
    const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
    gl::Rectangle clippedArea;
    if (!ClipRectangle(origArea, fbRect, &clippedArea))
    {
        // nothing to read
        return angle::Result::Continue;
    }

    ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);

    ANGLE_TRY(flushDeferredClears(contextWgpu));

    ANGLE_TRY(contextWgpu->flush(webgpu::RenderPassClosureReason::GLReadPixels));

    GLuint outputSkipBytes = 0;
    PackPixelsParams params;
    ANGLE_TRY(webgpu::ImageHelper::getReadPixelsParams(contextWgpu, pack, packBuffer, format, type,
                                                       origArea, clippedArea, &params,
                                                       &outputSkipBytes));

    webgpu::ImageHelper *sourceImageHelper = getReadPixelsRenderTarget()->getImage();
    ANGLE_TRY(sourceImageHelper->readPixels(contextWgpu, params.area, params,
                                            static_cast<uint8_t *>(pixels) + outputSkipBytes));

    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::blit(const gl::Context *context,
                                    const gl::Rectangle &sourceArea,
                                    const gl::Rectangle &destArea,
                                    GLbitfield mask,
                                    GLenum filter)
{
    return angle::Result::Continue;
}

gl::FramebufferStatus FramebufferWgpu::checkStatus(const gl::Context *context) const
{
    return gl::FramebufferStatus::Complete();
}

angle::Result FramebufferWgpu::syncState(const gl::Context *context,
                                         GLenum binding,
                                         const gl::Framebuffer::DirtyBits &dirtyBits,
                                         gl::Command command)
{
    ContextWgpu *contextWgpu = webgpu::GetImpl(context);
    bool dirtyDepthStencilAttachment = false;
    ASSERT(dirtyBits.any());

    gl::DrawBufferMask dirtyColorAttachments;
    for (size_t dirtyBit : dirtyBits)
    {
        switch (dirtyBit)
        {
            case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
            case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS:
            case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
            case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS:
            {
                ANGLE_TRY(mRenderTargetCache.updateDepthStencilRenderTarget(context, mState));
                dirtyDepthStencilAttachment = true;
                // Update the current depth stencil texture format let the context know if this
                // framebuffer is bound for draw
                RenderTargetWgpu *rt       = mRenderTargetCache.getDepthStencil();
                mCurrentDepthStencilFormat = (rt && rt->getImage())
                                                 ? rt->getImage()->toWgpuTextureFormat()
                                                 : wgpu::TextureFormat::Undefined;
                if (binding == GL_DRAW_FRAMEBUFFER)
                {
                    contextWgpu->setDepthStencilFormat(mCurrentDepthStencilFormat);
                }

                break;
            }

            case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
                ANGLE_TRY(mRenderTargetCache.update(context, mState, dirtyBits));
                break;
            case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
            case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
            case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
            case gl::Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
            case gl::Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
            case gl::Framebuffer::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE:
            case gl::Framebuffer::DIRTY_BIT_DEFAULT_LAYERS:
            case gl::Framebuffer::DIRTY_BIT_FOVEATION:
                break;
            default:
            {
                static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
                uint32_t colorIndexGL;
                if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
                {
                    colorIndexGL = static_cast<uint32_t>(
                        dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
                }
                else
                {
                    ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 &&
                           dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX);
                    colorIndexGL = static_cast<uint32_t>(
                        dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0);
                }

                ANGLE_TRY(
                    mRenderTargetCache.updateColorRenderTarget(context, mState, colorIndexGL));

                // Update the current color texture formats let the context know if this framebuffer
                // is bound for draw
                RenderTargetWgpu *rt = mRenderTargetCache.getColorDraw(mState, colorIndexGL);
                mCurrentColorAttachmentFormats[colorIndexGL] =
                    (rt && rt->getImage()) ? rt->getImage()->toWgpuTextureFormat()
                                           : wgpu::TextureFormat::Undefined;
                if (binding == GL_DRAW_FRAMEBUFFER)
                {
                    contextWgpu->setColorAttachmentFormat(
                        colorIndexGL, mCurrentColorAttachmentFormats[colorIndexGL]);
                }

                dirtyColorAttachments.set(colorIndexGL);
                break;
            }
        }
    }

    // Like in Vulkan, defer clears for draw framebuffer ops as well as clears to read framebuffer
    // attachments that are not taking part in a blit operation.
    const bool isBlitCommand = command >= gl::Command::Blit && command <= gl::Command::BlitAll;
    bool deferColorClears    = binding == GL_DRAW_FRAMEBUFFER;
    bool deferDepthStencilClears = binding == GL_DRAW_FRAMEBUFFER;
    if (binding == GL_READ_FRAMEBUFFER && isBlitCommand)
    {
        uint32_t blitMask =
            static_cast<uint32_t>(command) - static_cast<uint32_t>(gl::Command::Blit);
        if ((blitMask & gl::CommandBlitBufferColor) == 0)
        {
            deferColorClears = true;
        }
        if ((blitMask & (gl::CommandBlitBufferDepth | gl::CommandBlitBufferStencil)) == 0)
        {
            deferDepthStencilClears = true;
        }
    }

    ANGLE_TRY(flushAttachmentUpdates(context, dirtyColorAttachments, dirtyDepthStencilAttachment,
                                     deferColorClears, deferDepthStencilClears));

    // Notify the ContextWgpu to update the pipeline desc or restart the renderpass
    ANGLE_TRY(contextWgpu->onFramebufferChange(this, command));

    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::getSamplePosition(const gl::Context *context,
                                                 size_t index,
                                                 GLfloat *xy) const
{
    return angle::Result::Continue;
}

RenderTargetWgpu *FramebufferWgpu::getReadPixelsRenderTarget() const
{
    return mRenderTargetCache.getColorRead(mState);
}

void FramebufferWgpu::addNewColorAttachments(
    std::vector<wgpu::RenderPassColorAttachment> newColorAttachments)
{
    mNewColorAttachments.insert(mCurrentColorAttachments.end(), newColorAttachments.begin(),
                                newColorAttachments.end());
}

void FramebufferWgpu::updateDepthStencilAttachment(
    wgpu::RenderPassDepthStencilAttachment newRenderPassDepthStencilAttachment)
{
    mNewDepthStencilAttachment      = std::move(newRenderPassDepthStencilAttachment);
    mAddedNewDepthStencilAttachment = true;
}

angle::Result FramebufferWgpu::flushOneColorAttachmentUpdate(const gl::Context *context,
                                                             bool deferClears,
                                                             uint32_t colorIndexGL)
{
    ContextWgpu *contextWgpu           = GetImplAs<ContextWgpu>(context);
    RenderTargetWgpu *drawRenderTarget = nullptr;
    RenderTargetWgpu *readRenderTarget = nullptr;

    drawRenderTarget = mRenderTargetCache.getColorDraw(mState, colorIndexGL);
    if (drawRenderTarget)
    {
        if (deferClears)
        {
            ANGLE_TRY(drawRenderTarget->flushImageStagedUpdates(contextWgpu, &mDeferredClears,
                                                                colorIndexGL));
        }
        else
        {
            ANGLE_TRY(drawRenderTarget->flushImageStagedUpdates(contextWgpu, nullptr, 0));
        }
    }

    if (mState.getReadBufferState() != GL_NONE && mState.getReadIndex() == colorIndexGL)
    {
        readRenderTarget = mRenderTargetCache.getColorRead(mState);
        if (readRenderTarget && readRenderTarget != drawRenderTarget)
        {
            ANGLE_TRY(readRenderTarget->flushImageStagedUpdates(contextWgpu, nullptr, 0));
        }
    }

    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::flushAttachmentUpdates(const gl::Context *context,
                                                      gl::DrawBufferMask dirtyColorAttachments,
                                                      bool dirtyDepthStencilAttachment,
                                                      bool deferColorClears,
                                                      bool deferDepthStencilClears)
{
    for (size_t colorIndexGL : dirtyColorAttachments)
    {
        ANGLE_TRY(flushOneColorAttachmentUpdate(context, deferColorClears,
                                                static_cast<uint32_t>(colorIndexGL)));
    }

    ContextWgpu *contextWgpu         = GetImplAs<ContextWgpu>(context);
    RenderTargetWgpu *depthStencilRt = mRenderTargetCache.getDepthStencil();

    if (depthStencilRt && dirtyDepthStencilAttachment)
    {
        if (deferDepthStencilClears)
        {
            // The underlying ImageHelper will check if a clear has a stencil value and store the
            // deferred clear in the correct index.
            ANGLE_TRY(depthStencilRt->flushImageStagedUpdates(contextWgpu, &mDeferredClears,
                                                              webgpu::kUnpackedDepthIndex));
        }
        else
        {
            ANGLE_TRY(depthStencilRt->flushImageStagedUpdates(contextWgpu, nullptr, 0));
        }
    }

    // If we added any new attachments, we start a render pass to fully flush the updates.
    if ((!mNewColorAttachments.empty() &&
         mNewColorAttachments.size() != mCurrentColorAttachments.size()) ||
        mAddedNewDepthStencilAttachment)
    {
        ANGLE_TRY(startRenderPassNewAttachments(contextWgpu));
    }
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::flushDeferredClears(ContextWgpu *contextWgpu)
{
    if (mDeferredClears.empty())
    {
        return angle::Result::Continue;
    }
    ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));
    mCurrentColorAttachments.clear();
    for (size_t colorIndexGL : mState.getColorAttachmentsMask())
    {
        if (!mDeferredClears.test(colorIndexGL))
        {
            continue;
        }
        mCurrentColorAttachments.push_back(webgpu::CreateNewClearColorAttachment(
            mDeferredClears[colorIndexGL].clearColor, mDeferredClears[colorIndexGL].depthSlice,
            mRenderTargetCache.getColorDraw(mState, colorIndexGL)->getTextureView()));
    }
    if (mRenderTargetCache.getDepthStencil() &&
        (mDeferredClears.hasDepth() || mDeferredClears.hasStencil()))
    {
        mCurrentDepthStencilAttachment = webgpu::CreateNewDepthStencilAttachment(
            mDeferredClears.getDepthValue(), mDeferredClears.getStencilValue(),
            mRenderTargetCache.getDepthStencil()->getTextureView(), !mDeferredClears.hasDepth(),
            !mDeferredClears.hasStencil());
        mCurrentRenderPassDesc.depthStencilAttachment = &mCurrentDepthStencilAttachment;
    }
    else
    {
        mCurrentRenderPassDesc.depthStencilAttachment = nullptr;
    }
    mCurrentRenderPassDesc.colorAttachmentCount = mCurrentColorAttachments.size();
    mCurrentRenderPassDesc.colorAttachments     = mCurrentColorAttachments.data();
    ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));
    ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));

    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::startRenderPassNewAttachments(ContextWgpu *contextWgpu)
{
    // Flush out a render pass if there is an active one.
    ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));

    setUpForRenderPass(contextWgpu, mAddedNewDepthStencilAttachment, mNewColorAttachments,
                       mNewDepthStencilAttachment);
    mNewColorAttachments.clear();
    mAddedNewDepthStencilAttachment = false;
    ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));
    return angle::Result::Continue;
}

angle::Result FramebufferWgpu::startNewRenderPass(ContextWgpu *contextWgpu)
{
    ANGLE_TRY(contextWgpu->endRenderPass(webgpu::RenderPassClosureReason::NewRenderPass));

    mCurrentColorAttachments.clear();
    for (size_t colorIndexGL : mState.getColorAttachmentsMask())
    {
        wgpu::RenderPassColorAttachment colorAttachment;
        colorAttachment.view =
            mRenderTargetCache.getColorDraw(mState, colorIndexGL)->getTextureView();
        colorAttachment.depthSlice = wgpu::kDepthSliceUndefined;
        colorAttachment.loadOp     = wgpu::LoadOp::Load;
        colorAttachment.storeOp    = wgpu::StoreOp::Store;

        mCurrentColorAttachments.push_back(colorAttachment);
    }
    if (mRenderTargetCache.getDepthStencil())
    {
        mCurrentDepthStencilAttachment = webgpu::CreateNewDepthStencilAttachment(
            contextWgpu->getState().getDepthClearValue(),
            static_cast<uint32_t>(contextWgpu->getState().getStencilClearValue()),
            mRenderTargetCache.getDepthStencil()->getTextureView(), mState.hasDepth(),
            mState.hasStencil());
        mCurrentRenderPassDesc.depthStencilAttachment = &mCurrentDepthStencilAttachment;
    }
    else
    {
        mCurrentRenderPassDesc.depthStencilAttachment = nullptr;
    }

    mCurrentRenderPassDesc.colorAttachmentCount = mCurrentColorAttachments.size();
    mCurrentRenderPassDesc.colorAttachments     = mCurrentColorAttachments.data();
    ANGLE_TRY(contextWgpu->startRenderPass(mCurrentRenderPassDesc));

    return angle::Result::Continue;
}

void FramebufferWgpu::setUpForRenderPass(
    ContextWgpu *contextWgpu,
    bool depthOrStencil,
    std::vector<wgpu::RenderPassColorAttachment> colorAttachments,
    wgpu::RenderPassDepthStencilAttachment depthStencilAttachment)
{
    if (depthOrStencil)
    {
        mCurrentDepthStencilAttachment                = std::move(depthStencilAttachment);
        mCurrentRenderPassDesc.depthStencilAttachment = &mCurrentDepthStencilAttachment;
    }
    else
    {
        mCurrentRenderPassDesc.depthStencilAttachment = nullptr;
    }
    mCurrentColorAttachments                    = std::move(colorAttachments);
    mCurrentRenderPassDesc.colorAttachmentCount = mCurrentColorAttachments.size();
    mCurrentRenderPassDesc.colorAttachments     = mCurrentColorAttachments.data();
}

void FramebufferWgpu::mergeClearWithDeferredClears(wgpu::Color clearValue,
                                                   gl::DrawBufferMask clearColorBuffers,
                                                   float depthValue,
                                                   uint32_t stencilValue,
                                                   bool clearColor,
                                                   bool clearDepth,
                                                   bool clearStencil)
{
    for (size_t enabledDrawBuffer : clearColorBuffers)
    {
        mDeferredClears.store(static_cast<uint32_t>(enabledDrawBuffer),
                              {clearValue, wgpu::kDepthSliceUndefined, 0, 0});
    }
    if (clearDepth)
    {
        mDeferredClears.store(webgpu::kUnpackedDepthIndex,
                              {clearValue, wgpu::kDepthSliceUndefined, depthValue, 0});
    }
    if (clearStencil)
    {
        mDeferredClears.store(webgpu::kUnpackedStencilIndex,
                              {clearValue, wgpu::kDepthSliceUndefined, 0, stencilValue});
    }
}

}  // namespace rx
