Replace BlendState builder via BlendState descriptor.

This change also removes BlendState object.

Bug=dawn:32

Change-Id: I8bf4ff7531e7504efb17b6bae3ca01f1f2b4131e
Reviewed-on: https://dawn-review.googlesource.com/c/3042
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
diff --git a/BUILD.gn b/BUILD.gn
index 885fa0b..4734942 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -363,8 +363,6 @@
     "src/dawn_native/BindGroup.h",
     "src/dawn_native/BindGroupLayout.cpp",
     "src/dawn_native/BindGroupLayout.h",
-    "src/dawn_native/BlendState.cpp",
-    "src/dawn_native/BlendState.h",
     "src/dawn_native/Buffer.cpp",
     "src/dawn_native/Buffer.h",
     "src/dawn_native/Builder.cpp",
@@ -437,8 +435,6 @@
       "src/dawn_native/d3d12/BindGroupD3D12.h",
       "src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp",
       "src/dawn_native/d3d12/BindGroupLayoutD3D12.h",
-      "src/dawn_native/d3d12/BlendStateD3D12.cpp",
-      "src/dawn_native/d3d12/BlendStateD3D12.h",
       "src/dawn_native/d3d12/BufferD3D12.cpp",
       "src/dawn_native/d3d12/BufferD3D12.h",
       "src/dawn_native/d3d12/CommandAllocatorManager.cpp",
@@ -493,8 +489,6 @@
       "IOKit.framework",
     ]
     sources += [
-      "src/dawn_native/metal/BlendStateMTL.h",
-      "src/dawn_native/metal/BlendStateMTL.mm",
       "src/dawn_native/metal/BufferMTL.h",
       "src/dawn_native/metal/BufferMTL.mm",
       "src/dawn_native/metal/CommandBufferMTL.h",
@@ -537,8 +531,6 @@
   if (dawn_enable_opengl) {
     deps += [ "third_party:glad" ]
     sources += [
-      "src/dawn_native/opengl/BlendStateGL.cpp",
-      "src/dawn_native/opengl/BlendStateGL.h",
       "src/dawn_native/opengl/BufferGL.cpp",
       "src/dawn_native/opengl/BufferGL.h",
       "src/dawn_native/opengl/CommandBufferGL.cpp",
@@ -580,8 +572,6 @@
       "src/dawn_native/vulkan/BindGroupLayoutVk.h",
       "src/dawn_native/vulkan/BindGroupVk.cpp",
       "src/dawn_native/vulkan/BindGroupVk.h",
-      "src/dawn_native/vulkan/BlendStateVk.cpp",
-      "src/dawn_native/vulkan/BlendStateVk.h",
       "src/dawn_native/vulkan/BufferUploader.cpp",
       "src/dawn_native/vulkan/BufferUploader.h",
       "src/dawn_native/vulkan/BufferVk.cpp",
@@ -827,7 +817,6 @@
     "src/tests/unittests/ToBackendTests.cpp",
     "src/tests/unittests/WireTests.cpp",
     "src/tests/unittests/validation/BindGroupValidationTests.cpp",
-    "src/tests/unittests/validation/BlendStateValidationTests.cpp",
     "src/tests/unittests/validation/BufferValidationTests.cpp",
     "src/tests/unittests/validation/CommandBufferValidationTests.cpp",
     "src/tests/unittests/validation/ComputeValidationTests.cpp",
diff --git a/dawn.json b/dawn.json
index 3921d1b..0fcc21d 100644
--- a/dawn.json
+++ b/dawn.json
@@ -119,40 +119,14 @@
             {"value": 4, "name": "max"}
         ]
     },
-    "blend state": {
-        "category": "object"
-    },
-    "blend state builder": {
-        "category": "object",
-        "methods": [
-            {
-                "name": "get result",
-                "returns": "blend state"
-            },
-            {
-                "name": "set blend enabled",
-                "args": [
-                    {"name": "blend enabled", "type": "bool"}
-                ]
-            },
-            {
-                "name": "set alpha blend",
-                "args": [
-                    {"name": "alpha blend", "type": "blend descriptor", "annotation": "const*"}
-                ]
-            },
-            {
-                "name": "set color blend",
-                "args": [
-                    {"name": "color blend", "type": "blend descriptor", "annotation": "const*"}
-                ]
-            },
-            {
-                "name": "set color write mask",
-                "args": [
-                    {"name": "color write mask", "type": "color write mask"}
-                ]
-            }
+    "blend state descriptor": {
+        "category": "structure",
+        "extensible": true,
+        "members": [
+            {"name": "blend enabled", "type": "bool"},
+            {"name": "alpha blend", "type": "blend descriptor"},
+            {"name": "color blend", "type": "blend descriptor"},
+            {"name": "color write mask", "type": "color write mask"}
         ]
     },
     "bool": {
@@ -433,10 +407,6 @@
                 ]
             },
             {
-                "name": "create blend state builder",
-                "returns": "blend state builder"
-            },
-            {
                 "name": "create buffer",
                 "returns": "buffer",
                 "args": [
@@ -920,7 +890,7 @@
             {"name": "sample count", "type": "uint32_t"},
             {"name": "depth stencil state", "type": "depth stencil state"},
             {"name": "num blend states", "type": "uint32_t"},
-            {"name": "blend states", "type": "blend state", "annotation": "const*", "length": "num blend states"}
+            {"name": "blend states", "type": "blend state descriptor", "annotation": "const*", "length": "num blend states"}
         ]
     },
     "sampler": {
diff --git a/examples/CHelloTriangle.cpp b/examples/CHelloTriangle.cpp
index ca4f114..741e528 100644
--- a/examples/CHelloTriangle.cpp
+++ b/examples/CHelloTriangle.cpp
@@ -85,10 +85,18 @@
         descriptor.sampleCount = 1;
 
         descriptor.numBlendStates = 1;
-        dawnBlendStateBuilder blendStateBuilder = dawnDeviceCreateBlendStateBuilder(device);
-        dawnBlendState blendState = dawnBlendStateBuilderGetResult(blendStateBuilder);
-        descriptor.blendStates =  &blendState;
-        dawnBlendStateBuilderRelease(blendStateBuilder);
+
+        dawnBlendDescriptor blendDescriptor;
+        blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
+        blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
+        blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
+        dawnBlendStateDescriptor blendStateDescriptor;
+        blendStateDescriptor.nextInChain = nullptr;
+        blendStateDescriptor.blendEnabled = false;
+        blendStateDescriptor.alphaBlend = blendDescriptor;
+        blendStateDescriptor.colorBlend = blendDescriptor;
+        blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
+        descriptor.blendStates = &blendStateDescriptor;
 
         dawnPipelineLayoutDescriptor pl;
         pl.nextInChain = nullptr;
@@ -109,7 +117,6 @@
 
         pipeline = dawnDeviceCreateRenderPipeline(device, &descriptor);
 
-        dawnBlendStateRelease(descriptor.blendStates[0]);
         dawnDepthStencilStateRelease(descriptor.depthStencilState);
         dawnInputStateRelease(descriptor.inputState);
     }
diff --git a/src/dawn_native/BlendState.cpp b/src/dawn_native/BlendState.cpp
deleted file mode 100644
index 2cf042d..0000000
--- a/src/dawn_native/BlendState.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "dawn_native/BlendState.h"
-
-#include "dawn_native/Device.h"
-
-namespace dawn_native {
-
-    // BlendStateBase
-
-    BlendStateBase::BlendStateBase(BlendStateBuilder* builder)
-        : ObjectBase(builder->GetDevice()), mBlendInfo(builder->mBlendInfo) {
-    }
-
-    const BlendStateBase::BlendInfo& BlendStateBase::GetBlendInfo() const {
-        return mBlendInfo;
-    }
-
-    // BlendStateBuilder
-
-    enum BlendStateSetProperties {
-        BLEND_STATE_PROPERTY_BLEND_ENABLED = 0x1,
-        BLEND_STATE_PROPERTY_ALPHA_BLEND = 0x2,
-        BLEND_STATE_PROPERTY_COLOR_BLEND = 0x4,
-        BLEND_STATE_PROPERTY_COLOR_WRITE_MASK = 0x08,
-    };
-
-    BlendStateBuilder::BlendStateBuilder(DeviceBase* device) : Builder(device) {
-    }
-
-    BlendStateBase* BlendStateBuilder::GetResultImpl() {
-        return GetDevice()->CreateBlendState(this);
-    }
-
-    void BlendStateBuilder::SetBlendEnabled(bool blendEnabled) {
-        if ((mPropertiesSet & BLEND_STATE_PROPERTY_BLEND_ENABLED) != 0) {
-            HandleError("Blend enabled property set multiple times");
-            return;
-        }
-
-        mPropertiesSet |= BLEND_STATE_PROPERTY_BLEND_ENABLED;
-
-        mBlendInfo.blendEnabled = blendEnabled;
-    }
-
-    void BlendStateBuilder::SetAlphaBlend(const BlendDescriptor* alphaBlend) {
-        if ((mPropertiesSet & BLEND_STATE_PROPERTY_ALPHA_BLEND) != 0) {
-            HandleError("Alpha blend property set multiple times");
-            return;
-        }
-
-        mPropertiesSet |= BLEND_STATE_PROPERTY_ALPHA_BLEND;
-
-        // TODO(yunchao.he@intel.com): validate the enum values in
-        // ValidateBlendStateDescriptor when it is added.
-        mBlendInfo.alphaBlend.operation = alphaBlend->operation;
-        mBlendInfo.alphaBlend.srcFactor = alphaBlend->srcFactor;
-        mBlendInfo.alphaBlend.dstFactor = alphaBlend->dstFactor;
-    }
-
-    void BlendStateBuilder::SetColorBlend(const BlendDescriptor* colorBlend) {
-        if ((mPropertiesSet & BLEND_STATE_PROPERTY_COLOR_BLEND) != 0) {
-            HandleError("Color blend property set multiple times");
-            return;
-        }
-
-        mPropertiesSet |= BLEND_STATE_PROPERTY_COLOR_BLEND;
-
-        // TODO(yunchao.he@intel.com): validate the enum values in
-        // ValidateBlendStateDescriptor when it is added.
-        mBlendInfo.colorBlend.operation = colorBlend->operation;
-        mBlendInfo.colorBlend.srcFactor = colorBlend->srcFactor;
-        mBlendInfo.colorBlend.dstFactor = colorBlend->dstFactor;
-    }
-
-    void BlendStateBuilder::SetColorWriteMask(dawn::ColorWriteMask colorWriteMask) {
-        if ((mPropertiesSet & BLEND_STATE_PROPERTY_COLOR_WRITE_MASK) != 0) {
-            HandleError("Color write mask property set multiple times");
-            return;
-        }
-
-        mPropertiesSet |= BLEND_STATE_PROPERTY_COLOR_WRITE_MASK;
-
-        mBlendInfo.colorWriteMask = colorWriteMask;
-    }
-}  // namespace dawn_native
diff --git a/src/dawn_native/BlendState.h b/src/dawn_native/BlendState.h
deleted file mode 100644
index 72e8821..0000000
--- a/src/dawn_native/BlendState.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef DAWNNATIVE_BLENDSTATE_H_
-#define DAWNNATIVE_BLENDSTATE_H_
-
-#include "dawn_native/Builder.h"
-#include "dawn_native/Forward.h"
-#include "dawn_native/ObjectBase.h"
-
-#include "dawn_native/dawn_platform.h"
-
-namespace dawn_native {
-
-    class BlendStateBase : public ObjectBase {
-      public:
-        BlendStateBase(BlendStateBuilder* builder);
-
-        struct BlendInfo {
-            bool blendEnabled = false;
-            BlendDescriptor alphaBlend = {dawn::BlendOperation::Add, dawn::BlendFactor::One,
-                                          dawn::BlendFactor::Zero};
-            BlendDescriptor colorBlend = {dawn::BlendOperation::Add, dawn::BlendFactor::One,
-                                          dawn::BlendFactor::Zero};
-            dawn::ColorWriteMask colorWriteMask = dawn::ColorWriteMask::All;
-        };
-
-        const BlendInfo& GetBlendInfo() const;
-
-      private:
-        BlendInfo mBlendInfo;
-    };
-
-    class BlendStateBuilder : public Builder<BlendStateBase> {
-      public:
-        BlendStateBuilder(DeviceBase* device);
-
-        // Dawn API
-        void SetBlendEnabled(bool blendEnabled);
-        void SetAlphaBlend(const BlendDescriptor* alphaBlend);
-        void SetColorBlend(const BlendDescriptor* colorBlend);
-        void SetColorWriteMask(dawn::ColorWriteMask colorWriteMask);
-
-      private:
-        friend class BlendStateBase;
-
-        BlendStateBase* GetResultImpl() override;
-
-        int mPropertiesSet = 0;
-
-        BlendStateBase::BlendInfo mBlendInfo;
-    };
-
-}  // namespace dawn_native
-
-#endif  // DAWNNATIVE_BLENDSTATE_H_
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 5ed4d27..c94dc11 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -16,7 +16,6 @@
 
 #include "dawn_native/BindGroup.h"
 #include "dawn_native/BindGroupLayout.h"
-#include "dawn_native/BlendState.h"
 #include "dawn_native/Buffer.h"
 #include "dawn_native/CommandBuffer.h"
 #include "dawn_native/ComputePipeline.h"
@@ -120,9 +119,6 @@
 
         return result;
     }
-    BlendStateBuilder* DeviceBase::CreateBlendStateBuilder() {
-        return new BlendStateBuilder(this);
-    }
     BufferBase* DeviceBase::CreateBuffer(const BufferDescriptor* descriptor) {
         BufferBase* result = nullptr;
 
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index bfd6649..012cb22 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -51,7 +51,6 @@
 
         FenceSignalTracker* GetFenceSignalTracker() const;
 
-        virtual BlendStateBase* CreateBlendState(BlendStateBuilder* builder) = 0;
         virtual CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) = 0;
         virtual DepthStencilStateBase* CreateDepthStencilState(
             DepthStencilStateBuilder* builder) = 0;
@@ -85,7 +84,6 @@
         // Dawn API
         BindGroupBase* CreateBindGroup(const BindGroupDescriptor* descriptor);
         BindGroupLayoutBase* CreateBindGroupLayout(const BindGroupLayoutDescriptor* descriptor);
-        BlendStateBuilder* CreateBlendStateBuilder();
         BufferBase* CreateBuffer(const BufferDescriptor* descriptor);
         CommandBufferBuilder* CreateCommandBufferBuilder();
         ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor);
