// Copyright 2022 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "src/dawn/native/ApplyClearColorValueWithDrawHelper.h"

#include <limits>
#include <string>
#include <utility>

#include "src/dawn/common/Enumerator.h"
#include "src/dawn/common/Range.h"
#include "src/dawn/common/Strings.h"
#include "src/dawn/native/BindGroup.h"
#include "src/dawn/native/BindGroupLayout.h"
#include "src/dawn/native/Buffer.h"
#include "src/dawn/native/CommandEncoder.h"
#include "src/dawn/native/Device.h"
#include "src/dawn/native/InternalPipelineStore.h"
#include "src/dawn/native/ObjectContentHasher.h"
#include "src/dawn/native/RenderPassEncoder.h"
#include "src/dawn/native/RenderPipeline.h"
#include "src/dawn/native/utils/WGPUHelpers.h"
#include "src/dawn/native/webgpu_absl_format.h"
#include "src/utils/compiler.h"

namespace dawn::native {

namespace {

// General helper functions and data structures for applying clear values with draw
static const char kVSSource[] = DAWN_MULTILINE(
    @vertex
    fn main(@builtin(vertex_index) vertexIndex : u32) -> @builtin(position) vec4f {
        var pos = array(
            vec2f(-1.0, -1.0),
            vec2f( 3.0, -1.0),
            vec2f(-1.0,  3.0));
        return vec4f(pos[vertexIndex], 0.0, 1.0);
    }
);

const char* GetTextureComponentTypeString(DeviceBase* device, wgpu::TextureFormat format) {
    DAWN_CHECK(format != wgpu::TextureFormat::Undefined);

    const Format& formatInfo = device->GetValidInternalFormat(format);
    switch (formatInfo.GetAspectInfo(Aspect::Color).baseType) {
        case TextureComponentType::Sint:
            return "i32";
        case TextureComponentType::Uint:
            return "u32";
        case TextureComponentType::Float:
            return "f32";
    }
    DAWN_UNREACHABLE();
}

// Construct the fragment shader to apply the input color values to the corresponding color
// attachments of KeyOfApplyClearColorValueWithDrawPipelines.
// The fragment shader will look like:
//
// enable chromium_experimental_pixel_local;
// struct PLS {
//     a0 : u32,
//     a1 : u32,
//     a2 : u32,
//     a3 : u32,
// }
//
// var<pixel_local> pls : PLS;
// struct OutputColor {
//     @location(0) output0 : vec4<f32>,
// }
// struct ClearColors {
//     color0 : vec4<f32>,
// }
//
// @group(0) @binding(0) var<uniform> clearColors : ClearColors;
//
// @fragment
// fn main() -> OutputColor {
//     var outputColor : OutputColor;
//     outputColor.output0 = clearColors.color0;
//     _ = pls.a0;
//
//     return outputColor;
// }
std::string ConstructFragmentShader(DeviceBase* device,
                                    const KeyOfApplyClearColorValueWithDrawPipelines& key) {
    std::ostringstream outputColorDeclarationStream;
    std::ostringstream clearValueUniformBufferDeclarationStream;
    std::ostringstream assignOutputColorStream;

    outputColorDeclarationStream << "struct OutputColor {\n";
    clearValueUniformBufferDeclarationStream << "struct ClearColors {\n";

    // Only generate the assignments we need.
    for (auto i : key.colorTargetsToApplyClearColorValue) {
        wgpu::TextureFormat currentFormat = key.colorTargetFormats[i];
        DAWN_CHECK(currentFormat != wgpu::TextureFormat::Undefined);

        const char* type = GetTextureComponentTypeString(device, currentFormat);

        outputColorDeclarationStream
            << absl::StrFormat("    @location(%u) output%u : vec4<%s>,\n", i, i, type);
        clearValueUniformBufferDeclarationStream
            << absl::StrFormat("    color%u : vec4<%s>,\n", i, type);
        assignOutputColorStream << absl::StrFormat(
            "    outputColor.output%u = clearColors.color%u;\n", i, i);
    }
    outputColorDeclarationStream << "}\n";
    clearValueUniformBufferDeclarationStream << "}\n";

    std::ostringstream fragmentShaderStream;
    if (key.hasPLS) {
        std::vector<const char*> plsTypes;
        const size_t plsSlotCount = key.totalPixelLocalStorageSize / kPLSSlotByteSize;
        plsTypes.resize(plsSlotCount, "u32");
        for (const auto& attachment : key.plsAttachments) {
            switch (attachment.format) {
                case wgpu::TextureFormat::R32Uint:
                    plsTypes[attachment.offset / kPLSSlotByteSize] = "u32";
                    break;
                case wgpu::TextureFormat::R32Sint:
                    plsTypes[attachment.offset / kPLSSlotByteSize] = "i32";
                    break;
                case wgpu::TextureFormat::R32Float:
                    plsTypes[attachment.offset / kPLSSlotByteSize] = "f32";
                    break;
                default:
                    DAWN_UNREACHABLE();
            }
        }

        fragmentShaderStream << "enable chromium_experimental_pixel_local;";
        std::ostringstream plsDeclarationStream;
        plsDeclarationStream << "struct PLS {\n";
        for (size_t i = 0; i < plsSlotCount; ++i) {
            plsDeclarationStream << absl::StrFormat("    a%u : %s,\n", i, plsTypes[i]);
        }
        plsDeclarationStream << "}\n";
        if (plsSlotCount) {
            // Read the PLS with a phony-assignment to mark the PLS as statically used for
            // compatibility with the render pass.
            assignOutputColorStream << "_ = pls.a0;";
        }

        fragmentShaderStream << plsDeclarationStream.str() << "var<pixel_local> pls : PLS;";
    }

    fragmentShaderStream << outputColorDeclarationStream.str()
                         << clearValueUniformBufferDeclarationStream.str();
    fragmentShaderStream << DAWN_MULTILINE(
        @group(0) @binding(0) var<uniform> clearColors : ClearColors;

        @fragment
        fn main() -> OutputColor {
            var outputColor : OutputColor;
    );
    fragmentShaderStream << assignOutputColorStream.str();
    fragmentShaderStream << DAWN_MULTILINE(
            return outputColor;
        }
    );
    return fragmentShaderStream.str();
}

RenderPipelineBase* GetCachedPipeline(InternalPipelineStore* store,
                                      const KeyOfApplyClearColorValueWithDrawPipelines& key) {
    auto iter = store->applyClearColorValueWithDrawPipelines.find(key);
    if (iter != store->applyClearColorValueWithDrawPipelines.end()) {
        return iter->second.Get();
    }
    return nullptr;
}

ResultOrError<RenderPipelineBase*> GetOrCreateApplyClearValueWithDrawPipeline(
    DeviceBase* device,
    const KeyOfApplyClearColorValueWithDrawPipelines& key) {
    InternalPipelineStore* store = device->GetInternalPipelineStore();
    RenderPipelineBase* cachedPipeline = GetCachedPipeline(store, key);
    if (cachedPipeline != nullptr) {
        return cachedPipeline;
    }

    // Prepare the vertex stage
    Ref<ShaderModuleBase> vertexModule;
    DAWN_TRY_ASSIGN(vertexModule, utils::CreateShaderModule(device, kVSSource));
    VertexState vertex = {};
    vertex.module = vertexModule.Get();
    vertex.entryPoint = "main";

    // Prepare the fragment stage
    std::string fragmentShader = ConstructFragmentShader(device, key);
    Ref<ShaderModuleBase> fragmentModule;
    DAWN_TRY_ASSIGN(fragmentModule, utils::CreateShaderModule(device, fragmentShader.c_str()));
    FragmentState fragment = {};
    fragment.module = fragmentModule.Get();
    fragment.entryPoint = "main";

    // Prepare the color states
    PerColorAttachment<ColorTargetState> colorTargets = {};
    for (auto [i, target] : Enumerate(colorTargets)) {
        target.format = key.colorTargetFormats[i];
        // We shouldn't change the color targets that are not involved in.
        if (!key.colorTargetsToApplyClearColorValue[i]) {
            target.writeMask = wgpu::ColorWriteMask::None;
        }
    }

    // Create RenderPipeline
    RenderPipelineDescriptor renderPipelineDesc = {};

    renderPipelineDesc.vertex = vertex;
    renderPipelineDesc.fragment = &fragment;
    renderPipelineDesc.multisample.count = key.sampleCount;
    DepthStencilState depthStencilState = {};
    depthStencilState.depthWriteEnabled = false;
    if (key.depthStencilFormat != wgpu::TextureFormat::Undefined) {
        depthStencilState.format = key.depthStencilFormat;
        renderPipelineDesc.depthStencil = &depthStencilState;
    }
    fragment.targetCount = key.colorAttachmentCount;
    fragment.targets = colorTargets.data();

    // Build the pipeline layout explicitly as we might need to add PLS information to it.
    Ref<BindGroupLayoutBase> bgl;
    DAWN_TRY_ASSIGN(bgl, utils::MakeBindGroupLayout(device, {{0, wgpu::ShaderStage::Fragment,
                                                              wgpu::BufferBindingType::Uniform}}));

    PipelineLayoutDescriptor pipelineLayoutDesc{};
    pipelineLayoutDesc.bindGroupLayoutCount = 1;
    pipelineLayoutDesc.bindGroupLayouts = &bgl.Get();

    wgpu::PipelineLayoutPixelLocalStorage pls;
    if (key.hasPLS) {
        pls.totalPixelLocalStorageSize = key.totalPixelLocalStorageSize;
        pls.storageAttachmentCount = key.plsAttachments.size();
        pls.storageAttachments = key.plsAttachments.data();

        pipelineLayoutDesc.nextInChain = &pls;
    }

    Ref<PipelineLayoutBase> pipelineLayout;
    DAWN_TRY_ASSIGN(pipelineLayout, device->CreatePipelineLayout(&pipelineLayoutDesc));
    renderPipelineDesc.layout = pipelineLayout.Get();
    Ref<RenderPipelineBase> pipeline;
    DAWN_TRY_ASSIGN(pipeline, device->CreateRenderPipeline(&renderPipelineDesc));
    store->applyClearColorValueWithDrawPipelines.emplace(key, std::move(pipeline));

    return GetCachedPipeline(store, key);
}

ResultOrError<Ref<BufferBase>> CreateUniformBufferWithClearValues(
    CommandEncoder* encoder,
    const RenderPassDescriptor* renderPassDescriptor,
    const KeyOfApplyClearColorValueWithDrawPipelines& key) {
    auto colorAttachments = ityp::SpanFromUntyped<ColorAttachmentIndex>(
        renderPassDescriptor->colorAttachments, renderPassDescriptor->colorAttachmentCount);

    std::array<uint8_t, sizeof(uint32_t) * 4 * kMaxColorAttachments> clearValues = {};
    uint32_t offset = 0;
    for (auto i : key.colorTargetsToApplyClearColorValue) {
        const Format& format = colorAttachments[i].view->GetFormat();
        TextureComponentType baseType = format.GetAspectInfo(Aspect::Color).baseType;

        Color initialClearValue = colorAttachments[i].clearValue;
        Color clearValue = ClampClearColorValueToLegalRange(initialClearValue, format);
        switch (baseType) {
            case TextureComponentType::Uint: {
                uint32_t* clearValuePtr =
                    reinterpret_cast<uint32_t*>(DAWN_UNSAFE_TODO(clearValues.data() + offset));
                clearValuePtr[0] = static_cast<uint32_t>(clearValue.r);
                DAWN_UNSAFE_TODO(clearValuePtr[1]) = static_cast<uint32_t>(clearValue.g);
                DAWN_UNSAFE_TODO(clearValuePtr[2]) = static_cast<uint32_t>(clearValue.b);
                DAWN_UNSAFE_TODO(clearValuePtr[3]) = static_cast<uint32_t>(clearValue.a);
                break;
            }
            case TextureComponentType::Sint: {
                int32_t* clearValuePtr =
                    reinterpret_cast<int32_t*>(DAWN_UNSAFE_TODO(clearValues.data() + offset));
                clearValuePtr[0] = static_cast<int32_t>(clearValue.r);
                DAWN_UNSAFE_TODO(clearValuePtr[1]) = static_cast<int32_t>(clearValue.g);
                DAWN_UNSAFE_TODO(clearValuePtr[2]) = static_cast<int32_t>(clearValue.b);
                DAWN_UNSAFE_TODO(clearValuePtr[3]) = static_cast<int32_t>(clearValue.a);
                break;
            }
            case TextureComponentType::Float: {
                float* clearValuePtr =
                    reinterpret_cast<float*>(DAWN_UNSAFE_TODO(clearValues.data() + offset));
                clearValuePtr[0] = static_cast<float>(clearValue.r);
                DAWN_UNSAFE_TODO(clearValuePtr[1]) = static_cast<float>(clearValue.g);
                DAWN_UNSAFE_TODO(clearValuePtr[2]) = static_cast<float>(clearValue.b);
                DAWN_UNSAFE_TODO(clearValuePtr[3]) = static_cast<float>(clearValue.a);
                break;
            }
        }
        offset += sizeof(uint32_t) * 4;
    }

    DAWN_CHECK(offset > 0);

    Ref<BufferBase> buffer;
    DAWN_TRY_ASSIGN(buffer, encoder->GetDevice()->GetOrCreateTemporaryUniformBuffer(offset));
    buffer->SetLabel("Internal_UniformClearValues");
    encoder->APIWriteBuffer(buffer.Get(), 0, clearValues.data(), offset);

    return std::move(buffer);
}

bool NeedsBigIntClear(const Format& format, const std::array<double, 4>& clearValue) {
    // We should only apply this workaround on 32-bit signed and unsigned integer formats.
    switch (format.format) {
        case wgpu::TextureFormat::R32Sint:
        case wgpu::TextureFormat::RG32Sint:
        case wgpu::TextureFormat::RGBA32Sint:
        case wgpu::TextureFormat::R32Uint:
        case wgpu::TextureFormat::RG32Uint:
        case wgpu::TextureFormat::RGBA32Uint:
            break;
        default:
            return false;
    }

    for (uint8_t i = 0; i < format.componentCount; ++i) {
        double value = clearValue[i];
        switch (format.GetAspectInfo(Aspect::Color).baseType) {
            case TextureComponentType::Uint: {
                constexpr double kMaxUintRepresentableInFloat =
                    1 << std::numeric_limits<float>::digits;
                if (value > kMaxUintRepresentableInFloat) {
                    return true;
                }
                break;
            }
            case TextureComponentType::Sint: {
                constexpr double kMaxSintRepresentableInFloat =
                    1 << std::numeric_limits<float>::digits;
                constexpr double kMinSintRepresentableInFloat = -kMaxSintRepresentableInFloat;
                if (value > kMaxSintRepresentableInFloat || value < kMinSintRepresentableInFloat) {
                    return true;
                }
                break;
            }
            case TextureComponentType::Float:
                DAWN_UNREACHABLE();
                return false;
        }
    }

    return false;
}

bool ShouldApplyClearColorWithDraw(const DeviceBase* device,
                                   const TextureViewBase* view,
                                   wgpu::LoadOp loadOp,
                                   const std::array<double, 4>& clearValue) {
    bool clearWithDraw = device->IsToggleEnabled(Toggle::ClearColorWithDraw);
    bool clearWithDrawForBigInt =
        device->IsToggleEnabled(Toggle::ApplyClearBigIntegerColorValueWithDraw);

    if (!clearWithDraw && !clearWithDrawForBigInt) {
        return false;
    }
    if (view == nullptr) {
        return false;
    }
    if (loadOp != wgpu::LoadOp::Clear) {
        return false;
    }
    if (!clearWithDraw && clearWithDrawForBigInt) {
        return NeedsBigIntClear(view->GetFormat(), clearValue);
    }

    return true;
}

bool GetKeyOfApplyClearColorValueWithDrawPipelines(
    const DeviceBase* device,
    const UnpackedPtr<RenderPassDescriptor>& renderPassDescriptor,
    KeyOfApplyClearColorValueWithDrawPipelines* key) {
    bool clearWithDraw = device->IsToggleEnabled(Toggle::ClearColorWithDraw);
    bool clearWithDrawForBigInt =
        device->IsToggleEnabled(Toggle::ApplyClearBigIntegerColorValueWithDraw);

    if (!clearWithDraw && !clearWithDrawForBigInt) {
        return false;
    }

    key->colorAttachmentCount = static_cast<uint8_t>(renderPassDescriptor->colorAttachmentCount);

    auto colorAttachments = ityp::SpanFromUntyped<ColorAttachmentIndex>(
        renderPassDescriptor->colorAttachments, key->colorAttachmentCount);

    key->colorTargetFormats.fill(wgpu::TextureFormat::Undefined);
    for (auto [i, attachment] : Enumerate(colorAttachments)) {
        if (attachment.view == nullptr) {
            continue;
        }

        key->colorTargetFormats[i] = attachment.view->GetFormat().format;
        if (key->sampleCount == 0) {
            key->sampleCount = attachment.view->GetTexture()->GetSampleCount();
        } else {
            DAWN_ASSERT(key->sampleCount == attachment.view->GetTexture()->GetSampleCount());
        }

        Color clearValue = attachment.clearValue;
        std::array<double, 4> clearValueInArray = {
            {clearValue.r, clearValue.g, clearValue.b, clearValue.a}};

        if (ShouldApplyClearColorWithDraw(device, attachment.view, attachment.loadOp,
                                          clearValueInArray)) {
            key->colorTargetsToApplyClearColorValue.set(i);
        }
    }

    if (renderPassDescriptor->depthStencilAttachment &&
        renderPassDescriptor->depthStencilAttachment->view != nullptr) {
        key->depthStencilFormat =
            renderPassDescriptor->depthStencilAttachment->view->GetFormat().format;
    }

    if (key->colorTargetsToApplyClearColorValue.none()) {
        return false;
    }

    if (const auto* pls = renderPassDescriptor.Get<RenderPassPixelLocalStorage>()) {
        key->hasPLS = true;
        key->totalPixelLocalStorageSize = pls->totalPixelLocalStorageSize;
        for (size_t i = 0; i < pls->storageAttachmentCount; ++i) {
            wgpu::PipelineLayoutStorageAttachment attachment{};
            attachment.format =
                DAWN_UNSAFE_TODO(pls->storageAttachments[i]).storage->GetFormat().format;
            attachment.offset = DAWN_UNSAFE_TODO(pls->storageAttachments[i]).offset;
            key->plsAttachments.push_back(std::move(attachment));
        }
        // Sort the PLS attachments by offset to make sure the order is deterministic.
        std::sort(
            key->plsAttachments.begin(), key->plsAttachments.end(),
            [](const wgpu::PipelineLayoutStorageAttachment& a,
               const wgpu::PipelineLayoutStorageAttachment& b) { return a.offset < b.offset; });
    }

    return true;
}

}  // namespace

// TODO(crbug.com/364834368): Move handling of workaround at command submission time instead of
// recording time.
ColorAttachmentMask ClearWithDrawHelper::GetAppliedColorAttachments(
    const DeviceBase* device,
    BeginRenderPassCmd* renderPass) {
    ColorAttachmentMask mask;
    for (auto i : renderPass->attachmentState->GetColorAttachmentsMask()) {
        const auto clearValue = renderPass->colorAttachments[i].clearColor;
        std::array<double, 4> clearValueInArray = {
            {clearValue.r, clearValue.g, clearValue.b, clearValue.a}};
        if (!ShouldApplyClearColorWithDraw(device, renderPass->colorAttachments[i].view.Get(),
                                           renderPass->colorAttachments[i].loadOp,
                                           clearValueInArray)) {
            continue;
        }
        mask.set(i);
    }

    return mask;
}

size_t KeyOfApplyClearColorValueWithDrawPipelinesHashFunc::operator()(
    KeyOfApplyClearColorValueWithDrawPipelines key) const {
    size_t hash = 0;

    HashCombine(&hash, key.colorAttachmentCount);

    HashCombine(&hash, key.colorTargetsToApplyClearColorValue);

    for (wgpu::TextureFormat format : key.colorTargetFormats) {
        HashCombine(&hash, format);
    }

    HashCombine(&hash, key.sampleCount);

    HashCombine(&hash, key.depthStencilFormat);

    HashCombine(&hash, key.hasPLS);
    HashCombine(&hash, key.totalPixelLocalStorageSize);
    for (const auto attachment : key.plsAttachments) {
        HashCombine(&hash, attachment.offset);
        HashCombine(&hash, attachment.format);
    }

    return hash;
}

bool KeyOfApplyClearColorValueWithDrawPipelinesEqualityFunc::operator()(
    KeyOfApplyClearColorValueWithDrawPipelines key1,
    KeyOfApplyClearColorValueWithDrawPipelines key2) const {
    if (key1.colorAttachmentCount != key2.colorAttachmentCount) {
        return false;
    }

    if (key1.colorTargetsToApplyClearColorValue != key2.colorTargetsToApplyClearColorValue) {
        return false;
    }

    for (auto i : Range(key1.colorTargetFormats.size())) {
        if (key1.colorTargetFormats[i] != key2.colorTargetFormats[i]) {
            return false;
        }
    }

    if (key1.hasPLS != key2.hasPLS ||
        key1.totalPixelLocalStorageSize != key2.totalPixelLocalStorageSize ||
        key1.plsAttachments.size() != key2.plsAttachments.size()) {
        return false;
    }

    for (auto i : Range(key1.plsAttachments.size())) {
        if (key1.plsAttachments[i].format != key2.plsAttachments[i].format ||
            key1.plsAttachments[i].offset != key2.plsAttachments[i].offset) {
            return false;
        }
    }

    return key1.sampleCount == key2.sampleCount &&
           key1.depthStencilFormat == key2.depthStencilFormat;
}

ClearWithDrawHelper::ClearWithDrawHelper() = default;
ClearWithDrawHelper::~ClearWithDrawHelper() = default;

MaybeError ClearWithDrawHelper::Initialize(
    CommandEncoder* encoder,
    const UnpackedPtr<RenderPassDescriptor>& renderPassDescriptor) {
    DeviceBase* device = encoder->GetDevice();
    mShouldRun = GetKeyOfApplyClearColorValueWithDrawPipelines(device, renderPassDescriptor, &mKey);
    if (!mShouldRun) {
        return {};
    }

    DAWN_TRY_ASSIGN(mUniformBufferWithClearColorValues,
                    CreateUniformBufferWithClearValues(encoder, *renderPassDescriptor, mKey));

    return {};
}

MaybeError ClearWithDrawHelper::Apply(RenderPassEncoder* renderPassEncoder) {
    if (!mShouldRun) {
        return {};
    }

    DeviceBase* device = renderPassEncoder->GetDevice();

    RenderPipelineBase* pipeline = nullptr;
    DAWN_TRY_ASSIGN(pipeline, GetOrCreateApplyClearValueWithDrawPipeline(device, mKey));

    Ref<BindGroupLayoutBase> layout;
    DAWN_TRY_ASSIGN(layout, pipeline->GetBindGroupLayout(0));

    Ref<BindGroupBase> bindGroup;
    DAWN_TRY_ASSIGN(bindGroup,
                    utils::MakeBindGroup(device, layout, {{0, mUniformBufferWithClearColorValues}},
                                         UsageValidationMode::Internal));

    renderPassEncoder->APISetBindGroup(0, bindGroup.Get());
    renderPassEncoder->APISetPipeline(pipeline);
    renderPassEncoder->APIDraw(3);

    return {};
}

}  // namespace dawn::native
