{"action":"created","comment":{"url":"https://api.github.com/repos/google/nxt-standalone/pulls/comments/131238247","pull_request_review_id":54183679,"id":131238247,"diff_hunk":"@@ -0,0 +1,1055 @@\n+\n+// Copyright 2017 The NXT Authors\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+\n+#include <array>\n+\n+#include \"tests/NXTTest.h\"\n+\n+#include \"common/Assert.h\"\n+#include \"common/Constants.h\"\n+#include \"utils/NXTHelpers.h\"\n+\n+constexpr static unsigned int kRTSize = 64;\n+\n+class BlendStateTest : public NXTTest {\n+    protected:\n+        void SetUp() override {\n+            NXTTest::SetUp();\n+\n+            vsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Vertex, R\"(\n+                #version 450\n+                void main() {\n+                    const vec2 pos[3] = vec2[3](vec2(0.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f));\n+                    gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);\n+                }\n+            )\");\n+\n+\n+            bindGroupLayout = device.CreateBindGroupLayoutBuilder()\n+                .SetBindingsType(nxt::ShaderStageBit::Fragment, nxt::BindingType::UniformBuffer, 0, 1)\n+                .GetResult();\n+\n+            pipelineLayout = device.CreatePipelineLayoutBuilder()\n+                .SetBindGroupLayout(0, bindGroupLayout)\n+                .GetResult();\n+\n+            renderTarget = device.CreateTextureBuilder()\n+                .SetDimension(nxt::TextureDimension::e2D)\n+                .SetExtent(kRTSize, kRTSize, 1)\n+                .SetFormat(nxt::TextureFormat::R8G8B8A8Unorm)\n+                .SetMipLevels(1)\n+                .SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment | nxt::TextureUsageBit::TransferSrc)\n+                .SetInitialUsage(nxt::TextureUsageBit::OutputAttachment)\n+                .GetResult();\n+\n+            renderTargetView = renderTarget.CreateTextureViewBuilder().GetResult();\n+\n+            renderpass = device.CreateRenderPassBuilder()\n+                .SetAttachmentCount(1)\n+                .SetSubpassCount(1)\n+                .AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm)\n+                .SubpassSetColorAttachment(0, 0, 0)\n+                .GetResult();\n+\n+            framebuffer = device.CreateFramebufferBuilder()\n+                .SetRenderPass(renderpass)\n+                .SetDimensions(kRTSize, kRTSize)\n+                .SetAttachment(0, renderTargetView)\n+                .GetResult();\n+        }\n+\n+        struct TriangleSpec {\n+            RGBA8 color;\n+            std::array<float, 4> blendFactor = {};\n+        };\n+\n+        void SetupSingleSourcePipelines(const nxt::BlendState &blendState) {\n+            nxt::ShaderModule fsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Fragment, R\"(\n+                #version 450\n+                layout(set = 0, binding = 0) uniform myBlock {\n+                    vec4 color;\n+                } myUbo;\n+\n+                layout(location = 0) out vec4 fragColor;\n+\n+                void main() {\n+                    fragColor = myUbo.color;\n+                }\n+            )\");\n+\n+            basePipeline = device.CreateRenderPipelineBuilder()\n+                .SetSubpass(renderpass, 0)\n+                .SetLayout(pipelineLayout)\n+                .SetStage(nxt::ShaderStage::Vertex, vsModule, \"main\")\n+                .SetStage(nxt::ShaderStage::Fragment, fsModule, \"main\")\n+                .GetResult();\n+\n+            testPipeline = device.CreateRenderPipelineBuilder()\n+                .SetSubpass(renderpass, 0)\n+                .SetLayout(pipelineLayout)\n+                .SetStage(nxt::ShaderStage::Vertex, vsModule, \"main\")\n+                .SetStage(nxt::ShaderStage::Fragment, fsModule, \"main\")\n+                .SetColorAttachmentBlendState(0, blendState)\n+                .GetResult();\n+        }\n+\n+        void SetupDualSourcePipelines(const nxt::BlendState &blendState) {\n+            nxt::ShaderModule fsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Fragment, R\"(\n+                #version 450\n+                layout(set = 0, binding = 0) uniform myBlock {\n+                    vec4 color0;\n+                    vec4 color1;\n+                } myUbo;\n+\n+                layout(location = 0) out vec4 fragColor0;\n+                layout(location = 1) out vec4 fragColor1;\n+\n+                void main() {\n+                    fragColor0 = myUbo.color0;\n+                    fragColor1 = myUbo.color1;\n+                }\n+            )\");\n+\n+            basePipeline = device.CreateRenderPipelineBuilder()\n+                .SetSubpass(renderpass, 0)\n+                .SetLayout(pipelineLayout)\n+                .SetStage(nxt::ShaderStage::Vertex, vsModule, \"main\")\n+                .SetStage(nxt::ShaderStage::Fragment, fsModule, \"main\")\n+                .GetResult();\n+\n+            testPipeline = device.CreateRenderPipelineBuilder()\n+                .SetSubpass(renderpass, 0)\n+                .SetLayout(pipelineLayout)\n+                .SetStage(nxt::ShaderStage::Vertex, vsModule, \"main\")\n+                .SetStage(nxt::ShaderStage::Fragment, fsModule, \"main\")\n+                .SetColorAttachmentBlendState(0, blendState)\n+                .GetResult();\n+        }\n+\n+        template <size_t N>\n+        nxt::BindGroup MakeBindGroupForColors(std::array<RGBA8, N> colors) {\n+            std::array<float, 4 * N> data;\n+            for (unsigned int i = 0; i < N; ++i) {\n+                data[4 * i + 0] = static_cast<float>(colors[i].r) / 255.f;\n+                data[4 * i + 1] = static_cast<float>(colors[i].g) / 255.f;\n+                data[4 * i + 2] = static_cast<float>(colors[i].b) / 255.f;\n+                data[4 * i + 3] = static_cast<float>(colors[i].a) / 255.f;\n+            }\n+\n+            uint32_t bufferSize = static_cast<uint32_t>(4 * N * sizeof(float));\n+\n+            nxt::Buffer buffer = utils::CreateFrozenBufferFromData(device, &data, bufferSize, nxt::BufferUsageBit::Uniform);\n+\n+            nxt::BufferView view = buffer.CreateBufferViewBuilder()\n+                .SetExtent(0, bufferSize)\n+                .GetResult();\n+\n+            return device.CreateBindGroupBuilder()\n+                .SetLayout(bindGroupLayout)\n+                .SetUsage(nxt::BindGroupUsage::Frozen)\n+                .SetBufferViews(0, 1, &view)\n+                .GetResult();\n+        }\n+\n+        void DoSingleSourceTest(RGBA8 base, const TriangleSpec& triangle, const RGBA8& expected) {\n+            renderTarget.TransitionUsage(nxt::TextureUsageBit::OutputAttachment);\n+\n+            nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()\n+                .BeginRenderPass(renderpass, framebuffer)\n+                    .BeginRenderSubpass()\n+                        .SetRenderPipeline(basePipeline)\n+                        .SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { base } })))\n+                        .DrawArrays(3, 1, 0, 0)\n+\n+                        .SetRenderPipeline(testPipeline)\n+                        .SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 1>({ { triangle.color } })))\n+                        .SetBlendColor(triangle.blendFactor[0], triangle.blendFactor[1], triangle.blendFactor[2], triangle.blendFactor[3])\n+                        .DrawArrays(3, 1, 0, 0)\n+                    .EndRenderSubpass()\n+                .EndRenderPass()\n+                .GetResult();\n+\n+\n+            queue.Submit(1, &commands);\n+\n+            EXPECT_PIXEL_RGBA8_EQ(expected, renderTarget, kRTSize / 2, kRTSize / 2);\n+        }\n+\n+        void DoDualSourceTest(RGBA8 base, const TriangleSpec& triangle, const RGBA8& color1, const RGBA8& expected) {\n+            renderTarget.TransitionUsage(nxt::TextureUsageBit::OutputAttachment);\n+\n+            nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()\n+                .BeginRenderPass(renderpass, framebuffer)\n+                    .BeginRenderSubpass()\n+                        .SetRenderPipeline(basePipeline)\n+                        .SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 2>({ { base, base } })))\n+                        .DrawArrays(3, 1, 0, 0)\n+\n+                        .SetRenderPipeline(testPipeline)\n+                        .SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 2>({ { triangle.color, color1 } })))\n+                        .SetBlendColor(triangle.blendFactor[0], triangle.blendFactor[1], triangle.blendFactor[2], triangle.blendFactor[3])\n+                        .DrawArrays(3, 1, 0, 0)\n+                    .EndRenderSubpass()\n+                    .EndRenderPass()\n+                .GetResult();\n+\n+\n+            queue.Submit(1, &commands);\n+\n+            EXPECT_PIXEL_RGBA8_EQ(expected, renderTarget, kRTSize / 2, kRTSize / 2);\n+        }\n+\n+        void CheckBlendOperation(RGBA8 base, nxt::BlendOperation operation, std::vector<std::pair<RGBA8, RGBA8>> tests) {\n+            nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+                .SetBlendEnabled(true)\n+                .SetColorBlend(operation, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+                .SetAlphaBlend(operation, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+                .GetResult();\n+\n+            SetupSingleSourcePipelines(blendState);\n+\n+            for (const auto& test : tests) {\n+                DoSingleSourceTest(base, { test.first }, test.second);\n+            }\n+        }\n+\n+        void CheckBlendFactor(RGBA8 base, nxt::BlendFactor colorSrcFactor, nxt::BlendFactor colorDstFactor, nxt::BlendFactor alphaSrcFactor, nxt::BlendFactor alphaDstFactor, std::vector<std::pair<TriangleSpec, RGBA8>> tests) {\n+            nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+                .SetBlendEnabled(true)\n+                .SetColorBlend(nxt::BlendOperation::Add, colorSrcFactor, colorDstFactor)\n+                .SetAlphaBlend(nxt::BlendOperation::Add, alphaSrcFactor, alphaDstFactor)\n+                .GetResult();\n+\n+            SetupSingleSourcePipelines(blendState);\n+\n+            for (const auto& test : tests) {\n+                DoSingleSourceTest(base, test.first, test.second);\n+            }\n+        }\n+\n+        void CheckSrcBlendFactor(RGBA8 base, nxt::BlendFactor colorFactor, nxt::BlendFactor alphaFactor, std::vector<std::pair<TriangleSpec, RGBA8>> tests) {\n+            CheckBlendFactor(base, colorFactor, nxt::BlendFactor::One, alphaFactor, nxt::BlendFactor::One, tests);\n+        }\n+\n+        void CheckDstBlendFactor(RGBA8 base, nxt::BlendFactor colorFactor, nxt::BlendFactor alphaFactor, std::vector<std::pair<TriangleSpec, RGBA8>> tests) {\n+            CheckBlendFactor(base, nxt::BlendFactor::One, colorFactor, nxt::BlendFactor::One, alphaFactor, tests);\n+        }\n+\n+        nxt::Framebuffer framebuffer;\n+        nxt::RenderPass renderpass;\n+        nxt::RenderPipeline basePipeline;\n+        nxt::RenderPipeline testPipeline;\n+        nxt::Texture renderTarget;\n+        nxt::TextureView renderTargetView;\n+        nxt::ShaderModule vsModule;\n+        nxt::BindGroupLayout bindGroupLayout;\n+        nxt::PipelineLayout pipelineLayout;\n+};\n+\n+namespace {\n+    // Add two colors and clamp\n+    constexpr RGBA8 operator+(const RGBA8& col1, const RGBA8& col2) {\n+        int r = static_cast<int>(col1.r) + static_cast<int>(col2.r);\n+        int g = static_cast<int>(col1.g) + static_cast<int>(col2.g);\n+        int b = static_cast<int>(col1.b) + static_cast<int>(col2.b);\n+        int a = static_cast<int>(col1.a) + static_cast<int>(col2.a);\n+        r = (r > 255 ? 255 : (r < 0 ? 0 : r));\n+        g = (g > 255 ? 255 : (g < 0 ? 0 : g));\n+        b = (b > 255 ? 255 : (b < 0 ? 0 : b));\n+        a = (a > 255 ? 255 : (a < 0 ? 0 : a));\n+\n+        return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b), static_cast<uint8_t>(a));\n+    }\n+\n+    // Subtract two colors and clamp\n+    constexpr RGBA8 operator-(const RGBA8& col1, const RGBA8& col2) {\n+        int r = static_cast<int>(col1.r) - static_cast<int>(col2.r);\n+        int g = static_cast<int>(col1.g) - static_cast<int>(col2.g);\n+        int b = static_cast<int>(col1.b) - static_cast<int>(col2.b);\n+        int a = static_cast<int>(col1.a) - static_cast<int>(col2.a);\n+        r = (r > 255 ? 255 : (r < 0 ? 0 : r));\n+        g = (g > 255 ? 255 : (g < 0 ? 0 : g));\n+        b = (b > 255 ? 255 : (b < 0 ? 0 : b));\n+        a = (a > 255 ? 255 : (a < 0 ? 0 : a));\n+\n+        return RGBA8(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b), static_cast<uint8_t>(a));\n+    }\n+\n+    // Get the component-wise minimum of two colors\n+    RGBA8 min(const RGBA8& col1, const RGBA8& col2) {\n+        return RGBA8(\n+            std::min(col1.r, col2.r),\n+            std::min(col1.g, col2.g),\n+            std::min(col1.b, col2.b),\n+            std::min(col1.a, col2.a)\n+        );\n+    }\n+\n+    // Get the component-wise maximum of two colors\n+    RGBA8 max(const RGBA8& col1, const RGBA8& col2) {\n+        return RGBA8(\n+            std::max(col1.r, col2.r),\n+            std::max(col1.g, col2.g),\n+            std::max(col1.b, col2.b),\n+            std::max(col1.a, col2.a)\n+        );\n+    }\n+\n+    // Blend two RGBA8 color values parameterized by the provided factors in the range [0.f, 1.f]\n+    RGBA8 mix(const RGBA8& col1, const RGBA8& col2, std::array<float, 4> fac) {\n+        float r = static_cast<float>(col1.r) * (1.f - fac[0]) + static_cast<float>(col2.r) * fac[0];\n+        float g = static_cast<float>(col1.g) * (1.f - fac[1]) + static_cast<float>(col2.g) * fac[1];\n+        float b = static_cast<float>(col1.b) * (1.f - fac[2]) + static_cast<float>(col2.b) * fac[2];\n+        float a = static_cast<float>(col1.a) * (1.f - fac[3]) + static_cast<float>(col2.a) * fac[3];\n+\n+        // Get the integer locations of the resulting 4D RGBA8 hypercube after the blend\n+        std::array<RGBA8, 16> corners = { {\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 0.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 0.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 0.f), static_cast<uint8_t>(a + 1.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 0.f)}),\n+            RGBA8({static_cast<uint8_t>(r + 1.f), static_cast<uint8_t>(g + 1.f), static_cast<uint8_t>(b + 1.f), static_cast<uint8_t>(a + 1.f)}),\n+        } };\n+\n+        // Find the nearest RGBA8 color by Euclidean distance\n+        return [&](auto start, auto end, auto curr) -> RGBA8 {\n+            auto distance = [&](const RGBA8& color) -> double {\n+                return std::sqrt(\n+                    std::pow(static_cast<float>(color.r) - r, 2) +\n+                    std::pow(static_cast<float>(color.g) - g, 2) +\n+                    std::pow(static_cast<float>(color.b) - b, 2) +\n+                    std::pow(static_cast<float>(color.a) - a, 2)\n+                );\n+            };\n+\n+            for (auto& iter = start; iter != end; iter++) {\n+                if (distance(*iter) < distance(*curr)) {\n+                    curr = iter;\n+                }\n+            }\n+\n+            return *curr;\n+\n+        }(corners.begin() + 1, corners.end(), corners.begin());\n+    }\n+\n+    // Blend two RGBA8 color values parameterized by the provided RGBA8 factor\n+    RGBA8 mix(const RGBA8& col1, const RGBA8& col2, const RGBA8& fac) {\n+        std::array<float, 4> f = { {\n+            static_cast<float>(fac.r) / 255.f,\n+            static_cast<float>(fac.g) / 255.f,\n+            static_cast<float>(fac.b) / 255.f,\n+            static_cast<float>(fac.a) / 255.f,\n+        } };\n+        return mix(col1, col2, f);\n+    }\n+\n+    constexpr std::array<RGBA8, 8> kColors = { {\n+        // check operations over multiple channels\n+        RGBA8(64,0,0,0),\n+        RGBA8(0,64,0,0),\n+        RGBA8(64,0,32,0),\n+        RGBA8(0,64,32,0),\n+        RGBA8(128,0,128,128),\n+        RGBA8(0,128,128,128),\n+\n+        // check cases that may cause overflow\n+        RGBA8(0,0,0,0),\n+        RGBA8(255,255,255,255),\n+    } };\n+}\n+\n+// Test compilation and usage of the fixture\n+TEST_P(BlendStateTest, Basic) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder().GetResult();\n+    SetupSingleSourcePipelines(blendState);\n+\n+    DoSingleSourceTest(RGBA8(0, 0, 0, 0), { RGBA8(255, 0, 0, 0) }, RGBA8(255, 0, 0, 0));\n+}\n+\n+TEST_P(BlendStateTest, BlendOperationAdd) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<RGBA8, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(color, base + color);\n+    });\n+    CheckBlendOperation(base, nxt::BlendOperation::Add, tests);\n+}\n+\n+TEST_P(BlendStateTest, BlendOperationSubtract) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<RGBA8, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(color, color - base);\n+    });\n+    CheckBlendOperation(base, nxt::BlendOperation::Subtract, tests);\n+}\n+\n+TEST_P(BlendStateTest, BlendOperationReverseSubtract) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<RGBA8, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(color, base - color);\n+    });\n+    CheckBlendOperation(base, nxt::BlendOperation::ReverseSubtract, tests);\n+}\n+\n+TEST_P(BlendStateTest, BlendOperationMin) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<RGBA8, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(color, min(base, color));\n+    });\n+    CheckBlendOperation(base, nxt::BlendOperation::Min, tests);\n+}\n+\n+TEST_P(BlendStateTest, BlendOperationMax) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<RGBA8, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(color, max(base, color));\n+    });\n+    CheckBlendOperation(base, nxt::BlendOperation::Max, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorZero) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(TriangleSpec({ { color } }), base);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::Zero, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOne) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(TriangleSpec({ { color } }), base + color);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::One, nxt::BlendFactor::One, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorSrcColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = color;\n+        fac.a = 0;\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::SrcColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusSrcColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - color;\n+        fac.a = 0;\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::OneMinusSrcColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorSrcAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac(color.a, color.a, color.a, color.a);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::SrcAlpha, nxt::BlendFactor::SrcAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusSrcAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::OneMinusSrcAlpha, nxt::BlendFactor::OneMinusSrcAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorDstColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = base;\n+        fac.a = 0;\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::DstColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusDstColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - base;\n+        fac.a = 0;\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::OneMinusDstColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorDstAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac(base.a, base.a, base.a, base.a);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::DstAlpha, nxt::BlendFactor::DstAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusDstAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::OneMinusDstAlpha, nxt::BlendFactor::OneMinusDstAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorSrcAlphaSaturated) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));\n+        RGBA8 fac(f, f, f, 255);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::SrcAlphaSaturated, nxt::BlendFactor::SrcAlphaSaturated, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorBlendColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, triangleSpec.blendFactor);\n+        return std::make_pair(triangleSpec, expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::BlendColor, nxt::BlendFactor::BlendColor, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusBlendColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });\n+        std::array<float, 4> f = { { 0.8f, 0.6f, 0.4f, 0.2f } };\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, f);\n+        return std::make_pair(triangleSpec, expected);\n+    });\n+    CheckSrcBlendFactor(base, nxt::BlendFactor::OneMinusBlendColor, nxt::BlendFactor::OneMinusBlendColor, tests);\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorSrc1Color) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::Src1Color, nxt::BlendFactor::One)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, color1);\n+        expected.a = color.a;\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusSrc1Color) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::OneMinusSrc1Color, nxt::BlendFactor::One)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, RGBA8(255, 255, 255, 255) - color1);\n+        expected.a = color.a;\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorSrc1Alpha) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::Src1Alpha, nxt::BlendFactor::One)\n+        .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::Src1Alpha, nxt::BlendFactor::One)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 fac(color1.a, color1.a, color1.a, color1.a);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, SrcBlendFactorOneMinusSrc1Alpha) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::OneMinusSrc1Alpha, nxt::BlendFactor::One)\n+        .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::OneMinusSrc1Alpha, nxt::BlendFactor::One)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color1.a, color1.a, color1.a, color1.a);\n+        RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac);\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorZero) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(TriangleSpec({ { color } }), color);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::Zero, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOne) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        return std::make_pair(TriangleSpec({ { color } }), base + color);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::One, nxt::BlendFactor::One, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorSrcColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = color;\n+        fac.a = 0;\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::SrcColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusSrcColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - color;\n+        fac.a = 0;\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::OneMinusSrcColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorSrcAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac(color.a, color.a, color.a, color.a);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::SrcAlpha, nxt::BlendFactor::SrcAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusSrcAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color.a, color.a, color.a, color.a);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::OneMinusSrcAlpha, nxt::BlendFactor::OneMinusSrcAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorDstColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = base;\n+        fac.a = 0;\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::DstColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusDstColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - base;\n+        fac.a = 0;\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::OneMinusDstColor, nxt::BlendFactor::Zero, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorDstAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac(base.a, base.a, base.a, base.a);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::DstAlpha, nxt::BlendFactor::DstAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusDstAlpha) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(base.a, base.a, base.a, base.a);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::OneMinusDstAlpha, nxt::BlendFactor::OneMinusDstAlpha, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorSrcAlphaSaturated) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        uint8_t f = std::min(color.a, static_cast<uint8_t>(255 - base.a));\n+        RGBA8 fac(f, f, f, 255);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        return std::make_pair(TriangleSpec({ { color } }), expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::SrcAlphaSaturated, nxt::BlendFactor::SrcAlphaSaturated, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorBlendColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, triangleSpec.blendFactor);\n+        return std::make_pair(triangleSpec, expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::BlendColor, nxt::BlendFactor::BlendColor, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusBlendColor) {\n+    RGBA8 base(32, 64, 128, 192);\n+    std::vector<std::pair<TriangleSpec, RGBA8>> tests;\n+    std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) {\n+        auto triangleSpec = TriangleSpec({ { color }, {{ 0.2f, 0.4f, 0.6f, 0.8f }} });\n+        std::array<float, 4> f = { { 0.8f, 0.6f, 0.4f, 0.2f } };\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, f);\n+        return std::make_pair(triangleSpec, expected);\n+    });\n+    CheckDstBlendFactor(base, nxt::BlendFactor::OneMinusBlendColor, nxt::BlendFactor::OneMinusBlendColor, tests);\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorSrc1Color) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::Src1Color)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, color1);\n+        expected.a = color.a;\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusSrc1Color) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::OneMinusSrc1Color)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, RGBA8(255, 255, 255, 255) - color1);\n+        expected.a = color.a;\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorSrc1Alpha) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::Src1Alpha)\n+        .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::Src1Alpha)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 fac(color1.a, color1.a, color1.a, color1.a);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, DstBlendFactorOneMinusSrc1Alpha) {\n+    nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+        .SetBlendEnabled(true)\n+        .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::OneMinusSrc1Alpha)\n+        .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::OneMinusSrc1Alpha)\n+        .GetResult();\n+    SetupDualSourcePipelines(blendState);\n+\n+    RGBA8 base(0, 0, 0, 0);\n+    for (auto& color : kColors) {\n+        RGBA8 color1(64, 32, 192, 128);\n+        RGBA8 fac = RGBA8(255, 255, 255, 255) - RGBA8(color1.a, color1.a, color1.a, color1.a);\n+        RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac);\n+        DoDualSourceTest(base, { color }, color1, expected);\n+    }\n+}\n+\n+TEST_P(BlendStateTest, ColorWriteMask) {\n+    {\n+        // Test single channel color write\n+        nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+            .SetBlendEnabled(true)\n+            .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetColorWriteMask(nxt::ColorWriteMask::Red)\n+            .GetResult();\n+        SetupSingleSourcePipelines(blendState);\n+\n+        RGBA8 base(32, 64, 128, 192);\n+        for (auto& color : kColors) {\n+            RGBA8 expected = base + RGBA8(color.r, 0, 0, 0);\n+            DoSingleSourceTest(base, { color }, expected);\n+        }\n+    }\n+\n+    {\n+        // Test multi channel color write\n+        nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+            .SetBlendEnabled(true)\n+            .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetColorWriteMask(nxt::ColorWriteMask::Green | nxt::ColorWriteMask::Alpha)\n+            .GetResult();\n+        SetupSingleSourcePipelines(blendState);\n+\n+        RGBA8 base(32, 64, 128, 192);\n+        for (auto& color : kColors) {\n+            RGBA8 expected = base + RGBA8(0, color.g, 0, color.a);\n+            DoSingleSourceTest(base, { color }, expected);\n+        }\n+    }\n+\n+    {\n+        // Test no channel color write\n+        nxt::BlendState blendState = device.CreateBlendStateBuilder()\n+            .SetBlendEnabled(true)\n+            .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetColorWriteMask(nxt::ColorWriteMask::None)\n+            .GetResult();\n+        SetupSingleSourcePipelines(blendState);\n+\n+        RGBA8 base(32, 64, 128, 192);\n+        for (auto& color : kColors) {\n+            DoSingleSourceTest(base, { color }, base);\n+        }\n+    }\n+}\n+\n+TEST_P(BlendStateTest, IndependentBlendState) {\n+\n+    std::array<nxt::Texture, 5> renderTargets;\n+    std::array<nxt::TextureView, 5> renderTargetViews;\n+\n+    for (uint32_t i = 0; i < 5; ++i) {\n+        renderTargets[i] = device.CreateTextureBuilder()\n+            .SetDimension(nxt::TextureDimension::e2D)\n+            .SetExtent(kRTSize, kRTSize, 1)\n+            .SetFormat(nxt::TextureFormat::R8G8B8A8Unorm)\n+            .SetMipLevels(1)\n+            .SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment | nxt::TextureUsageBit::TransferSrc)\n+            .SetInitialUsage(nxt::TextureUsageBit::OutputAttachment)\n+            .GetResult();\n+        renderTargetViews[i] = renderTargets[i].CreateTextureViewBuilder().GetResult();\n+    }\n+\n+    renderpass = device.CreateRenderPassBuilder()\n+        .SetAttachmentCount(5)\n+        .SetSubpassCount(1)\n+        .AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm)\n+        .AttachmentSetFormat(1, nxt::TextureFormat::R8G8B8A8Unorm)\n+        .AttachmentSetFormat(2, nxt::TextureFormat::R8G8B8A8Unorm)\n+        .AttachmentSetFormat(3, nxt::TextureFormat::R8G8B8A8Unorm)\n+        .AttachmentSetFormat(4, nxt::TextureFormat::R8G8B8A8Unorm)\n+        // Scatter these so we know indexing to the right shader location is working\n+        .SubpassSetColorAttachment(0, 0, 2)\n+        // We skip attachment index 1 to check the case where the blend states in the pipeline state are not tightly packed\n+        .SubpassSetColorAttachment(0, 1, 4)\n+        .SubpassSetColorAttachment(0, 2, 3)\n+        .SubpassSetColorAttachment(0, 3, 0)\n+        .GetResult();\n+\n+    framebuffer = device.CreateFramebufferBuilder()\n+        .SetRenderPass(renderpass)\n+        .SetDimensions(kRTSize, kRTSize)\n+        .SetAttachment(0, renderTargetViews[0])\n+        .SetAttachment(1, renderTargetViews[1])\n+        .SetAttachment(2, renderTargetViews[2])\n+        .SetAttachment(3, renderTargetViews[3])\n+        .SetAttachment(4, renderTargetViews[4])\n+        .GetResult();\n+\n+    nxt::ShaderModule fsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Fragment, R\"(\n+        #version 450\n+        layout(set = 0, binding = 0) uniform myBlock {\n+            vec4 color0;\n+            vec4 color1;\n+            vec4 color2;\n+            vec4 color3;\n+        } myUbo;\n+\n+        layout(location = 0) out vec4 fragColor0;\n+        layout(location = 1) out vec4 fragColor1;\n+        layout(location = 2) out vec4 fragColor2;\n+        layout(location = 3) out vec4 fragColor3;\n+\n+        void main() {\n+            fragColor0 = myUbo.color0;\n+            fragColor1 = myUbo.color1;\n+            fragColor2 = myUbo.color2;\n+            fragColor3 = myUbo.color3;\n+        }\n+    )\");\n+\n+    std::array<nxt::BlendState, 3> blendStates = { {\n+        device.CreateBlendStateBuilder()\n+            .SetBlendEnabled(true)\n+            .SetColorBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetAlphaBlend(nxt::BlendOperation::Add, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .GetResult(),\n+        device.CreateBlendStateBuilder()\n+            .SetBlendEnabled(true)\n+            .SetColorBlend(nxt::BlendOperation::Subtract, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetAlphaBlend(nxt::BlendOperation::Subtract, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .GetResult(),\n+        device.CreateBlendStateBuilder()\n+            .SetBlendEnabled(true)\n+            .SetColorBlend(nxt::BlendOperation::Min, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .SetAlphaBlend(nxt::BlendOperation::Min, nxt::BlendFactor::One, nxt::BlendFactor::One)\n+            .GetResult(),\n+    } };\n+\n+    basePipeline = device.CreateRenderPipelineBuilder()\n+        .SetSubpass(renderpass, 0)\n+        .SetLayout(pipelineLayout)\n+        .SetStage(nxt::ShaderStage::Vertex, vsModule, \"main\")\n+        .SetStage(nxt::ShaderStage::Fragment, fsModule, \"main\")\n+        .GetResult();\n+\n+    testPipeline = device.CreateRenderPipelineBuilder()\n+        .SetSubpass(renderpass, 0)\n+        .SetLayout(pipelineLayout)\n+        .SetStage(nxt::ShaderStage::Vertex, vsModule, \"main\")\n+        .SetStage(nxt::ShaderStage::Fragment, fsModule, \"main\")\n+        .SetColorAttachmentBlendState(0, blendStates[0])\n+        .SetColorAttachmentBlendState(1, blendStates[1])\n+        // Blend state not set on third color attachment. It should be default\n+        .SetColorAttachmentBlendState(3, blendStates[2])\n+        .GetResult();\n+\n+\n+    for (unsigned int c = 0; c < kColors.size(); ++c) {\n+        RGBA8 base = kColors[((c + 31) * 29) % kColors.size()];\n+        RGBA8 color0 = kColors[((c + 19) * 13) % kColors.size()];\n+        RGBA8 color1 = kColors[((c + 11) * 43) % kColors.size()];\n+        RGBA8 color2 = kColors[((c + 7) * 3) % kColors.size()];\n+        RGBA8 color3 = kColors[((c + 13) * 71) % kColors.size()];\n+\n+        RGBA8 expected0 = color0 + base;\n+        RGBA8 expected1 = color1 - base;\n+        RGBA8 expected2 = color2;\n+        RGBA8 expected3 = min(color3, base);\n+\n+        renderTargets[2].TransitionUsage(nxt::TextureUsageBit::OutputAttachment);\n+        renderTargets[4].TransitionUsage(nxt::TextureUsageBit::OutputAttachment);\n+        renderTargets[3].TransitionUsage(nxt::TextureUsageBit::OutputAttachment);\n+        renderTargets[0].TransitionUsage(nxt::TextureUsageBit::OutputAttachment);\n+\n+        nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()\n+            .BeginRenderPass(renderpass, framebuffer)\n+            .BeginRenderSubpass()\n+            .SetRenderPipeline(basePipeline)\n+            .SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 4>({ { base, base, base, base } })))\n+            .DrawArrays(3, 1, 0, 0)\n+\n+            .SetRenderPipeline(testPipeline)\n+            .SetBindGroup(0, MakeBindGroupForColors(std::array<RGBA8, 4>({ { color0, color1, color2, color3 } })))\n+            .DrawArrays(3, 1, 0, 0)\n+            .EndRenderSubpass()\n+            .EndRenderPass()\n+            .GetResult();\n+\n+        queue.Submit(1, &commands);\n+\n+        EXPECT_PIXEL_RGBA8_EQ(expected0, renderTargets[2], kRTSize / 2, kRTSize / 2) << \"Attachment slot 0 using render target 2 should have been \" << color0 << \" + \" << base << \" = \" << expected0;\n+        EXPECT_PIXEL_RGBA8_EQ(expected1, renderTargets[4], kRTSize / 2, kRTSize / 2) << \"Attachment slot 1 using render target 4 should have been \" << color1 << \" - \" << base << \" = \" << expected1;\n+        EXPECT_PIXEL_RGBA8_EQ(expected2, renderTargets[3], kRTSize / 2, kRTSize / 2) << \"Attachment slot 2 using render target 3 should have been \" << color2 << \" = \" << expected2 << \"(no blending)\";\n+        EXPECT_PIXEL_RGBA8_EQ(expected3, renderTargets[0], kRTSize / 2, kRTSize / 2) << \"Attachment slot 3 using render target 0 should have been min(\" << color3 << \", \" << base << \") = \" << expected3;\n+    }\n+}\n+","path":"src/tests/end2end/BlendStateTests.cpp","position":1054,"original_position":1054,"commit_id":"2e84b0c80e4d3b5787b8ca069e6cbc164766efed","original_commit_id":"2e84b0c80e4d3b5787b8ca069e6cbc164766efed","user":{"login":"Kangz","id":472810,"avatar_url":"https://avatars3.githubusercontent.com/u/472810?v=4","gravatar_id":"","url":"https://api.github.com/users/Kangz","html_url":"https://github.com/Kangz","followers_url":"https://api.github.com/users/Kangz/followers","following_url":"https://api.github.com/users/Kangz/following{/other_user}","gists_url":"https://api.github.com/users/Kangz/gists{/gist_id}","starred_url":"https://api.github.com/users/Kangz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Kangz/subscriptions","organizations_url":"https://api.github.com/users/Kangz/orgs","repos_url":"https://api.github.com/users/Kangz/repos","events_url":"https://api.github.com/users/Kangz/events{/privacy}","received_events_url":"https://api.github.com/users/Kangz/received_events","type":"User","site_admin":false},"body":"Could we have a the following tests too:\r\n - BlendConstants are set to a known value (0, 0, 0, 0?) at the beginning of each subpass with no inheritance between subpasses.\r\n - Validation tests for all the validation you added.","created_at":"2017-08-03T19:29:28Z","updated_at":"2017-08-03T19:29:44Z","html_url":"https://github.com/google/nxt-standalone/pull/103#discussion_r131238247","pull_request_url":"https://api.github.com/repos/google/nxt-standalone/pulls/103","_links":{"self":{"href":"https://api.github.com/repos/google/nxt-standalone/pulls/comments/131238247"},"html":{"href":"https://github.com/google/nxt-standalone/pull/103#discussion_r131238247"},"pull_request":{"href":"https://api.github.com/repos/google/nxt-standalone/pulls/103"}}},"pull_request":{"url":"https://api.github.com/repos/google/nxt-standalone/pulls/103","id":133814269,"html_url":"https://github.com/google/nxt-standalone/pull/103","diff_url":"https://github.com/google/nxt-standalone/pull/103.diff","patch_url":"https://github.com/google/nxt-standalone/pull/103.patch","issue_url":"https://api.github.com/repos/google/nxt-standalone/issues/103","number":103,"state":"open","locked":false,"title":"Blend state","user":{"login":"austinEng","id":2154796,"avatar_url":"https://avatars3.githubusercontent.com/u/2154796?v=4","gravatar_id":"","url":"https://api.github.com/users/austinEng","html_url":"https://github.com/austinEng","followers_url":"https://api.github.com/users/austinEng/followers","following_url":"https://api.github.com/users/austinEng/following{/other_user}","gists_url":"https://api.github.com/users/austinEng/gists{/gist_id}","starred_url":"https://api.github.com/users/austinEng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/austinEng/subscriptions","organizations_url":"https://api.github.com/users/austinEng/orgs","repos_url":"https://api.github.com/users/austinEng/repos","events_url":"https://api.github.com/users/austinEng/events{/privacy}","received_events_url":"https://api.github.com/users/austinEng/received_events","type":"User","site_admin":false},"body":"Not working yet.\r\n - As usual, this is completely untested on Metal. But for some reason it's saying some enums which I got from the docs aren't defined?\r\n- This PR also sets the render target formats based on the subpass\r\n- Updates `shaderc` so we can have 4 draw buffers\r\n- Needs some comments","created_at":"2017-08-02T21:35:35Z","updated_at":"2017-08-03T19:29:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":"652d065e9d069939869dbab69c5a8c573a736f03","assignee":null,"assignees":[],"requested_reviewers":[{"login":"kainino0x","id":606355,"avatar_url":"https://avatars1.githubusercontent.com/u/606355?v=4","gravatar_id":"","url":"https://api.github.com/users/kainino0x","html_url":"https://github.com/kainino0x","followers_url":"https://api.github.com/users/kainino0x/followers","following_url":"https://api.github.com/users/kainino0x/following{/other_user}","gists_url":"https://api.github.com/users/kainino0x/gists{/gist_id}","starred_url":"https://api.github.com/users/kainino0x/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kainino0x/subscriptions","organizations_url":"https://api.github.com/users/kainino0x/orgs","repos_url":"https://api.github.com/users/kainino0x/repos","events_url":"https://api.github.com/users/kainino0x/events{/privacy}","received_events_url":"https://api.github.com/users/kainino0x/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/google/nxt-standalone/pulls/103/commits","review_comments_url":"https://api.github.com/repos/google/nxt-standalone/pulls/103/comments","review_comment_url":"https://api.github.com/repos/google/nxt-standalone/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/nxt-standalone/issues/103/comments","statuses_url":"https://api.github.com/repos/google/nxt-standalone/statuses/2e84b0c80e4d3b5787b8ca069e6cbc164766efed","head":{"label":"austinEng:blend-state","ref":"blend-state","sha":"2e84b0c80e4d3b5787b8ca069e6cbc164766efed","user":{"login":"austinEng","id":2154796,"avatar_url":"https://avatars3.githubusercontent.com/u/2154796?v=4","gravatar_id":"","url":"https://api.github.com/users/austinEng","html_url":"https://github.com/austinEng","followers_url":"https://api.github.com/users/austinEng/followers","following_url":"https://api.github.com/users/austinEng/following{/other_user}","gists_url":"https://api.github.com/users/austinEng/gists{/gist_id}","starred_url":"https://api.github.com/users/austinEng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/austinEng/subscriptions","organizations_url":"https://api.github.com/users/austinEng/orgs","repos_url":"https://api.github.com/users/austinEng/repos","events_url":"https://api.github.com/users/austinEng/events{/privacy}","received_events_url":"https://api.github.com/users/austinEng/received_events","type":"User","site_admin":false},"repo":{"id":92305329,"name":"nxt-standalone","full_name":"austinEng/nxt-standalone","owner":{"login":"austinEng","id":2154796,"avatar_url":"https://avatars3.githubusercontent.com/u/2154796?v=4","gravatar_id":"","url":"https://api.github.com/users/austinEng","html_url":"https://github.com/austinEng","followers_url":"https://api.github.com/users/austinEng/followers","following_url":"https://api.github.com/users/austinEng/following{/other_user}","gists_url":"https://api.github.com/users/austinEng/gists{/gist_id}","starred_url":"https://api.github.com/users/austinEng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/austinEng/subscriptions","organizations_url":"https://api.github.com/users/austinEng/orgs","repos_url":"https://api.github.com/users/austinEng/repos","events_url":"https://api.github.com/users/austinEng/events{/privacy}","received_events_url":"https://api.github.com/users/austinEng/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/austinEng/nxt-standalone","description":"ANGLE-like library for Chromium's next-generation Web graphics API prototype.","fork":true,"url":"https://api.github.com/repos/austinEng/nxt-standalone","forks_url":"https://api.github.com/repos/austinEng/nxt-standalone/forks","keys_url":"https://api.github.com/repos/austinEng/nxt-standalone/keys{/key_id}","collaborators_url":"https://api.github.com/repos/austinEng/nxt-standalone/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/austinEng/nxt-standalone/teams","hooks_url":"https://api.github.com/repos/austinEng/nxt-standalone/hooks","issue_events_url":"https://api.github.com/repos/austinEng/nxt-standalone/issues/events{/number}","events_url":"https://api.github.com/repos/austinEng/nxt-standalone/events","assignees_url":"https://api.github.com/repos/austinEng/nxt-standalone/assignees{/user}","branches_url":"https://api.github.com/repos/austinEng/nxt-standalone/branches{/branch}","tags_url":"https://api.github.com/repos/austinEng/nxt-standalone/tags","blobs_url":"https://api.github.com/repos/austinEng/nxt-standalone/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/austinEng/nxt-standalone/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/austinEng/nxt-standalone/git/refs{/sha}","trees_url":"https://api.github.com/repos/austinEng/nxt-standalone/git/trees{/sha}","statuses_url":"https://api.github.com/repos/austinEng/nxt-standalone/statuses/{sha}","languages_url":"https://api.github.com/repos/austinEng/nxt-standalone/languages","stargazers_url":"https://api.github.com/repos/austinEng/nxt-standalone/stargazers","contributors_url":"https://api.github.com/repos/austinEng/nxt-standalone/contributors","subscribers_url":"https://api.github.com/repos/austinEng/nxt-standalone/subscribers","subscription_url":"https://api.github.com/repos/austinEng/nxt-standalone/subscription","commits_url":"https://api.github.com/repos/austinEng/nxt-standalone/commits{/sha}","git_commits_url":"https://api.github.com/repos/austinEng/nxt-standalone/git/commits{/sha}","comments_url":"https://api.github.com/repos/austinEng/nxt-standalone/comments{/number}","issue_comment_url":"https://api.github.com/repos/austinEng/nxt-standalone/issues/comments{/number}","contents_url":"https://api.github.com/repos/austinEng/nxt-standalone/contents/{+path}","compare_url":"https://api.github.com/repos/austinEng/nxt-standalone/compare/{base}...{head}","merges_url":"https://api.github.com/repos/austinEng/nxt-standalone/merges","archive_url":"https://api.github.com/repos/austinEng/nxt-standalone/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/austinEng/nxt-standalone/downloads","issues_url":"https://api.github.com/repos/austinEng/nxt-standalone/issues{/number}","pulls_url":"https://api.github.com/repos/austinEng/nxt-standalone/pulls{/number}","milestones_url":"https://api.github.com/repos/austinEng/nxt-standalone/milestones{/number}","notifications_url":"https://api.github.com/repos/austinEng/nxt-standalone/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/austinEng/nxt-standalone/labels{/name}","releases_url":"https://api.github.com/repos/austinEng/nxt-standalone/releases{/id}","deployments_url":"https://api.github.com/repos/austinEng/nxt-standalone/deployments","created_at":"2017-05-24T15:03:55Z","updated_at":"2017-05-24T15:03:57Z","pushed_at":"2017-08-03T16:55:49Z","git_url":"git://github.com/austinEng/nxt-standalone.git","ssh_url":"git@github.com:austinEng/nxt-standalone.git","clone_url":"https://github.com/austinEng/nxt-standalone.git","svn_url":"https://github.com/austinEng/nxt-standalone","homepage":"","size":1137,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"google:master","ref":"master","sha":"2c05f4d8fc327bf35db02e781e1020b290c2650b","user":{"login":"google","id":1342004,"avatar_url":"https://avatars1.githubusercontent.com/u/1342004?v=4","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"repo":{"id":88893060,"name":"nxt-standalone","full_name":"google/nxt-standalone","owner":{"login":"google","id":1342004,"avatar_url":"https://avatars1.githubusercontent.com/u/1342004?v=4","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/google/nxt-standalone","description":"ANGLE-like library for Chromium's next-generation Web graphics API prototype.","fork":false,"url":"https://api.github.com/repos/google/nxt-standalone","forks_url":"https://api.github.com/repos/google/nxt-standalone/forks","keys_url":"https://api.github.com/repos/google/nxt-standalone/keys{/key_id}","collaborators_url":"https://api.github.com/repos/google/nxt-standalone/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/google/nxt-standalone/teams","hooks_url":"https://api.github.com/repos/google/nxt-standalone/hooks","issue_events_url":"https://api.github.com/repos/google/nxt-standalone/issues/events{/number}","events_url":"https://api.github.com/repos/google/nxt-standalone/events","assignees_url":"https://api.github.com/repos/google/nxt-standalone/assignees{/user}","branches_url":"https://api.github.com/repos/google/nxt-standalone/branches{/branch}","tags_url":"https://api.github.com/repos/google/nxt-standalone/tags","blobs_url":"https://api.github.com/repos/google/nxt-standalone/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/google/nxt-standalone/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/google/nxt-standalone/git/refs{/sha}","trees_url":"https://api.github.com/repos/google/nxt-standalone/git/trees{/sha}","statuses_url":"https://api.github.com/repos/google/nxt-standalone/statuses/{sha}","languages_url":"https://api.github.com/repos/google/nxt-standalone/languages","stargazers_url":"https://api.github.com/repos/google/nxt-standalone/stargazers","contributors_url":"https://api.github.com/repos/google/nxt-standalone/contributors","subscribers_url":"https://api.github.com/repos/google/nxt-standalone/subscribers","subscription_url":"https://api.github.com/repos/google/nxt-standalone/subscription","commits_url":"https://api.github.com/repos/google/nxt-standalone/commits{/sha}","git_commits_url":"https://api.github.com/repos/google/nxt-standalone/git/commits{/sha}","comments_url":"https://api.github.com/repos/google/nxt-standalone/comments{/number}","issue_comment_url":"https://api.github.com/repos/google/nxt-standalone/issues/comments{/number}","contents_url":"https://api.github.com/repos/google/nxt-standalone/contents/{+path}","compare_url":"https://api.github.com/repos/google/nxt-standalone/compare/{base}...{head}","merges_url":"https://api.github.com/repos/google/nxt-standalone/merges","archive_url":"https://api.github.com/repos/google/nxt-standalone/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/google/nxt-standalone/downloads","issues_url":"https://api.github.com/repos/google/nxt-standalone/issues{/number}","pulls_url":"https://api.github.com/repos/google/nxt-standalone/pulls{/number}","milestones_url":"https://api.github.com/repos/google/nxt-standalone/milestones{/number}","notifications_url":"https://api.github.com/repos/google/nxt-standalone/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/google/nxt-standalone/labels{/name}","releases_url":"https://api.github.com/repos/google/nxt-standalone/releases{/id}","deployments_url":"https://api.github.com/repos/google/nxt-standalone/deployments","created_at":"2017-04-20T17:33:32Z","updated_at":"2017-07-30T05:47:47Z","pushed_at":"2017-08-03T18:45:13Z","git_url":"git://github.com/google/nxt-standalone.git","ssh_url":"git@github.com:google/nxt-standalone.git","clone_url":"https://github.com/google/nxt-standalone.git","svn_url":"https://github.com/google/nxt-standalone","homepage":"","size":1062,"stargazers_count":47,"watchers_count":47,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":14,"forks":11,"open_issues":14,"watchers":47,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/nxt-standalone/pulls/103"},"html":{"href":"https://github.com/google/nxt-standalone/pull/103"},"issue":{"href":"https://api.github.com/repos/google/nxt-standalone/issues/103"},"comments":{"href":"https://api.github.com/repos/google/nxt-standalone/issues/103/comments"},"review_comments":{"href":"https://api.github.com/repos/google/nxt-standalone/pulls/103/comments"},"review_comment":{"href":"https://api.github.com/repos/google/nxt-standalone/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/nxt-standalone/pulls/103/commits"},"statuses":{"href":"https://api.github.com/repos/google/nxt-standalone/statuses/2e84b0c80e4d3b5787b8ca069e6cbc164766efed"}}}}
{"actor":{"display_login":"Kangz"}}