diff --git a/src/dawn_native/Forward.h b/src/dawn_native/Forward.h
index 211fdc5..93e6352 100644
--- a/src/dawn_native/Forward.h
+++ b/src/dawn_native/Forward.h
@@ -23,8 +23,6 @@
     class BindGroupBuilder;
     class BindGroupLayoutBase;
     class BindGroupLayoutBuilder;
-    class BlendStateBase;
-    class BlendStateBuilder;
     class BufferBase;
     class BufferBuilder;
     class ComputePipelineBase;
diff --git a/src/dawn_native/RenderPipeline.cpp b/src/dawn_native/RenderPipeline.cpp
index 6d1f48c..1fde80a 100644
--- a/src/dawn_native/RenderPipeline.cpp
+++ b/src/dawn_native/RenderPipeline.cpp
@@ -15,7 +15,6 @@
 #include "dawn_native/RenderPipeline.h"
 
 #include "common/BitSetIterator.h"
-#include "dawn_native/BlendState.h"
 #include "dawn_native/DepthStencilState.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/InputState.h"
@@ -73,6 +72,20 @@
             return {};
         }
 
+        MaybeError ValidateBlendStateDescriptor(const BlendStateDescriptor* descriptor) {
+            if (descriptor->nextInChain != nullptr) {
+                return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
+            }
+            DAWN_TRY(ValidateBlendOperation(descriptor->alphaBlend.operation));
+            DAWN_TRY(ValidateBlendFactor(descriptor->alphaBlend.srcFactor));
+            DAWN_TRY(ValidateBlendFactor(descriptor->alphaBlend.dstFactor));
+            DAWN_TRY(ValidateBlendOperation(descriptor->colorBlend.operation));
+            DAWN_TRY(ValidateBlendFactor(descriptor->colorBlend.srcFactor));
+            DAWN_TRY(ValidateBlendFactor(descriptor->colorBlend.dstFactor));
+            DAWN_TRY(ValidateColorWriteMask(descriptor->colorWriteMask));
+            return {};
+        }
+
     }  // namespace
 
     MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
@@ -94,9 +107,7 @@
         }
 
         for (uint32_t i = 0; i < descriptor->numBlendStates; ++i) {
-            if (descriptor->blendStates[i] == nullptr) {
-                return DAWN_VALIDATION_ERROR("Blend state must not be null");
-            }
+            DAWN_TRY(ValidateBlendStateDescriptor(&descriptor->blendStates[i]));
         }
 
         DAWN_TRY(ValidateIndexFormat(descriptor->indexFormat));
@@ -158,9 +169,10 @@
         // attachment are set?
     }
 
-    BlendStateBase* RenderPipelineBase::GetBlendState(uint32_t attachmentSlot) {
+    const BlendStateDescriptor* RenderPipelineBase::GetBlendStateDescriptor(
+        uint32_t attachmentSlot) {
         ASSERT(attachmentSlot < mBlendStates.size());
-        return mBlendStates[attachmentSlot].Get();
+        return &mBlendStates[attachmentSlot];
     }
 
     DepthStencilStateBase* RenderPipelineBase::GetDepthStencilState() {
diff --git a/src/dawn_native/RenderPipeline.h b/src/dawn_native/RenderPipeline.h
index ad70e7e..35cf013 100644
--- a/src/dawn_native/RenderPipeline.h
+++ b/src/dawn_native/RenderPipeline.h
@@ -15,7 +15,6 @@
 #ifndef DAWNNATIVE_RENDERPIPELINE_H_
 #define DAWNNATIVE_RENDERPIPELINE_H_
 
-#include "dawn_native/BlendState.h"
 #include "dawn_native/DepthStencilState.h"
 #include "dawn_native/InputState.h"
 #include "dawn_native/Pipeline.h"
@@ -36,7 +35,7 @@
       public:
         RenderPipelineBase(DeviceBase* device, const RenderPipelineDescriptor* descriptor);
 
-        BlendStateBase* GetBlendState(uint32_t attachmentSlot);
+        const BlendStateDescriptor* GetBlendStateDescriptor(uint32_t attachmentSlot);
         DepthStencilStateBase* GetDepthStencilState();
         dawn::IndexFormat GetIndexFormat() const;
         InputStateBase* GetInputState();
@@ -56,7 +55,7 @@
         dawn::IndexFormat mIndexFormat;
         Ref<InputStateBase> mInputState;
         dawn::PrimitiveTopology mPrimitiveTopology;
-        std::array<Ref<BlendStateBase>, kMaxColorAttachments> mBlendStates;
+        std::array<BlendStateDescriptor, kMaxColorAttachments> mBlendStates;
 
         std::bitset<kMaxColorAttachments> mColorAttachmentsSet;
         std::array<dawn::TextureFormat, kMaxColorAttachments> mColorAttachmentFormats;
diff --git a/src/dawn_native/ToBackend.h b/src/dawn_native/ToBackend.h
index 3477e27..34b7492 100644
--- a/src/dawn_native/ToBackend.h
+++ b/src/dawn_native/ToBackend.h
@@ -34,11 +34,6 @@
     };
 
     template <typename BackendTraits>
-    struct ToBackendTraits<BlendStateBase, BackendTraits> {
-        using BackendType = typename BackendTraits::BlendStateType;
-    };
-
-    template <typename BackendTraits>
     struct ToBackendTraits<BufferBase, BackendTraits> {
         using BackendType = typename BackendTraits::BufferType;
     };
diff --git a/src/dawn_native/d3d12/BlendStateD3D12.cpp b/src/dawn_native/d3d12/BlendStateD3D12.cpp
deleted file mode 100644
index 862315e..0000000
--- a/src/dawn_native/d3d12/BlendStateD3D12.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "dawn_native/d3d12/BlendStateD3D12.h"
-
-#include "common/Assert.h"
-#include "dawn_native/d3d12/DeviceD3D12.h"
-
-namespace dawn_native { namespace d3d12 {
-
-    namespace {
-        D3D12_BLEND D3D12Blend(dawn::BlendFactor factor) {
-            switch (factor) {
-                case dawn::BlendFactor::Zero:
-                    return D3D12_BLEND_ZERO;
-                case dawn::BlendFactor::One:
-                    return D3D12_BLEND_ONE;
-                case dawn::BlendFactor::SrcColor:
-                    return D3D12_BLEND_SRC_COLOR;
-                case dawn::BlendFactor::OneMinusSrcColor:
-                    return D3D12_BLEND_INV_SRC_COLOR;
-                case dawn::BlendFactor::SrcAlpha:
-                    return D3D12_BLEND_SRC_ALPHA;
-                case dawn::BlendFactor::OneMinusSrcAlpha:
-                    return D3D12_BLEND_INV_SRC_ALPHA;
-                case dawn::BlendFactor::DstColor:
-                    return D3D12_BLEND_DEST_COLOR;
-                case dawn::BlendFactor::OneMinusDstColor:
-                    return D3D12_BLEND_INV_DEST_COLOR;
-                case dawn::BlendFactor::DstAlpha:
-                    return D3D12_BLEND_DEST_ALPHA;
-                case dawn::BlendFactor::OneMinusDstAlpha:
-                    return D3D12_BLEND_INV_DEST_ALPHA;
-                case dawn::BlendFactor::SrcAlphaSaturated:
-                    return D3D12_BLEND_SRC_ALPHA_SAT;
-                case dawn::BlendFactor::BlendColor:
-                    return D3D12_BLEND_BLEND_FACTOR;
-                case dawn::BlendFactor::OneMinusBlendColor:
-                    return D3D12_BLEND_INV_BLEND_FACTOR;
-                default:
-                    UNREACHABLE();
-            }
-        }
-
-        D3D12_BLEND_OP D3D12BlendOperation(dawn::BlendOperation operation) {
-            switch (operation) {
-                case dawn::BlendOperation::Add:
-                    return D3D12_BLEND_OP_ADD;
-                case dawn::BlendOperation::Subtract:
-                    return D3D12_BLEND_OP_SUBTRACT;
-                case dawn::BlendOperation::ReverseSubtract:
-                    return D3D12_BLEND_OP_REV_SUBTRACT;
-                case dawn::BlendOperation::Min:
-                    return D3D12_BLEND_OP_MIN;
-                case dawn::BlendOperation::Max:
-                    return D3D12_BLEND_OP_MAX;
-                default:
-                    UNREACHABLE();
-            }
-        }
-
-        uint8_t D3D12RenderTargetWriteMask(dawn::ColorWriteMask colorWriteMask) {
-            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Red) ==
-                              D3D12_COLOR_WRITE_ENABLE_RED,
-                          "ColorWriteMask values must match");
-            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Green) ==
-                              D3D12_COLOR_WRITE_ENABLE_GREEN,
-                          "ColorWriteMask values must match");
-            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Blue) ==
-                              D3D12_COLOR_WRITE_ENABLE_BLUE,
-                          "ColorWriteMask values must match");
-            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Alpha) ==
-                              D3D12_COLOR_WRITE_ENABLE_ALPHA,
-                          "ColorWriteMask values must match");
-            return static_cast<uint8_t>(colorWriteMask);
-        }
-    }  // namespace
-
-    BlendState::BlendState(BlendStateBuilder* builder) : BlendStateBase(builder) {
-        auto& info = GetBlendInfo();
-        mBlendDesc.BlendEnable = info.blendEnabled;
-        mBlendDesc.SrcBlend = D3D12Blend(info.colorBlend.srcFactor);
-        mBlendDesc.DestBlend = D3D12Blend(info.colorBlend.dstFactor);
-        mBlendDesc.BlendOp = D3D12BlendOperation(info.colorBlend.operation);
-        mBlendDesc.SrcBlendAlpha = D3D12Blend(info.alphaBlend.srcFactor);
-        mBlendDesc.DestBlendAlpha = D3D12Blend(info.alphaBlend.dstFactor);
-        mBlendDesc.BlendOpAlpha = D3D12BlendOperation(info.alphaBlend.operation);
-        mBlendDesc.RenderTargetWriteMask = D3D12RenderTargetWriteMask(info.colorWriteMask);
-        mBlendDesc.LogicOpEnable = false;
-        mBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
-    }
-
-    const D3D12_RENDER_TARGET_BLEND_DESC& BlendState::GetD3D12BlendDesc() const {
-        return mBlendDesc;
-    }
-
-}}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/BlendStateD3D12.h b/src/dawn_native/d3d12/BlendStateD3D12.h
deleted file mode 100644
index ea1a32d..0000000
--- a/src/dawn_native/d3d12/BlendStateD3D12.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef DAWNNATIVE_D3D12_BLENDSTATED3D12_H_
-#define DAWNNATIVE_D3D12_BLENDSTATED3D12_H_
-
-#include "dawn_native/BlendState.h"
-
-#include "dawn_native/d3d12/d3d12_platform.h"
-
-namespace dawn_native { namespace d3d12 {
-
-    class BlendState : public BlendStateBase {
-      public:
-        BlendState(BlendStateBuilder* builder);
-
-        const D3D12_RENDER_TARGET_BLEND_DESC& GetD3D12BlendDesc() const;
-
-      private:
-        D3D12_RENDER_TARGET_BLEND_DESC mBlendDesc;
-    };
-
-}}  // namespace dawn_native::d3d12
-
-#endif  // DAWNNATIVE_D3D12_BLENDSTATED3D12_H_
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 26fc819..5bfc7cc 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -19,7 +19,6 @@
 #include "dawn_native/D3D12Backend.h"
 #include "dawn_native/d3d12/BindGroupD3D12.h"
 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
-#include "dawn_native/d3d12/BlendStateD3D12.h"
 #include "dawn_native/d3d12/BufferD3D12.h"
 #include "dawn_native/d3d12/CommandAllocatorManager.h"
 #include "dawn_native/d3d12/CommandBufferD3D12.h"
@@ -294,9 +293,6 @@
         const BindGroupLayoutDescriptor* descriptor) {
         return new BindGroupLayout(this, descriptor);
     }
-    BlendStateBase* Device::CreateBlendState(BlendStateBuilder* builder) {
-        return new BlendState(builder);
-    }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
         return new Buffer(this, descriptor);
     }
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 193b049..70341ae 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -41,7 +41,6 @@
         Device();
         ~Device();
 
-        BlendStateBase* CreateBlendState(BlendStateBuilder* builder) override;
         CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
         DepthStencilStateBase* CreateDepthStencilState(DepthStencilStateBuilder* builder) override;
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
diff --git a/src/dawn_native/d3d12/Forward.h b/src/dawn_native/d3d12/Forward.h
index cfc7677..d3b6b44 100644
--- a/src/dawn_native/d3d12/Forward.h
+++ b/src/dawn_native/d3d12/Forward.h
@@ -21,7 +21,6 @@
 
     class BindGroup;
     class BindGroupLayout;
-    class BlendState;
     class Buffer;
     class CommandBuffer;
     class ComputePipeline;
@@ -41,7 +40,6 @@
     struct D3D12BackendTraits {
         using BindGroupType = BindGroup;
         using BindGroupLayoutType = BindGroupLayout;
-        using BlendStateType = BlendState;
         using BufferType = Buffer;
         using CommandBufferType = CommandBuffer;
         using ComputePipelineType = ComputePipeline;
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
index 17b4424..2866485 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
@@ -15,7 +15,6 @@
 #include "dawn_native/d3d12/RenderPipelineD3D12.h"
 
 #include "common/Assert.h"
-#include "dawn_native/d3d12/BlendStateD3D12.h"
 #include "dawn_native/d3d12/DepthStencilStateD3D12.h"
 #include "dawn_native/d3d12/DeviceD3D12.h"
 #include "dawn_native/d3d12/InputStateD3D12.h"
@@ -61,6 +60,88 @@
                     UNREACHABLE();
             }
         }
+
+        D3D12_BLEND D3D12Blend(dawn::BlendFactor factor) {
+            switch (factor) {
+                case dawn::BlendFactor::Zero:
+                    return D3D12_BLEND_ZERO;
+                case dawn::BlendFactor::One:
+                    return D3D12_BLEND_ONE;
+                case dawn::BlendFactor::SrcColor:
+                    return D3D12_BLEND_SRC_COLOR;
+                case dawn::BlendFactor::OneMinusSrcColor:
+                    return D3D12_BLEND_INV_SRC_COLOR;
+                case dawn::BlendFactor::SrcAlpha:
+                    return D3D12_BLEND_SRC_ALPHA;
+                case dawn::BlendFactor::OneMinusSrcAlpha:
+                    return D3D12_BLEND_INV_SRC_ALPHA;
+                case dawn::BlendFactor::DstColor:
+                    return D3D12_BLEND_DEST_COLOR;
+                case dawn::BlendFactor::OneMinusDstColor:
+                    return D3D12_BLEND_INV_DEST_COLOR;
+                case dawn::BlendFactor::DstAlpha:
+                    return D3D12_BLEND_DEST_ALPHA;
+                case dawn::BlendFactor::OneMinusDstAlpha:
+                    return D3D12_BLEND_INV_DEST_ALPHA;
+                case dawn::BlendFactor::SrcAlphaSaturated:
+                    return D3D12_BLEND_SRC_ALPHA_SAT;
+                case dawn::BlendFactor::BlendColor:
+                    return D3D12_BLEND_BLEND_FACTOR;
+                case dawn::BlendFactor::OneMinusBlendColor:
+                    return D3D12_BLEND_INV_BLEND_FACTOR;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        D3D12_BLEND_OP D3D12BlendOperation(dawn::BlendOperation operation) {
+            switch (operation) {
+                case dawn::BlendOperation::Add:
+                    return D3D12_BLEND_OP_ADD;
+                case dawn::BlendOperation::Subtract:
+                    return D3D12_BLEND_OP_SUBTRACT;
+                case dawn::BlendOperation::ReverseSubtract:
+                    return D3D12_BLEND_OP_REV_SUBTRACT;
+                case dawn::BlendOperation::Min:
+                    return D3D12_BLEND_OP_MIN;
+                case dawn::BlendOperation::Max:
+                    return D3D12_BLEND_OP_MAX;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        uint8_t D3D12RenderTargetWriteMask(dawn::ColorWriteMask colorWriteMask) {
+            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Red) ==
+                              D3D12_COLOR_WRITE_ENABLE_RED,
+                          "ColorWriteMask values must match");
+            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Green) ==
+                              D3D12_COLOR_WRITE_ENABLE_GREEN,
+                          "ColorWriteMask values must match");
+            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Blue) ==
+                              D3D12_COLOR_WRITE_ENABLE_BLUE,
+                          "ColorWriteMask values must match");
+            static_assert(static_cast<D3D12_COLOR_WRITE_ENABLE>(dawn::ColorWriteMask::Alpha) ==
+                              D3D12_COLOR_WRITE_ENABLE_ALPHA,
+                          "ColorWriteMask values must match");
+            return static_cast<uint8_t>(colorWriteMask);
+        }
+
+        D3D12_RENDER_TARGET_BLEND_DESC ComputeBlendDesc(const BlendStateDescriptor* descriptor) {
+            D3D12_RENDER_TARGET_BLEND_DESC blendDesc;
+            blendDesc.BlendEnable = descriptor->blendEnabled;
+            blendDesc.SrcBlend = D3D12Blend(descriptor->colorBlend.srcFactor);
+            blendDesc.DestBlend = D3D12Blend(descriptor->colorBlend.dstFactor);
+            blendDesc.BlendOp = D3D12BlendOperation(descriptor->colorBlend.operation);
+            blendDesc.SrcBlendAlpha = D3D12Blend(descriptor->alphaBlend.srcFactor);
+            blendDesc.DestBlendAlpha = D3D12Blend(descriptor->alphaBlend.dstFactor);
+            blendDesc.BlendOpAlpha = D3D12BlendOperation(descriptor->alphaBlend.operation);
+            blendDesc.RenderTargetWriteMask =
+                D3D12RenderTargetWriteMask(descriptor->colorWriteMask);
+            blendDesc.LogicOpEnable = false;
+            blendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
+            return blendDesc;
+        }
     }  // namespace
 
     RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
@@ -151,7 +232,7 @@
         for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
             descriptorD3D12.RTVFormats[i] = D3D12TextureFormat(GetColorAttachmentFormat(i));
             descriptorD3D12.BlendState.RenderTarget[i] =
-                ToBackend(GetBlendState(i))->GetD3D12BlendDesc();
+                ComputeBlendDesc(GetBlendStateDescriptor(i));
         }
         descriptorD3D12.NumRenderTargets = static_cast<uint32_t>(GetColorAttachmentsMask().count());
 
diff --git a/src/dawn_native/metal/BlendStateMTL.h b/src/dawn_native/metal/BlendStateMTL.h
deleted file mode 100644
index 484bf51..0000000
--- a/src/dawn_native/metal/BlendStateMTL.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef DAWNNATIVE_METAL_BLENDSTATEMTL_H_
-#define DAWNNATIVE_METAL_BLENDSTATEMTL_H_
-
-#include "dawn_native/BlendState.h"
-
-#import <Metal/Metal.h>
-namespace dawn_native { namespace metal {
-
-    class BlendState : public BlendStateBase {
-      public:
-        BlendState(BlendStateBuilder* builder);
-
-        void ApplyBlendState(MTLRenderPipelineColorAttachmentDescriptor* descriptor) const;
-    };
-
-}}  // namespace dawn_native::metal
-
-#endif  // DAWNNATIVE_METAL_BLENDSTATEMTL_H_
diff --git a/src/dawn_native/metal/BlendStateMTL.mm b/src/dawn_native/metal/BlendStateMTL.mm
deleted file mode 100644
index 0622755..0000000
--- a/src/dawn_native/metal/BlendStateMTL.mm
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "dawn_native/metal/BlendStateMTL.h"
-
-namespace dawn_native { namespace metal {
-
-    namespace {
-
-        MTLBlendFactor MetalBlendFactor(dawn::BlendFactor factor, bool alpha) {
-            switch (factor) {
-                case dawn::BlendFactor::Zero:
-                    return MTLBlendFactorZero;
-                case dawn::BlendFactor::One:
-                    return MTLBlendFactorOne;
-                case dawn::BlendFactor::SrcColor:
-                    return MTLBlendFactorSourceColor;
-                case dawn::BlendFactor::OneMinusSrcColor:
-                    return MTLBlendFactorOneMinusSourceColor;
-                case dawn::BlendFactor::SrcAlpha:
-                    return MTLBlendFactorSourceAlpha;
-                case dawn::BlendFactor::OneMinusSrcAlpha:
-                    return MTLBlendFactorOneMinusSourceAlpha;
-                case dawn::BlendFactor::DstColor:
-                    return MTLBlendFactorDestinationColor;
-                case dawn::BlendFactor::OneMinusDstColor:
-                    return MTLBlendFactorOneMinusDestinationColor;
-                case dawn::BlendFactor::DstAlpha:
-                    return MTLBlendFactorDestinationAlpha;
-                case dawn::BlendFactor::OneMinusDstAlpha:
-                    return MTLBlendFactorOneMinusDestinationAlpha;
-                case dawn::BlendFactor::SrcAlphaSaturated:
-                    return MTLBlendFactorSourceAlphaSaturated;
-                case dawn::BlendFactor::BlendColor:
-                    return alpha ? MTLBlendFactorBlendAlpha : MTLBlendFactorBlendColor;
-                case dawn::BlendFactor::OneMinusBlendColor:
-                    return alpha ? MTLBlendFactorOneMinusBlendAlpha
-                                 : MTLBlendFactorOneMinusBlendColor;
-            }
-        }
-
-        MTLBlendOperation MetalBlendOperation(dawn::BlendOperation operation) {
-            switch (operation) {
-                case dawn::BlendOperation::Add:
-                    return MTLBlendOperationAdd;
-                case dawn::BlendOperation::Subtract:
-                    return MTLBlendOperationSubtract;
-                case dawn::BlendOperation::ReverseSubtract:
-                    return MTLBlendOperationReverseSubtract;
-                case dawn::BlendOperation::Min:
-                    return MTLBlendOperationMin;
-                case dawn::BlendOperation::Max:
-                    return MTLBlendOperationMax;
-            }
-        }
-
-        MTLColorWriteMask MetalColorWriteMask(dawn::ColorWriteMask colorWriteMask) {
-            return (((colorWriteMask & dawn::ColorWriteMask::Red) != dawn::ColorWriteMask::None
-                         ? MTLColorWriteMaskRed
-                         : MTLColorWriteMaskNone) |
-                    ((colorWriteMask & dawn::ColorWriteMask::Green) != dawn::ColorWriteMask::None
-                         ? MTLColorWriteMaskGreen
-                         : MTLColorWriteMaskNone) |
-                    ((colorWriteMask & dawn::ColorWriteMask::Blue) != dawn::ColorWriteMask::None
-                         ? MTLColorWriteMaskBlue
-                         : MTLColorWriteMaskNone) |
-                    ((colorWriteMask & dawn::ColorWriteMask::Alpha) != dawn::ColorWriteMask::None
-                         ? MTLColorWriteMaskAlpha
-                         : MTLColorWriteMaskNone));
-        }
-    }
-
-    BlendState::BlendState(BlendStateBuilder* builder) : BlendStateBase(builder) {
-    }
-
-    void BlendState::ApplyBlendState(MTLRenderPipelineColorAttachmentDescriptor* descriptor) const {
-        auto& info = GetBlendInfo();
-        descriptor.blendingEnabled = info.blendEnabled;
-        descriptor.sourceRGBBlendFactor = MetalBlendFactor(info.colorBlend.srcFactor, false);
-        descriptor.destinationRGBBlendFactor = MetalBlendFactor(info.colorBlend.dstFactor, false);
-        descriptor.rgbBlendOperation = MetalBlendOperation(info.colorBlend.operation);
-        descriptor.sourceAlphaBlendFactor = MetalBlendFactor(info.alphaBlend.srcFactor, true);
-        descriptor.destinationAlphaBlendFactor = MetalBlendFactor(info.alphaBlend.dstFactor, true);
-        descriptor.alphaBlendOperation = MetalBlendOperation(info.alphaBlend.operation);
-        descriptor.writeMask = MetalColorWriteMask(info.colorWriteMask);
-    }
-
-}}  // namespace dawn_native::metal
diff --git a/src/dawn_native/metal/DeviceMTL.h b/src/dawn_native/metal/DeviceMTL.h
index e1d8704..9aeb4a7 100644
--- a/src/dawn_native/metal/DeviceMTL.h
+++ b/src/dawn_native/metal/DeviceMTL.h
@@ -37,7 +37,6 @@
         Device(id<MTLDevice> mtlDevice);
         ~Device();
 
-        BlendStateBase* CreateBlendState(BlendStateBuilder* builder) override;
         CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
         DepthStencilStateBase* CreateDepthStencilState(DepthStencilStateBuilder* builder) override;
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm
index 7d6cda0..8f88822 100644
--- a/src/dawn_native/metal/DeviceMTL.mm
+++ b/src/dawn_native/metal/DeviceMTL.mm
@@ -18,7 +18,6 @@
 #include "dawn_native/BindGroupLayout.h"
 #include "dawn_native/MetalBackend.h"
 #include "dawn_native/RenderPassDescriptor.h"
-#include "dawn_native/metal/BlendStateMTL.h"
 #include "dawn_native/metal/BufferMTL.h"
 #include "dawn_native/metal/CommandBufferMTL.h"
 #include "dawn_native/metal/ComputePipelineMTL.h"
@@ -170,9 +169,6 @@
         const BindGroupLayoutDescriptor* descriptor) {
         return new BindGroupLayout(this, descriptor);
     }
-    BlendStateBase* Device::CreateBlendState(BlendStateBuilder* builder) {
-        return new BlendState(builder);
-    }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
         return new Buffer(this, descriptor);
     }
diff --git a/src/dawn_native/metal/Forward.h b/src/dawn_native/metal/Forward.h
index 7c0b286..6e67558 100644
--- a/src/dawn_native/metal/Forward.h
+++ b/src/dawn_native/metal/Forward.h
@@ -27,7 +27,6 @@
 
     using BindGroup = BindGroupBase;
     using BindGroupLayout = BindGroupLayoutBase;
-    class BlendState;
     class Buffer;
     class CommandBuffer;
     class ComputePipeline;
@@ -48,7 +47,6 @@
     struct MetalBackendTraits {
         using BindGroupType = BindGroup;
         using BindGroupLayoutType = BindGroupLayout;
-        using BlendStateType = BlendState;
         using BufferType = Buffer;
         using CommandBufferType = CommandBuffer;
         using ComputePipelineType = ComputePipeline;
diff --git a/src/dawn_native/metal/RenderPipelineMTL.mm b/src/dawn_native/metal/RenderPipelineMTL.mm
index 31ff355..b8ba954 100644
--- a/src/dawn_native/metal/RenderPipelineMTL.mm
+++ b/src/dawn_native/metal/RenderPipelineMTL.mm
@@ -14,7 +14,6 @@
 
 #include "dawn_native/metal/RenderPipelineMTL.h"
 
-#include "dawn_native/metal/BlendStateMTL.h"
 #include "dawn_native/metal/DepthStencilStateMTL.h"
 #include "dawn_native/metal/DeviceMTL.h"
 #include "dawn_native/metal/InputStateMTL.h"
@@ -62,7 +61,89 @@
                     return MTLIndexTypeUInt32;
             }
         }
-    }
+
+        MTLBlendFactor MetalBlendFactor(dawn::BlendFactor factor, bool alpha) {
+            switch (factor) {
+                case dawn::BlendFactor::Zero:
+                    return MTLBlendFactorZero;
+                case dawn::BlendFactor::One:
+                    return MTLBlendFactorOne;
+                case dawn::BlendFactor::SrcColor:
+                    return MTLBlendFactorSourceColor;
+                case dawn::BlendFactor::OneMinusSrcColor:
+                    return MTLBlendFactorOneMinusSourceColor;
+                case dawn::BlendFactor::SrcAlpha:
+                    return MTLBlendFactorSourceAlpha;
+                case dawn::BlendFactor::OneMinusSrcAlpha:
+                    return MTLBlendFactorOneMinusSourceAlpha;
+                case dawn::BlendFactor::DstColor:
+                    return MTLBlendFactorDestinationColor;
+                case dawn::BlendFactor::OneMinusDstColor:
+                    return MTLBlendFactorOneMinusDestinationColor;
+                case dawn::BlendFactor::DstAlpha:
+                    return MTLBlendFactorDestinationAlpha;
+                case dawn::BlendFactor::OneMinusDstAlpha:
+                    return MTLBlendFactorOneMinusDestinationAlpha;
+                case dawn::BlendFactor::SrcAlphaSaturated:
+                    return MTLBlendFactorSourceAlphaSaturated;
+                case dawn::BlendFactor::BlendColor:
+                    return alpha ? MTLBlendFactorBlendAlpha : MTLBlendFactorBlendColor;
+                case dawn::BlendFactor::OneMinusBlendColor:
+                    return alpha ? MTLBlendFactorOneMinusBlendAlpha
+                                 : MTLBlendFactorOneMinusBlendColor;
+            }
+        }
+
+        MTLBlendOperation MetalBlendOperation(dawn::BlendOperation operation) {
+            switch (operation) {
+                case dawn::BlendOperation::Add:
+                    return MTLBlendOperationAdd;
+                case dawn::BlendOperation::Subtract:
+                    return MTLBlendOperationSubtract;
+                case dawn::BlendOperation::ReverseSubtract:
+                    return MTLBlendOperationReverseSubtract;
+                case dawn::BlendOperation::Min:
+                    return MTLBlendOperationMin;
+                case dawn::BlendOperation::Max:
+                    return MTLBlendOperationMax;
+            }
+        }
+
+        MTLColorWriteMask MetalColorWriteMask(dawn::ColorWriteMask colorWriteMask) {
+            MTLColorWriteMask mask = MTLColorWriteMaskNone;
+
+            if (colorWriteMask & dawn::ColorWriteMask::Red) {
+                mask |= MTLColorWriteMaskRed;
+            }
+            if (colorWriteMask & dawn::ColorWriteMask::Green) {
+                mask |= MTLColorWriteMaskGreen;
+            }
+            if (colorWriteMask & dawn::ColorWriteMask::Blue) {
+                mask |= MTLColorWriteMaskBlue;
+            }
+            if (colorWriteMask & dawn::ColorWriteMask::Alpha) {
+                mask |= MTLColorWriteMaskAlpha;
+            }
+
+            return mask;
+        }
+
+        void ComputeBlendDesc(MTLRenderPipelineColorAttachmentDescriptor* attachment,
+                              const BlendStateDescriptor* descriptor) {
+            attachment.blendingEnabled = descriptor->blendEnabled;
+            attachment.sourceRGBBlendFactor =
+                MetalBlendFactor(descriptor->colorBlend.srcFactor, false);
+            attachment.destinationRGBBlendFactor =
+                MetalBlendFactor(descriptor->colorBlend.dstFactor, false);
+            attachment.rgbBlendOperation = MetalBlendOperation(descriptor->colorBlend.operation);
+            attachment.sourceAlphaBlendFactor =
+                MetalBlendFactor(descriptor->alphaBlend.srcFactor, true);
+            attachment.destinationAlphaBlendFactor =
+                MetalBlendFactor(descriptor->alphaBlend.dstFactor, true);
+            attachment.alphaBlendOperation = MetalBlendOperation(descriptor->alphaBlend.operation);
+            attachment.writeMask = MetalColorWriteMask(descriptor->colorWriteMask);
+        }
+    }  // anonymous namespace
 
     RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
         : RenderPipelineBase(device, descriptor),
@@ -94,7 +175,8 @@
         for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
             descriptorMTL.colorAttachments[i].pixelFormat =
                 MetalPixelFormat(GetColorAttachmentFormat(i));
-            ToBackend(GetBlendState(i))->ApplyBlendState(descriptorMTL.colorAttachments[i]);
+            const BlendStateDescriptor* descriptor = GetBlendStateDescriptor(i);
+            ComputeBlendDesc(descriptorMTL.colorAttachments[i], descriptor);
         }
 
         descriptorMTL.inputPrimitiveTopology = MTLInputPrimitiveTopology(GetPrimitiveTopology());
diff --git a/src/dawn_native/null/NullBackend.cpp b/src/dawn_native/null/NullBackend.cpp
index 9d1c614..4c84785 100644
--- a/src/dawn_native/null/NullBackend.cpp
+++ b/src/dawn_native/null/NullBackend.cpp
@@ -42,9 +42,6 @@
         const BindGroupLayoutDescriptor* descriptor) {
         return new BindGroupLayout(this, descriptor);
     }
-    BlendStateBase* Device::CreateBlendState(BlendStateBuilder* builder) {
-        return new BlendState(builder);
-    }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
         return new Buffer(this, descriptor);
     }
diff --git a/src/dawn_native/null/NullBackend.h b/src/dawn_native/null/NullBackend.h
index 361680f..aecb4df 100644
--- a/src/dawn_native/null/NullBackend.h
+++ b/src/dawn_native/null/NullBackend.h
@@ -19,7 +19,6 @@
 
 #include "dawn_native/BindGroup.h"
 #include "dawn_native/BindGroupLayout.h"
-#include "dawn_native/BlendState.h"
 #include "dawn_native/Buffer.h"
 #include "dawn_native/CommandBuffer.h"
 #include "dawn_native/ComputePipeline.h"
@@ -40,7 +39,6 @@
 
     using BindGroup = BindGroupBase;
     using BindGroupLayout = BindGroupLayoutBase;
-    using BlendState = BlendStateBase;
     class Buffer;
     class CommandBuffer;
     using ComputePipeline = ComputePipelineBase;
@@ -60,7 +58,6 @@
     struct NullBackendTraits {
         using BindGroupType = BindGroup;
         using BindGroupLayoutType = BindGroupLayout;
-        using BlendStateType = BlendState;
         using BufferType = Buffer;
         using CommandBufferType = CommandBuffer;
         using ComputePipelineType = ComputePipeline;
@@ -93,7 +90,6 @@
         Device();
         ~Device();
 
-        BlendStateBase* CreateBlendState(BlendStateBuilder* builder) override;
         CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
         DepthStencilStateBase* CreateDepthStencilState(DepthStencilStateBuilder* builder) override;
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
diff --git a/src/dawn_native/opengl/BlendStateGL.cpp b/src/dawn_native/opengl/BlendStateGL.cpp
deleted file mode 100644
index 6460cec..0000000
--- a/src/dawn_native/opengl/BlendStateGL.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "dawn_native/opengl/BlendStateGL.h"
-
-#include "common/Assert.h"
-
-namespace dawn_native { namespace opengl {
-
-    namespace {
-        GLenum GLBlendFactor(dawn::BlendFactor factor, bool alpha) {
-            switch (factor) {
-                case dawn::BlendFactor::Zero:
-                    return GL_ZERO;
-                case dawn::BlendFactor::One:
-                    return GL_ONE;
-                case dawn::BlendFactor::SrcColor:
-                    return GL_SRC_COLOR;
-                case dawn::BlendFactor::OneMinusSrcColor:
-                    return GL_ONE_MINUS_SRC_COLOR;
-                case dawn::BlendFactor::SrcAlpha:
-                    return GL_SRC_ALPHA;
-                case dawn::BlendFactor::OneMinusSrcAlpha:
-                    return GL_ONE_MINUS_SRC_ALPHA;
-                case dawn::BlendFactor::DstColor:
-                    return GL_DST_COLOR;
-                case dawn::BlendFactor::OneMinusDstColor:
-                    return GL_ONE_MINUS_DST_COLOR;
-                case dawn::BlendFactor::DstAlpha:
-                    return GL_DST_ALPHA;
-                case dawn::BlendFactor::OneMinusDstAlpha:
-                    return GL_ONE_MINUS_DST_ALPHA;
-                case dawn::BlendFactor::SrcAlphaSaturated:
-                    return GL_SRC_ALPHA_SATURATE;
-                case dawn::BlendFactor::BlendColor:
-                    return alpha ? GL_CONSTANT_ALPHA : GL_CONSTANT_COLOR;
-                case dawn::BlendFactor::OneMinusBlendColor:
-                    return alpha ? GL_ONE_MINUS_CONSTANT_ALPHA : GL_ONE_MINUS_CONSTANT_COLOR;
-                default:
-                    UNREACHABLE();
-            }
-        }
-
-        GLenum GLBlendMode(dawn::BlendOperation operation) {
-            switch (operation) {
-                case dawn::BlendOperation::Add:
-                    return GL_FUNC_ADD;
-                case dawn::BlendOperation::Subtract:
-                    return GL_FUNC_SUBTRACT;
-                case dawn::BlendOperation::ReverseSubtract:
-                    return GL_FUNC_REVERSE_SUBTRACT;
-                case dawn::BlendOperation::Min:
-                    return GL_MIN;
-                case dawn::BlendOperation::Max:
-                    return GL_MAX;
-                default:
-                    UNREACHABLE();
-            }
-        }
-    }  // namespace
-
-    BlendState::BlendState(BlendStateBuilder* builder) : BlendStateBase(builder) {
-    }
-
-    void BlendState::ApplyNow(uint32_t attachment) {
-        const auto& info = GetBlendInfo();
-
-        if (info.blendEnabled) {
-            glEnablei(GL_BLEND, attachment);
-            glBlendEquationSeparatei(attachment, GLBlendMode(info.colorBlend.operation),
-                                     GLBlendMode(info.alphaBlend.operation));
-            glBlendFuncSeparatei(attachment, GLBlendFactor(info.colorBlend.srcFactor, false),
-                                 GLBlendFactor(info.colorBlend.dstFactor, false),
-                                 GLBlendFactor(info.alphaBlend.srcFactor, true),
-                                 GLBlendFactor(info.alphaBlend.dstFactor, true));
-        } else {
-            glDisablei(GL_BLEND, attachment);
-        }
-        glColorMaski(attachment, info.colorWriteMask & dawn::ColorWriteMask::Red,
-                     info.colorWriteMask & dawn::ColorWriteMask::Green,
-                     info.colorWriteMask & dawn::ColorWriteMask::Blue,
-                     info.colorWriteMask & dawn::ColorWriteMask::Alpha);
-    }
-
-}}  // namespace dawn_native::opengl
diff --git a/src/dawn_native/opengl/BlendStateGL.h b/src/dawn_native/opengl/BlendStateGL.h
deleted file mode 100644
index afc2bde..0000000
--- a/src/dawn_native/opengl/BlendStateGL.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef DAWNNATIVE_OPENGL_BLENDSTATEGL_H_
-#define DAWNNATIVE_OPENGL_BLENDSTATEGL_H_
-
-#include "dawn_native/BlendState.h"
-
-#include "glad/glad.h"
-
-namespace dawn_native { namespace opengl {
-
-    class BlendState : public BlendStateBase {
-      public:
-        BlendState(BlendStateBuilder* builder);
-
-        void ApplyNow(uint32_t attachment);
-    };
-
-}}  // namespace dawn_native::opengl
-
-#endif  // DAWNNATIVE_OPENGL_BLENDSTATEGL_H_
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index b1214e2..fec3004 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -18,7 +18,6 @@
 #include "dawn_native/BindGroupLayout.h"
 #include "dawn_native/OpenGLBackend.h"
 #include "dawn_native/RenderPassDescriptor.h"
-#include "dawn_native/opengl/BlendStateGL.h"
 #include "dawn_native/opengl/BufferGL.h"
 #include "dawn_native/opengl/CommandBufferGL.h"
 #include "dawn_native/opengl/ComputePipelineGL.h"
@@ -69,9 +68,6 @@
         const BindGroupLayoutDescriptor* descriptor) {
         return new BindGroupLayout(this, descriptor);
     }
-    BlendStateBase* Device::CreateBlendState(BlendStateBuilder* builder) {
-        return new BlendState(builder);
-    }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
         return new Buffer(this, descriptor);
     }
diff --git a/src/dawn_native/opengl/DeviceGL.h b/src/dawn_native/opengl/DeviceGL.h
index 164c8c0..8216035 100644
--- a/src/dawn_native/opengl/DeviceGL.h
+++ b/src/dawn_native/opengl/DeviceGL.h
@@ -40,7 +40,6 @@
         void SubmitFenceSync();
 
         // Dawn API
-        BlendStateBase* CreateBlendState(BlendStateBuilder* builder) override;
         CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
         DepthStencilStateBase* CreateDepthStencilState(DepthStencilStateBuilder* builder) override;
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
diff --git a/src/dawn_native/opengl/Forward.h b/src/dawn_native/opengl/Forward.h
index d9d1677..1c5942f 100644
--- a/src/dawn_native/opengl/Forward.h
+++ b/src/dawn_native/opengl/Forward.h
@@ -27,7 +27,6 @@
 
     using BindGroup = BindGroupBase;
     using BindGroupLayout = BindGroupLayoutBase;
-    class BlendState;
     class Buffer;
     class CommandBuffer;
     class ComputePipeline;
@@ -48,7 +47,6 @@
     struct OpenGLBackendTraits {
         using BindGroupType = BindGroup;
         using BindGroupLayoutType = BindGroupLayout;
-        using BlendStateType = BlendState;
         using BufferType = Buffer;
         using CommandBufferType = CommandBuffer;
         using ComputePipelineType = ComputePipeline;
diff --git a/src/dawn_native/opengl/RenderPipelineGL.cpp b/src/dawn_native/opengl/RenderPipelineGL.cpp
index 830a960..fec187a 100644
--- a/src/dawn_native/opengl/RenderPipelineGL.cpp
+++ b/src/dawn_native/opengl/RenderPipelineGL.cpp
@@ -14,7 +14,6 @@
 
 #include "dawn_native/opengl/RenderPipelineGL.h"
 
-#include "dawn_native/opengl/BlendStateGL.h"
 #include "dawn_native/opengl/DepthStencilStateGL.h"
 #include "dawn_native/opengl/DeviceGL.h"
 #include "dawn_native/opengl/Forward.h"
@@ -40,6 +39,76 @@
                     UNREACHABLE();
             }
         }
+
+        GLenum GLBlendFactor(dawn::BlendFactor factor, bool alpha) {
+            switch (factor) {
+                case dawn::BlendFactor::Zero:
+                    return GL_ZERO;
+                case dawn::BlendFactor::One:
+                    return GL_ONE;
+                case dawn::BlendFactor::SrcColor:
+                    return GL_SRC_COLOR;
+                case dawn::BlendFactor::OneMinusSrcColor:
+                    return GL_ONE_MINUS_SRC_COLOR;
+                case dawn::BlendFactor::SrcAlpha:
+                    return GL_SRC_ALPHA;
+                case dawn::BlendFactor::OneMinusSrcAlpha:
+                    return GL_ONE_MINUS_SRC_ALPHA;
+                case dawn::BlendFactor::DstColor:
+                    return GL_DST_COLOR;
+                case dawn::BlendFactor::OneMinusDstColor:
+                    return GL_ONE_MINUS_DST_COLOR;
+                case dawn::BlendFactor::DstAlpha:
+                    return GL_DST_ALPHA;
+                case dawn::BlendFactor::OneMinusDstAlpha:
+                    return GL_ONE_MINUS_DST_ALPHA;
+                case dawn::BlendFactor::SrcAlphaSaturated:
+                    return GL_SRC_ALPHA_SATURATE;
+                case dawn::BlendFactor::BlendColor:
+                    return alpha ? GL_CONSTANT_ALPHA : GL_CONSTANT_COLOR;
+                case dawn::BlendFactor::OneMinusBlendColor:
+                    return alpha ? GL_ONE_MINUS_CONSTANT_ALPHA : GL_ONE_MINUS_CONSTANT_COLOR;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        GLenum GLBlendMode(dawn::BlendOperation operation) {
+            switch (operation) {
+                case dawn::BlendOperation::Add:
+                    return GL_FUNC_ADD;
+                case dawn::BlendOperation::Subtract:
+                    return GL_FUNC_SUBTRACT;
+                case dawn::BlendOperation::ReverseSubtract:
+                    return GL_FUNC_REVERSE_SUBTRACT;
+                case dawn::BlendOperation::Min:
+                    return GL_MIN;
+                case dawn::BlendOperation::Max:
+                    return GL_MAX;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        void ApplyBlendState(uint32_t attachment, const BlendStateDescriptor* descriptor) {
+            if (descriptor->blendEnabled) {
+                glEnablei(GL_BLEND, attachment);
+                glBlendEquationSeparatei(attachment, GLBlendMode(descriptor->colorBlend.operation),
+                                         GLBlendMode(descriptor->alphaBlend.operation));
+                glBlendFuncSeparatei(attachment,
+                                     GLBlendFactor(descriptor->colorBlend.srcFactor, false),
+                                     GLBlendFactor(descriptor->colorBlend.dstFactor, false),
+                                     GLBlendFactor(descriptor->alphaBlend.srcFactor, true),
+                                     GLBlendFactor(descriptor->alphaBlend.dstFactor, true));
+            } else {
+                glDisablei(GL_BLEND, attachment);
+            }
+            glColorMaski(attachment, descriptor->colorWriteMask & dawn::ColorWriteMask::Red,
+                         descriptor->colorWriteMask & dawn::ColorWriteMask::Green,
+                         descriptor->colorWriteMask & dawn::ColorWriteMask::Blue,
+                         descriptor->colorWriteMask & dawn::ColorWriteMask::Alpha);
+        }
+
     }  // namespace
 
     RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
@@ -66,7 +135,7 @@
         depthStencilState->ApplyNow(persistentPipelineState);
 
         for (uint32_t attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) {
-            ToBackend(GetBlendState(attachmentSlot))->ApplyNow(attachmentSlot);
+            ApplyBlendState(attachmentSlot, GetBlendStateDescriptor(attachmentSlot));
         }
     }
 
diff --git a/src/dawn_native/vulkan/BlendStateVk.cpp b/src/dawn_native/vulkan/BlendStateVk.cpp
deleted file mode 100644
index cc4911b..0000000
--- a/src/dawn_native/vulkan/BlendStateVk.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2018 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "dawn_native/vulkan/BlendStateVk.h"
-
-#include "common/Assert.h"
-
-namespace dawn_native { namespace vulkan {
-
-    namespace {
-        VkBlendFactor VulkanBlendFactor(dawn::BlendFactor factor) {
-            switch (factor) {
-                case dawn::BlendFactor::Zero:
-                    return VK_BLEND_FACTOR_ZERO;
-                case dawn::BlendFactor::One:
-                    return VK_BLEND_FACTOR_ONE;
-                case dawn::BlendFactor::SrcColor:
-                    return VK_BLEND_FACTOR_SRC_COLOR;
-                case dawn::BlendFactor::OneMinusSrcColor:
-                    return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
-                case dawn::BlendFactor::SrcAlpha:
-                    return VK_BLEND_FACTOR_SRC_ALPHA;
-                case dawn::BlendFactor::OneMinusSrcAlpha:
-                    return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-                case dawn::BlendFactor::DstColor:
-                    return VK_BLEND_FACTOR_DST_COLOR;
-                case dawn::BlendFactor::OneMinusDstColor:
-                    return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
-                case dawn::BlendFactor::DstAlpha:
-                    return VK_BLEND_FACTOR_DST_ALPHA;
-                case dawn::BlendFactor::OneMinusDstAlpha:
-                    return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
-                case dawn::BlendFactor::SrcAlphaSaturated:
-                    return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
-                case dawn::BlendFactor::BlendColor:
-                    return VK_BLEND_FACTOR_CONSTANT_COLOR;
-                case dawn::BlendFactor::OneMinusBlendColor:
-                    return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
-                default:
-                    UNREACHABLE();
-            }
-        }
-
-        VkBlendOp VulkanBlendOperation(dawn::BlendOperation operation) {
-            switch (operation) {
-                case dawn::BlendOperation::Add:
-                    return VK_BLEND_OP_ADD;
-                case dawn::BlendOperation::Subtract:
-                    return VK_BLEND_OP_SUBTRACT;
-                case dawn::BlendOperation::ReverseSubtract:
-                    return VK_BLEND_OP_REVERSE_SUBTRACT;
-                case dawn::BlendOperation::Min:
-                    return VK_BLEND_OP_MIN;
-                case dawn::BlendOperation::Max:
-                    return VK_BLEND_OP_MAX;
-                default:
-                    UNREACHABLE();
-            }
-        }
-
-        VkColorComponentFlagBits VulkanColorWriteMask(dawn::ColorWriteMask mask) {
-            // Vulkan and Dawn color write masks match, static assert it and return the mask
-            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Red) ==
-                              VK_COLOR_COMPONENT_R_BIT,
-                          "");
-            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Green) ==
-                              VK_COLOR_COMPONENT_G_BIT,
-                          "");
-            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Blue) ==
-                              VK_COLOR_COMPONENT_B_BIT,
-                          "");
-            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Alpha) ==
-                              VK_COLOR_COMPONENT_A_BIT,
-                          "");
-
-            return static_cast<VkColorComponentFlagBits>(mask);
-        }
-    }  // anonymous namespace
-
-    BlendState::BlendState(BlendStateBuilder* builder) : BlendStateBase(builder) {
-        // Fill the "color blend attachment info" that will be copied in an array and chained in
-        // the pipeline create info.
-        const auto& info = GetBlendInfo();
-
-        mState.blendEnable = info.blendEnabled ? VK_TRUE : VK_FALSE;
-        mState.srcColorBlendFactor = VulkanBlendFactor(info.colorBlend.srcFactor);
-        mState.dstColorBlendFactor = VulkanBlendFactor(info.colorBlend.dstFactor);
-        mState.colorBlendOp = VulkanBlendOperation(info.colorBlend.operation);
-        mState.srcAlphaBlendFactor = VulkanBlendFactor(info.alphaBlend.srcFactor);
-        mState.dstAlphaBlendFactor = VulkanBlendFactor(info.alphaBlend.dstFactor);
-        mState.alphaBlendOp = VulkanBlendOperation(info.alphaBlend.operation);
-        mState.colorWriteMask = VulkanColorWriteMask(info.colorWriteMask);
-    }
-
-    const VkPipelineColorBlendAttachmentState& BlendState::GetState() const {
-        return mState;
-    }
-
-}}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/BlendStateVk.h b/src/dawn_native/vulkan/BlendStateVk.h
deleted file mode 100644
index fd02570..0000000
--- a/src/dawn_native/vulkan/BlendStateVk.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef DAWNNATIVE_VULKAN_BLENDSTATEVK_H_
-#define DAWNNATIVE_VULKAN_BLENDSTATEVK_H_
-
-#include "dawn_native/BlendState.h"
-
-#include "common/vulkan_platform.h"
-
-namespace dawn_native { namespace vulkan {
-
-    class Device;
-
-    // Pre-computes the blend state configuration to give to a graphics pipeline create info.
-    class BlendState : public BlendStateBase {
-      public:
-        BlendState(BlendStateBuilder* builder);
-
-        const VkPipelineColorBlendAttachmentState& GetState() const;
-
-      private:
-        VkPipelineColorBlendAttachmentState mState;
-    };
-
-}}  // namespace dawn_native::vulkan
-
-#endif  // DAWNNATIVE_VULKAN_BLENDSTATEVK_H_
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index b315cd8..3e78b5e 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -21,7 +21,6 @@
 #include "dawn_native/VulkanBackend.h"
 #include "dawn_native/vulkan/BindGroupLayoutVk.h"
 #include "dawn_native/vulkan/BindGroupVk.h"
-#include "dawn_native/vulkan/BlendStateVk.h"
 #include "dawn_native/vulkan/BufferUploader.h"
 #include "dawn_native/vulkan/BufferVk.h"
 #include "dawn_native/vulkan/CommandBufferVk.h"
@@ -228,9 +227,6 @@
         const BindGroupLayoutDescriptor* descriptor) {
         return new BindGroupLayout(this, descriptor);
     }
-    BlendStateBase* Device::CreateBlendState(BlendStateBuilder* builder) {
-        return new BlendState(builder);
-    }
     ResultOrError<BufferBase*> Device::CreateBufferImpl(const BufferDescriptor* descriptor) {
         return new Buffer(this, descriptor);
     }
diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h
index 4e371bb..086b706 100644
--- a/src/dawn_native/vulkan/DeviceVk.h
+++ b/src/dawn_native/vulkan/DeviceVk.h
@@ -63,7 +63,6 @@
         void AddWaitSemaphore(VkSemaphore semaphore);
 
         // Dawn API
-        BlendStateBase* CreateBlendState(BlendStateBuilder* builder) override;
         CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
         DepthStencilStateBase* CreateDepthStencilState(DepthStencilStateBuilder* builder) override;
         InputStateBase* CreateInputState(InputStateBuilder* builder) override;
diff --git a/src/dawn_native/vulkan/Forward.h b/src/dawn_native/vulkan/Forward.h
index 6a4bdb6..3e43e32 100644
--- a/src/dawn_native/vulkan/Forward.h
+++ b/src/dawn_native/vulkan/Forward.h
@@ -21,7 +21,6 @@
 
     class BindGroup;
     class BindGroupLayout;
-    class BlendState;
     class Buffer;
     class CommandBuffer;
     class ComputePipeline;
@@ -41,7 +40,6 @@
     struct VulkanBackendTraits {
         using BindGroupType = BindGroup;
         using BindGroupLayoutType = BindGroupLayout;
-        using BlendStateType = BlendState;
         using BufferType = Buffer;
         using CommandBufferType = CommandBuffer;
         using ComputePipelineType = ComputePipeline;
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.cpp b/src/dawn_native/vulkan/RenderPipelineVk.cpp
index 707f66c..a1ef014 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn_native/vulkan/RenderPipelineVk.cpp
@@ -14,7 +14,6 @@
 
 #include "dawn_native/vulkan/RenderPipelineVk.h"
 
-#include "dawn_native/vulkan/BlendStateVk.h"
 #include "dawn_native/vulkan/DepthStencilStateVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
@@ -45,6 +44,88 @@
             }
         }
 
+        VkBlendFactor VulkanBlendFactor(dawn::BlendFactor factor) {
+            switch (factor) {
+                case dawn::BlendFactor::Zero:
+                    return VK_BLEND_FACTOR_ZERO;
+                case dawn::BlendFactor::One:
+                    return VK_BLEND_FACTOR_ONE;
+                case dawn::BlendFactor::SrcColor:
+                    return VK_BLEND_FACTOR_SRC_COLOR;
+                case dawn::BlendFactor::OneMinusSrcColor:
+                    return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
+                case dawn::BlendFactor::SrcAlpha:
+                    return VK_BLEND_FACTOR_SRC_ALPHA;
+                case dawn::BlendFactor::OneMinusSrcAlpha:
+                    return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+                case dawn::BlendFactor::DstColor:
+                    return VK_BLEND_FACTOR_DST_COLOR;
+                case dawn::BlendFactor::OneMinusDstColor:
+                    return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
+                case dawn::BlendFactor::DstAlpha:
+                    return VK_BLEND_FACTOR_DST_ALPHA;
+                case dawn::BlendFactor::OneMinusDstAlpha:
+                    return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
+                case dawn::BlendFactor::SrcAlphaSaturated:
+                    return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
+                case dawn::BlendFactor::BlendColor:
+                    return VK_BLEND_FACTOR_CONSTANT_COLOR;
+                case dawn::BlendFactor::OneMinusBlendColor:
+                    return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        VkBlendOp VulkanBlendOperation(dawn::BlendOperation operation) {
+            switch (operation) {
+                case dawn::BlendOperation::Add:
+                    return VK_BLEND_OP_ADD;
+                case dawn::BlendOperation::Subtract:
+                    return VK_BLEND_OP_SUBTRACT;
+                case dawn::BlendOperation::ReverseSubtract:
+                    return VK_BLEND_OP_REVERSE_SUBTRACT;
+                case dawn::BlendOperation::Min:
+                    return VK_BLEND_OP_MIN;
+                case dawn::BlendOperation::Max:
+                    return VK_BLEND_OP_MAX;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        VkColorComponentFlagBits VulkanColorWriteMask(dawn::ColorWriteMask mask) {
+            // Vulkan and Dawn color write masks match, static assert it and return the mask
+            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Red) ==
+                              VK_COLOR_COMPONENT_R_BIT,
+                          "");
+            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Green) ==
+                              VK_COLOR_COMPONENT_G_BIT,
+                          "");
+            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Blue) ==
+                              VK_COLOR_COMPONENT_B_BIT,
+                          "");
+            static_assert(static_cast<VkColorComponentFlagBits>(dawn::ColorWriteMask::Alpha) ==
+                              VK_COLOR_COMPONENT_A_BIT,
+                          "");
+
+            return static_cast<VkColorComponentFlagBits>(mask);
+        }
+
+        VkPipelineColorBlendAttachmentState ComputeBlendDesc(
+            const BlendStateDescriptor* descriptor) {
+            VkPipelineColorBlendAttachmentState attachment;
+            attachment.blendEnable = descriptor->blendEnabled ? VK_TRUE : VK_FALSE;
+            attachment.srcColorBlendFactor = VulkanBlendFactor(descriptor->colorBlend.srcFactor);
+            attachment.dstColorBlendFactor = VulkanBlendFactor(descriptor->colorBlend.dstFactor);
+            attachment.colorBlendOp = VulkanBlendOperation(descriptor->colorBlend.operation);
+            attachment.srcAlphaBlendFactor = VulkanBlendFactor(descriptor->alphaBlend.srcFactor);
+            attachment.dstAlphaBlendFactor = VulkanBlendFactor(descriptor->alphaBlend.dstFactor);
+            attachment.alphaBlendOp = VulkanBlendOperation(descriptor->alphaBlend.operation);
+            attachment.colorWriteMask = VulkanColorWriteMask(descriptor->colorWriteMask);
+            return attachment;
+        }
+
     }  // anonymous namespace
 
     RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
@@ -133,7 +214,8 @@
         // pre-computed in the BlendState
         std::array<VkPipelineColorBlendAttachmentState, kMaxColorAttachments> colorBlendAttachments;
         for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
-            colorBlendAttachments[i] = ToBackend(GetBlendState(i))->GetState();
+            const BlendStateDescriptor* descriptor = GetBlendStateDescriptor(i);
+            colorBlendAttachments[i] = ComputeBlendDesc(descriptor);
         }
         VkPipelineColorBlendStateCreateInfo colorBlend;
         colorBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
diff --git a/src/tests/end2end/BlendStateTests.cpp b/src/tests/end2end/BlendStateTests.cpp
index 1fbde36..d1722ff 100644
--- a/src/tests/end2end/BlendStateTests.cpp
+++ b/src/tests/end2end/BlendStateTests.cpp
@@ -53,7 +53,7 @@
         };
 
         // Set up basePipeline and testPipeline. testPipeline has the given blend state on the first attachment. basePipeline has no blending
-        void SetupSingleSourcePipelines(const dawn::BlendState &blendState) {
+        void SetupSingleSourcePipelines(const dawn::BlendStateDescriptor& blendStateDescriptor) {
             dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
                 #version 450
                 layout(set = 0, binding = 0) uniform myBlock {
@@ -82,7 +82,7 @@
             testDescriptor.cFragmentStage.module = fsModule;
             testDescriptor.cColorAttachments[0].format =
                 renderPass.colorFormat;
-            testDescriptor.cBlendStates[0] = blendState;
+            testDescriptor.cBlendStates[0] = blendStateDescriptor;
 
             testPipeline = device.CreateRenderPipeline(&testDescriptor);
         }
@@ -135,13 +135,14 @@
             blend.srcFactor = dawn::BlendFactor::One;
             blend.dstFactor = dawn::BlendFactor::One;
 
-            dawn::BlendState blendState = device.CreateBlendStateBuilder()
-                .SetBlendEnabled(true)
-                .SetColorBlend(&blend)
-                .SetAlphaBlend(&blend)
-                .GetResult();
+            dawn::BlendStateDescriptor descriptor;
+            descriptor.blendEnabled = true;
+            descriptor.alphaBlend = blend;
+            descriptor.colorBlend = blend;
+            descriptor.nextInChain = nullptr;
+            descriptor.colorWriteMask = dawn::ColorWriteMask::All;
 
-            SetupSingleSourcePipelines(blendState);
+            SetupSingleSourcePipelines(descriptor);
 
             for (const auto& test : tests) {
                 DoSingleSourceTest(base, { test.first }, test.second);
@@ -160,13 +161,14 @@
             alphaBlend.srcFactor = alphaSrcFactor;
             alphaBlend.dstFactor = alphaDstFactor;
 
-            dawn::BlendState blendState = device.CreateBlendStateBuilder()
-                .SetBlendEnabled(true)
-                .SetColorBlend(&colorBlend)
-                .SetAlphaBlend(&alphaBlend)
-                .GetResult();
+            dawn::BlendStateDescriptor descriptor;
+            descriptor.blendEnabled = true;
+            descriptor.colorBlend = colorBlend;
+            descriptor.alphaBlend = alphaBlend;
+            descriptor.nextInChain = nullptr;
+            descriptor.colorWriteMask = dawn::ColorWriteMask::All;
 
-            SetupSingleSourcePipelines(blendState);
+            SetupSingleSourcePipelines(descriptor);
 
             for (const auto& test : tests) {
                 DoSingleSourceTest(base, test.first, test.second);
@@ -276,8 +278,18 @@
 
 // Test compilation and usage of the fixture
 TEST_P(BlendStateTest, Basic) {
-    dawn::BlendState blendState = device.CreateBlendStateBuilder().GetResult();
-    SetupSingleSourcePipelines(blendState);
+    dawn::BlendDescriptor blend;
+    blend.operation = dawn::BlendOperation::Add;
+    blend.srcFactor = dawn::BlendFactor::One;
+    blend.dstFactor = dawn::BlendFactor::Zero;
+    dawn::BlendStateDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.blendEnabled = false;
+    descriptor.alphaBlend = blend;
+    descriptor.colorBlend = blend;
+    descriptor.colorWriteMask = dawn::ColorWriteMask::All;
+
+    SetupSingleSourcePipelines(descriptor);
 
     DoSingleSourceTest(RGBA8(0, 0, 0, 0), { RGBA8(255, 0, 0, 0) }, RGBA8(255, 0, 0, 0));
 }
@@ -627,15 +639,15 @@
     blend.srcFactor = dawn::BlendFactor::One;
     blend.dstFactor = dawn::BlendFactor::One;
 
+    dawn::BlendStateDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.blendEnabled = true;
+    descriptor.colorBlend = blend;
+    descriptor.alphaBlend = blend;
     {
         // Test single channel color write
-        dawn::BlendState blendState = device.CreateBlendStateBuilder()
-            .SetBlendEnabled(true)
-            .SetColorBlend(&blend)
-            .SetAlphaBlend(&blend)
-            .SetColorWriteMask(dawn::ColorWriteMask::Red)
-            .GetResult();
-        SetupSingleSourcePipelines(blendState);
+        descriptor.colorWriteMask = dawn::ColorWriteMask::Red;
+        SetupSingleSourcePipelines(descriptor);
 
         RGBA8 base(32, 64, 128, 192);
         for (auto& color : kColors) {
@@ -646,13 +658,8 @@
 
     {
         // Test multi channel color write
-        dawn::BlendState blendState = device.CreateBlendStateBuilder()
-            .SetBlendEnabled(true)
-            .SetColorBlend(&blend)
-            .SetAlphaBlend(&blend)
-            .SetColorWriteMask(dawn::ColorWriteMask::Green | dawn::ColorWriteMask::Alpha)
-            .GetResult();
-        SetupSingleSourcePipelines(blendState);
+        descriptor.colorWriteMask = dawn::ColorWriteMask::Green | dawn::ColorWriteMask::Alpha;
+        SetupSingleSourcePipelines(descriptor);
 
         RGBA8 base(32, 64, 128, 192);
         for (auto& color : kColors) {
@@ -663,13 +670,8 @@
 
     {
         // Test no channel color write
-        dawn::BlendState blendState = device.CreateBlendStateBuilder()
-            .SetBlendEnabled(true)
-            .SetColorBlend(&blend)
-            .SetAlphaBlend(&blend)
-            .SetColorWriteMask(dawn::ColorWriteMask::None)
-            .GetResult();
-        SetupSingleSourcePipelines(blendState);
+        descriptor.colorWriteMask = dawn::ColorWriteMask::None;
+        SetupSingleSourcePipelines(descriptor);
 
         RGBA8 base(32, 64, 128, 192);
         for (auto& color : kColors) {
@@ -681,11 +683,18 @@
 // Check that the color write mask works when blending is disabled
 TEST_P(BlendStateTest, ColorWriteMaskBlendingDisabled) {
     {
-        dawn::BlendState blendState = device.CreateBlendStateBuilder()
-            .SetBlendEnabled(false)
-            .SetColorWriteMask(dawn::ColorWriteMask::Red)
-            .GetResult();
-        SetupSingleSourcePipelines(blendState);
+        dawn::BlendDescriptor blend;
+        blend.operation = dawn::BlendOperation::Add;
+        blend.srcFactor = dawn::BlendFactor::One;
+        blend.dstFactor = dawn::BlendFactor::Zero;
+        dawn::BlendStateDescriptor descriptor;
+        descriptor.nextInChain = nullptr;
+        descriptor.alphaBlend = blend;
+        descriptor.colorBlend = blend;
+
+        descriptor.blendEnabled = false;
+        descriptor.colorWriteMask = dawn::ColorWriteMask::Red;
+        SetupSingleSourcePipelines(descriptor);
 
         RGBA8 base(32, 64, 128, 192);
         RGBA8 expected(32, 0, 0, 0);
@@ -763,40 +772,6 @@
         }
     )");
 
-    dawn::BlendDescriptor blend1;
-    blend1.operation = dawn::BlendOperation::Add;
-    blend1.srcFactor = dawn::BlendFactor::One;
-    blend1.dstFactor = dawn::BlendFactor::One;
-
-    dawn::BlendDescriptor blend2;
-    blend2.operation = dawn::BlendOperation::Subtract;
-    blend2.srcFactor = dawn::BlendFactor::One;
-    blend2.dstFactor = dawn::BlendFactor::One;
-
-    dawn::BlendDescriptor blend3;
-    blend3.operation = dawn::BlendOperation::Min;
-    blend3.srcFactor = dawn::BlendFactor::One;
-    blend3.dstFactor = dawn::BlendFactor::One;
-
-    std::array<dawn::BlendState, 4> blendStates = { {
-        device.CreateBlendStateBuilder()
-            .SetBlendEnabled(true)
-            .SetColorBlend(&blend1)
-            .SetAlphaBlend(&blend1)
-            .GetResult(),
-        device.CreateBlendStateBuilder()
-            .SetBlendEnabled(true)
-            .SetColorBlend(&blend2)
-            .SetAlphaBlend(&blend2)
-            .GetResult(),
-        device.CreateBlendStateBuilder().GetResult(),
-        device.CreateBlendStateBuilder()
-            .SetBlendEnabled(true)
-            .SetColorBlend(&blend3)
-            .SetAlphaBlend(&blend3)
-            .GetResult(),
-    } };
-
     utils::ComboRenderPipelineDescriptor baseDescriptor(device);
     baseDescriptor.layout = pipelineLayout;
     baseDescriptor.cVertexStage.module = vsModule;
@@ -812,7 +787,34 @@
     testDescriptor.cFragmentStage.module = fsModule;
     testDescriptor.cAttachmentsState.numColorAttachments = 4;
     testDescriptor.numBlendStates = 4;
-    testDescriptor.blendStates = blendStates.data();
+
+    // set blend states
+    dawn::BlendDescriptor blend1;
+    blend1.operation = dawn::BlendOperation::Add;
+    blend1.srcFactor = dawn::BlendFactor::One;
+    blend1.dstFactor = dawn::BlendFactor::One;
+
+    dawn::BlendDescriptor blend2;
+    blend2.operation = dawn::BlendOperation::Subtract;
+    blend2.srcFactor = dawn::BlendFactor::One;
+    blend2.dstFactor = dawn::BlendFactor::One;
+
+    dawn::BlendDescriptor blend3;
+    blend3.operation = dawn::BlendOperation::Min;
+    blend3.srcFactor = dawn::BlendFactor::One;
+    blend3.dstFactor = dawn::BlendFactor::One;
+
+    testDescriptor.cBlendStates[0].blendEnabled = true;
+    testDescriptor.cBlendStates[0].colorBlend = blend1;
+    testDescriptor.cBlendStates[0].alphaBlend = blend1;
+
+    testDescriptor.cBlendStates[1].blendEnabled = true;
+    testDescriptor.cBlendStates[1].colorBlend = blend2;
+    testDescriptor.cBlendStates[1].alphaBlend = blend2;
+
+    testDescriptor.cBlendStates[3].blendEnabled = true;
+    testDescriptor.cBlendStates[3].colorBlend = blend3;
+    testDescriptor.cBlendStates[3].alphaBlend = blend3;
 
     testPipeline = device.CreateRenderPipeline(&testDescriptor);
 
@@ -853,17 +855,6 @@
 
 // Test that the default blend color is correctly set at the beginning of every subpass
 TEST_P(BlendStateTest, DefaultBlendColor) {
-    dawn::BlendDescriptor blend;
-    blend.operation = dawn::BlendOperation::Add;
-    blend.srcFactor = dawn::BlendFactor::BlendColor;
-    blend.dstFactor = dawn::BlendFactor::One;
-
-    dawn::BlendState blendState = device.CreateBlendStateBuilder()
-        .SetBlendEnabled(true)
-        .SetColorBlend(&blend)
-        .SetAlphaBlend(&blend)
-        .GetResult();
-
     dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
         #version 450
         layout(set = 0, binding = 0) uniform myBlock {
@@ -892,7 +883,14 @@
     testDescriptor.cFragmentStage.module = fsModule;
     testDescriptor.cColorAttachments[0].format =
         renderPass.colorFormat;
-    testDescriptor.cBlendStates[0] = blendState;
+
+    dawn::BlendDescriptor blend;
+    blend.operation = dawn::BlendOperation::Add;
+    blend.srcFactor = dawn::BlendFactor::BlendColor;
+    blend.dstFactor = dawn::BlendFactor::One;
+    testDescriptor.cBlendStates[0].blendEnabled = true;
+    testDescriptor.cBlendStates[0].colorBlend = blend;
+    testDescriptor.cBlendStates[0].alphaBlend = blend;
 
     testPipeline = device.CreateRenderPipeline(&testDescriptor);
 
diff --git a/src/tests/end2end/PushConstantTests.cpp b/src/tests/end2end/PushConstantTests.cpp
index ebbd2ad..5d975ee 100644
--- a/src/tests/end2end/PushConstantTests.cpp
+++ b/src/tests/end2end/PushConstantTests.cpp
@@ -185,21 +185,19 @@
                 })").c_str()
             );
 
-            dawn::BlendDescriptor blend;
-            blend.operation = dawn::BlendOperation::Add;
-            blend.srcFactor = dawn::BlendFactor::One;
-            blend.dstFactor = dawn::BlendFactor::One;
-
             utils::ComboRenderPipelineDescriptor descriptor(device);
             descriptor.layout = layout;
             descriptor.cVertexStage.module = vsModule;
             descriptor.cFragmentStage.module = fsModule;
             descriptor.primitiveTopology = dawn::PrimitiveTopology::PointList;
-            descriptor.cBlendStates[0] = device.CreateBlendStateBuilder()
-                                         .SetBlendEnabled(true)
-                                         .SetColorBlend(&blend)
-                                         .SetAlphaBlend(&blend)
-                                         .GetResult();
+
+            dawn::BlendDescriptor blend;
+            blend.operation = dawn::BlendOperation::Add;
+            blend.srcFactor = dawn::BlendFactor::One;
+            blend.dstFactor = dawn::BlendFactor::One;
+            descriptor.cBlendStates[0].blendEnabled = true;
+            descriptor.cBlendStates[0].alphaBlend = blend;
+            descriptor.cBlendStates[0].colorBlend = blend;
 
             return device.CreateRenderPipeline(&descriptor);
         }
diff --git a/src/tests/unittests/WireTests.cpp b/src/tests/unittests/WireTests.cpp
index 930f19e..6812271 100644
--- a/src/tests/unittests/WireTests.cpp
+++ b/src/tests/unittests/WireTests.cpp
@@ -326,16 +326,17 @@
     EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _))
         .WillOnce(Return(apiVsModule));
 
-    // Create the blend state
-    dawnBlendStateBuilder blendStateBuilder = dawnDeviceCreateBlendStateBuilder(device);
-    dawnBlendStateBuilder apiBlendStateBuilder = api.GetNewBlendStateBuilder();
-    EXPECT_CALL(api, DeviceCreateBlendStateBuilder(apiDevice))
-        .WillOnce(Return(apiBlendStateBuilder));
-
-    dawnBlendState blendState = dawnBlendStateBuilderGetResult(blendStateBuilder);
-    dawnBlendState apiBlendState = api.GetNewBlendState();
-    EXPECT_CALL(api, BlendStateBuilderGetResult(apiBlendStateBuilder))
-        .WillOnce(Return(apiBlendState));
+    // Create the blend state descriptor
+    dawnBlendDescriptor blendDescriptor;
+    blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
+    blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
+    blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
+    dawnBlendStateDescriptor blendStateDescriptor;
+    blendStateDescriptor.nextInChain = nullptr;
+    blendStateDescriptor.blendEnabled = false;
+    blendStateDescriptor.alphaBlend = blendDescriptor;
+    blendStateDescriptor.colorBlend = blendDescriptor;
+    blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
 
     // Create the input state
     dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device);
@@ -397,7 +398,7 @@
     pipelineDescriptor.attachmentsState = &attachmentsState;
 
     pipelineDescriptor.numBlendStates = 1;
-    pipelineDescriptor.blendStates =  &blendState;
+    pipelineDescriptor.blendStates = &blendStateDescriptor;
 
     pipelineDescriptor.sampleCount = 1;
     pipelineDescriptor.layout = layout;
diff --git a/src/tests/unittests/validation/BlendStateValidationTests.cpp b/src/tests/unittests/validation/BlendStateValidationTests.cpp
deleted file mode 100644
index 030cc63..0000000
--- a/src/tests/unittests/validation/BlendStateValidationTests.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2017 The Dawn Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "tests/unittests/validation/ValidationTest.h"
-
-class BlendStateValidationTest : public ValidationTest {
-};
-
-// Test cases where creation should succeed
-TEST_F(BlendStateValidationTest, CreationSuccess) {
-    // Success for setting all properties
-    {
-        dawn::BlendDescriptor blend;
-        blend.operation = dawn::BlendOperation::Add;
-        blend.srcFactor = dawn::BlendFactor::One;
-        blend.dstFactor = dawn::BlendFactor::One;
-
-        dawn::BlendState state = AssertWillBeSuccess(device.CreateBlendStateBuilder())
-            .SetBlendEnabled(true)
-            .SetAlphaBlend(&blend)
-            .SetColorBlend(&blend)
-            .SetColorWriteMask(dawn::ColorWriteMask::Red)
-            .GetResult();
-    }
-
-    // Success for empty builder
-    {
-        dawn::BlendState state = AssertWillBeSuccess(device.CreateBlendStateBuilder())
-            .GetResult();
-    }
-}
-
-// Test creation failure when specifying properties multiple times
-TEST_F(BlendStateValidationTest, CreationDuplicates) {
-    // Test failure when specifying blend enabled multiple times
-    {
-        dawn::BlendState state = AssertWillBeError(device.CreateBlendStateBuilder())
-            .SetBlendEnabled(true)
-            .SetBlendEnabled(false)
-            .GetResult();
-    }
-
-    dawn::BlendDescriptor blend1;
-    blend1.operation = dawn::BlendOperation::Add;
-    blend1.srcFactor = dawn::BlendFactor::One;
-    blend1.dstFactor = dawn::BlendFactor::One;
-
-    dawn::BlendDescriptor blend2;
-    blend2.operation = dawn::BlendOperation::Add;
-    blend2.srcFactor = dawn::BlendFactor::Zero;
-    blend2.dstFactor = dawn::BlendFactor::Zero;
-
-    // Test failure when specifying alpha blend multiple times
-    {
-        dawn::BlendState state = AssertWillBeError(device.CreateBlendStateBuilder())
-            .SetAlphaBlend(&blend1)
-            .SetAlphaBlend(&blend2)
-            .GetResult();
-    }
-
-    // Test failure when specifying color blend multiple times
-    {
-        dawn::BlendState state = AssertWillBeError(device.CreateBlendStateBuilder())
-            .SetColorBlend(&blend1)
-            .SetColorBlend(&blend2)
-            .GetResult();
-    }
-
-    // Test failure when specifying color write mask multiple times
-    {
-        dawn::BlendState state = AssertWillBeError(device.CreateBlendStateBuilder())
-            .SetColorWriteMask(dawn::ColorWriteMask::Red)
-            .SetColorWriteMask(dawn::ColorWriteMask::Green)
-            .GetResult();
-    }
-
-}
diff --git a/src/utils/ComboRenderPipelineDescriptor.cpp b/src/utils/ComboRenderPipelineDescriptor.cpp
index 66b3105..ac799b3 100644
--- a/src/utils/ComboRenderPipelineDescriptor.cpp
+++ b/src/utils/ComboRenderPipelineDescriptor.cpp
@@ -41,7 +41,7 @@
         {
             descriptor->attachmentsState = &cAttachmentsState;
             cAttachmentsState.numColorAttachments = 1;
-            cAttachmentsState.colorAttachments = cColorAttachments;
+            cAttachmentsState.colorAttachments = &cColorAttachments[0];
             cAttachmentsState.depthStencilAttachment = &cDepthStencilAttachment;
             cAttachmentsState.hasDepthStencilAttachment = false;
 
@@ -55,10 +55,20 @@
         descriptor->layout = utils::MakeBasicPipelineLayout(device, nullptr);
 
         descriptor->numBlendStates = 1;
-        descriptor->blendStates = cBlendStates;
+        descriptor->blendStates = &cBlendStates[0];
 
+        dawn::BlendDescriptor blend;
+        blend.operation = dawn::BlendOperation::Add;
+        blend.srcFactor = dawn::BlendFactor::One;
+        blend.dstFactor = dawn::BlendFactor::Zero;
+        dawn::BlendStateDescriptor blendStateDescriptor;
+        blendStateDescriptor.nextInChain = nullptr;
+        blendStateDescriptor.blendEnabled = false;
+        blendStateDescriptor.alphaBlend = blend;
+        blendStateDescriptor.colorBlend = blend;
+        blendStateDescriptor.colorWriteMask = dawn::ColorWriteMask::All;
         for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
-            cBlendStates[i] = device.CreateBlendStateBuilder().GetResult();
+            cBlendStates[i] = blendStateDescriptor;
         }
     }
 
diff --git a/src/utils/ComboRenderPipelineDescriptor.h b/src/utils/ComboRenderPipelineDescriptor.h
index 47030a4..f9c7ffa 100644
--- a/src/utils/ComboRenderPipelineDescriptor.h
+++ b/src/utils/ComboRenderPipelineDescriptor.h
@@ -19,6 +19,8 @@
 

 #include "common/Constants.h"

 

+#include <array>

+

 namespace utils {

 

     class ComboRenderPipelineDescriptor : public dawn::RenderPipelineDescriptor {

@@ -29,11 +31,11 @@
         dawn::PipelineStageDescriptor cFragmentStage;

 

         dawn::AttachmentsStateDescriptor cAttachmentsState;

-        dawn::AttachmentDescriptor cColorAttachments[kMaxColorAttachments];

+        std::array<dawn::AttachmentDescriptor, kMaxColorAttachments> cColorAttachments;

         dawn::AttachmentDescriptor cDepthStencilAttachment;

-        dawn::BlendState cBlendStates[kMaxColorAttachments];

+        std::array<dawn::BlendStateDescriptor, kMaxColorAttachments> cBlendStates;

     };

 

 }  // namespace utils

 

-#endif  // UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_
\ No newline at end of file
+#endif  // UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_