Edit

kc3-lang/angle/src/tests/egl_tests/EGLStreamTest.cpp

Branch :

  • Show log

    Commit

  • Author : Jeff Gilbert
    Date : 2017-11-14 16:44:36
    Hash : 3dddccff
    Message : Support RGB gl-tex-external stream consumers for StreamProducerNV12. This makes it relatively simple to sample from a D3D11Texture of arbitrary format. From: https://bugzilla.mozilla.org/show_bug.cgi?id=1322746 BUG=angleproject:2233 TEST=angle_end2end_tests Change-Id: I10cd3043b5cb0c5d36dd613467ba6c0ceadf41af Reviewed-on: https://chromium-review.googlesource.com/758042 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/tests/egl_tests/EGLStreamTest.cpp
  • //
    // Copyright 2016 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // EGLStreamTest:
    //   Tests pertaining to egl::Stream.
    //
    
    #include <gtest/gtest.h>
    
    #include <vector>
    
    #include "OSWindow.h"
    #include "media/yuvtest.inl"
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    
    bool CheckNV12TextureSupport(ID3D11Device *device)
    {
        HRESULT result;
        UINT formatSupport;
        result = device->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport);
        if (result == E_FAIL)
        {
            return false;
        }
        return (formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
    }
    
    class EGLStreamTest : public ANGLETest
    {
      protected:
        EGLStreamTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
        }
    };
    
    // Tests validation of the stream API
    TEST_P(EGLStreamTest, StreamValidationTest)
    {
        EGLWindow *window            = getEGLWindow();
        EGLDisplay display           = window->getDisplay();
        const char *extensionsString = eglQueryString(display, EGL_EXTENSIONS);
        if (strstr(extensionsString, "EGL_KHR_stream") == nullptr)
        {
            std::cout << "Stream extension not supported" << std::endl;
            return;
        }
    
        const EGLint streamAttributesBad[] = {
            EGL_STREAM_STATE_KHR,
            0,
            EGL_NONE,
            EGL_PRODUCER_FRAME_KHR,
            0,
            EGL_NONE,
            EGL_CONSUMER_FRAME_KHR,
            0,
            EGL_NONE,
            EGL_CONSUMER_LATENCY_USEC_KHR,
            -1,
            EGL_NONE,
            EGL_RED_SIZE,
            EGL_DONT_CARE,
            EGL_NONE,
        };
    
        // Validate create stream attributes
        EGLStreamKHR stream = eglCreateStreamKHR(display, &streamAttributesBad[0]);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
        ASSERT_EQ(EGL_NO_STREAM_KHR, stream);
    
        stream = eglCreateStreamKHR(display, &streamAttributesBad[3]);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
        ASSERT_EQ(EGL_NO_STREAM_KHR, stream);
    
        stream = eglCreateStreamKHR(display, &streamAttributesBad[6]);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
        ASSERT_EQ(EGL_NO_STREAM_KHR, stream);
    
        stream = eglCreateStreamKHR(display, &streamAttributesBad[9]);
        ASSERT_EGL_ERROR(EGL_BAD_PARAMETER);
        ASSERT_EQ(EGL_NO_STREAM_KHR, stream);
    
        stream = eglCreateStreamKHR(display, &streamAttributesBad[12]);
        ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
        ASSERT_EQ(EGL_NO_STREAM_KHR, stream);
    
        const EGLint streamAttributes[] = {
            EGL_CONSUMER_LATENCY_USEC_KHR, 0, EGL_NONE,
        };
    
        stream = eglCreateStreamKHR(EGL_NO_DISPLAY, streamAttributes);
        ASSERT_EGL_ERROR(EGL_BAD_DISPLAY);
        ASSERT_EQ(EGL_NO_STREAM_KHR, stream);
    
        // Create an actual stream
        stream = eglCreateStreamKHR(display, streamAttributes);
        ASSERT_EGL_SUCCESS();
        ASSERT_NE(EGL_NO_STREAM_KHR, stream);
    
        // Assert it is in the created state
        EGLint state;
        eglQueryStreamKHR(display, stream, EGL_STREAM_STATE_KHR, &state);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(EGL_STREAM_STATE_CREATED_KHR, state);
    
        // Test getting and setting the latency
        EGLint latency = 10;
        eglStreamAttribKHR(display, stream, EGL_CONSUMER_LATENCY_USEC_KHR, latency);
        ASSERT_EGL_SUCCESS();
        eglQueryStreamKHR(display, stream, EGL_CONSUMER_LATENCY_USEC_KHR, &latency);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(10, latency);
        eglStreamAttribKHR(display, stream, EGL_CONSUMER_LATENCY_USEC_KHR, -1);
        ASSERT_EGL_ERROR(EGL_BAD_PARAMETER);
        ASSERT_EQ(10, latency);
    
        // Test the 64-bit queries
        EGLuint64KHR value;
        eglQueryStreamu64KHR(display, stream, EGL_CONSUMER_FRAME_KHR, &value);
        ASSERT_EGL_SUCCESS();
        eglQueryStreamu64KHR(display, stream, EGL_PRODUCER_FRAME_KHR, &value);
        ASSERT_EGL_SUCCESS();
    
        // Destroy the stream
        eglDestroyStreamKHR(display, stream);
        ASSERT_EGL_SUCCESS();
    }
    
    // Tests validation of stream consumer gltexture API
    TEST_P(EGLStreamTest, StreamConsumerGLTextureValidationTest)
    {
        EGLWindow *window            = getEGLWindow();
        EGLDisplay display           = window->getDisplay();
        const char *extensionsString = eglQueryString(display, EGL_EXTENSIONS);
        if (strstr(extensionsString, "EGL_KHR_stream_consumer_gltexture") == nullptr)
        {
            std::cout << "Stream consumer gltexture extension not supported" << std::endl;
            return;
        }
    
        const EGLint streamAttributes[] = {
            EGL_CONSUMER_LATENCY_USEC_KHR, 0, EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0, EGL_NONE,
        };
    
        EGLStreamKHR stream = eglCreateStreamKHR(display, streamAttributes);
        ASSERT_EGL_SUCCESS();
    
        EGLBoolean result = eglStreamConsumerGLTextureExternalKHR(display, stream);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
    
        GLuint tex;
        glGenTextures(1, &tex);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
        result = eglStreamConsumerGLTextureExternalKHR(display, stream);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        EGLint state;
        eglQueryStreamKHR(display, stream, EGL_STREAM_STATE_KHR, &state);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(EGL_STREAM_STATE_CONNECTING_KHR, state);
    
        eglDestroyStreamKHR(display, stream);
        ASSERT_EGL_SUCCESS();
    }
    
    // Tests validation of stream consumer gltexture yuv API
    TEST_P(EGLStreamTest, StreamConsumerGLTextureYUVValidationTest)
    {
        EGLWindow *window            = getEGLWindow();
        EGLDisplay display           = window->getDisplay();
        const char *extensionsString = eglQueryString(display, EGL_EXTENSIONS);
        if (strstr(extensionsString, "EGL_NV_stream_consumer_gltexture_yuv") == nullptr)
        {
            std::cout << "Stream consumer gltexture yuv extension not supported" << std::endl;
            return;
        }
    
        const EGLint streamAttributes[] = {
            EGL_CONSUMER_LATENCY_USEC_KHR, 0, EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0, EGL_NONE,
        };
    
        EGLStreamKHR stream = eglCreateStreamKHR(display, streamAttributes);
        ASSERT_EGL_SUCCESS();
    
        EGLAttrib consumerAttributesBad[] = {
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,  // 0
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            0,
            EGL_NONE,
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,  // 5
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            1,
            EGL_NONE,
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,  // 10
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            1,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            9999,
            EGL_NONE,
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,  // 17
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            1,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            0,
            EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
            1,
            EGL_NONE,
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,  // 26
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            2,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            0,
            EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
            0,
            EGL_NONE,
        };
    
        EGLAttrib consumerAttributes[] = {
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            2,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            0,
            EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
            1,
            EGL_NONE,
        };
    
        EGLBoolean result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, &consumerAttributesBad[0]);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_MATCH);
    
        result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, &consumerAttributesBad[5]);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_MATCH);
    
        result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, &consumerAttributesBad[10]);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
    
        result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, &consumerAttributesBad[17]);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_MATCH);
    
        result = eglStreamConsumerGLTextureExternalAttribsNV(display, stream, consumerAttributes);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
    
        GLuint tex[2];
        glGenTextures(2, tex);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[0]);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[1]);
    
        result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, &consumerAttributesBad[26]);
        ASSERT_EGL_FALSE(result);
        ASSERT_EGL_ERROR(EGL_BAD_ACCESS);
    
        result = eglStreamConsumerGLTextureExternalAttribsNV(display, stream, consumerAttributes);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        EGLint state;
        eglQueryStreamKHR(display, stream, EGL_STREAM_STATE_KHR, &state);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(EGL_STREAM_STATE_CONNECTING_KHR, state);
    
        eglDestroyStreamKHR(display, stream);
        ASSERT_EGL_SUCCESS();
    }
    
    // Tests that deleting a texture invalidates the associated stream
    TEST_P(EGLStreamTest, StreamConsumerGLTextureYUVDeletionTest)
    {
        EGLWindow *window            = getEGLWindow();
        EGLDisplay display           = window->getDisplay();
        const char *extensionsString = eglQueryString(display, EGL_EXTENSIONS);
        if (strstr(extensionsString, "EGL_ANGLE_stream_producer_d3d_texture") == nullptr)
        {
            std::cout << "Stream producer d3d texture not supported" << std::endl;
            return;
        }
    
        const EGLint streamAttributes[] = {
            EGL_CONSUMER_LATENCY_USEC_KHR, 0, EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0, EGL_NONE,
        };
    
        EGLStreamKHR stream = eglCreateStreamKHR(display, streamAttributes);
        ASSERT_EGL_SUCCESS();
    
        EGLAttrib consumerAttributes[] = {
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            2,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            0,
            EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
            1,
            EGL_NONE,
        };
    
        GLuint tex[2];
        glGenTextures(2, tex);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[0]);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[1]);
    
        EGLBoolean result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, consumerAttributes);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        EGLAttrib producerAttributes[] = {
            EGL_NONE,
        };
    
        result = eglCreateStreamProducerD3DTextureANGLE(display, stream, producerAttributes);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        EGLint state;
        eglQueryStreamKHR(display, stream, EGL_STREAM_STATE_KHR, &state);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(EGL_STREAM_STATE_EMPTY_KHR, state);
    
        // Delete the first texture, which should be enough to invalidate the stream
        glDeleteTextures(1, tex);
    
        eglQueryStreamKHR(display, stream, EGL_STREAM_STATE_KHR, &state);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(EGL_STREAM_STATE_DISCONNECTED_KHR, state);
    
        eglDestroyStreamKHR(display, stream);
        ASSERT_EGL_SUCCESS();
    }
    
    class D3D11TextureStreamSamplingTest : public ANGLETest
    {
      protected:
        void SetUp() override
        {
            ANGLETest::SetUp();
    
            EGLWindow *window = getEGLWindow();
            mDisplay          = window->getDisplay();
            if (!eglDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_stream_producer_d3d_texture"))
            {
                std::cout << "Stream producer d3d texture not supported" << std::endl;
                return;
            }
    
            glGenRenderbuffers(1, &mRB);
            glBindRenderbuffer(GL_RENDERBUFFER, mRB);
            glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 2, 2);
            glBindRenderbuffer(GL_RENDERBUFFER, 0);
    
            glGenFramebuffers(1, &mFB);
            glBindFramebuffer(GL_FRAMEBUFFER, mFB);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRB);
            ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            glViewport(0, 0, 2, 2);
            glClearColor(1, 0, 0, 1);
            glClear(GL_COLOR_BUFFER_BIT);
            ASSERT_GL_NO_ERROR();
    
            uint32_t pixel;
            glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
            ASSERT_EQ(pixel, 0xff0000ff);
    
            mStream = eglCreateStreamKHR(mDisplay, nullptr);
            ASSERT_EGL_SUCCESS();
    
            EGLDeviceEXT eglDevice;
            eglQueryDisplayAttribEXT(mDisplay, EGL_DEVICE_EXT, (EGLAttrib *)&eglDevice);
            eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, (EGLAttrib *)&mD3D);
        }
    
        void TearDown() override
        {
            EGLBoolean result = eglDestroyStreamKHR(mDisplay, mStream);
            ASSERT_EGL_TRUE(result);
            ASSERT_EGL_SUCCESS();
    
            glDeleteRenderbuffers(1, &mRB);
            glDeleteFramebuffers(1, &mFB);
    
            ANGLETest::TearDown();
        }
    
        EGLDisplay mDisplay  = 0;
        EGLStreamKHR mStream = 0;
        GLuint mRB           = 0;
        GLuint mFB           = 0;
        ID3D11Device *mD3D;
    };
    
    // Test RGBA texture sampling via EGLStreams
    TEST_P(D3D11TextureStreamSamplingTest, RGBA)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
        ANGLE_SKIP_TEST_IF(
            !eglDisplayExtensionEnabled(display, "EGL_ANGLE_stream_producer_d3d_texture"));
    
        constexpr char kVertShader[] = R"(
            attribute vec4 aPos;
            varying vec2 vTexCoord;
            void main()
            {
                gl_Position = aPos;
                vTexCoord = gl_Position.xy * 0.5 + 0.5;
            }
        )";
        constexpr char kFragShader[] = R"(
            #extension GL_NV_EGL_stream_consumer_external : require
            precision mediump float;
            uniform samplerExternalOES uTex;
            varying vec2 vTexCoord;
            void main()
            {
                gl_FragColor = texture2D(uTex, vTexCoord);
            }
        )";
        ANGLE_GL_PROGRAM(prog, kVertShader, kFragShader);
        glUseProgram(prog);
        glUniform1i(glGetUniformLocation(prog, "uTex"), 0);
    
        GLTexture tex;
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
    
        EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(mDisplay, mStream, nullptr);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
        glActiveTexture(GL_TEXTURE3);  // Set to an unused slot to ensure StreamConsumer picked up
                                       // the current slot.
    
        result = eglCreateStreamProducerD3DTextureANGLE(mDisplay, mStream, nullptr);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        // Create and post the d3d11 texture
    
        D3D11_TEXTURE2D_DESC desc = {};
        desc.Width                = 1;
        desc.Height               = 1;
        desc.Format               = DXGI_FORMAT_R8G8B8A8_UNORM;
        desc.MipLevels            = 1;
        desc.ArraySize            = 1;
        desc.SampleDesc.Count     = 1;
        desc.Usage                = D3D11_USAGE_DEFAULT;
        desc.BindFlags            = D3D11_BIND_SHADER_RESOURCE;
    
        constexpr uint8_t texData[] = {0x10, 0x20, 0x30, 0x40};
        D3D11_SUBRESOURCE_DATA subres;
        subres.pSysMem     = texData;
        subres.SysMemPitch = 4;
    
        ID3D11Texture2D *texture = nullptr;
        (void)mD3D->CreateTexture2D(&desc, &subres, &texture);
        ASSERT_NE(nullptr, texture);
    
        result = eglStreamPostD3DTextureANGLE(mDisplay, mStream, (void *)texture, nullptr);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        // Sample and test
    
        result = eglStreamConsumerAcquireKHR(mDisplay, mStream);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        drawQuad(prog, "aPos", 0.0);
    
        uint32_t pixel;
        glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
        ASSERT_GL_NO_ERROR();
        ASSERT_EQ(pixel, static_cast<uint32_t>(0x40302010));
    
        result = eglStreamConsumerReleaseKHR(mDisplay, mStream);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    }
    
    // Test NV12 texture sampling via EGLStreams
    TEST_P(D3D11TextureStreamSamplingTest, NV12)
    {
        EGLWindow *window  = getEGLWindow();
        EGLDisplay display = window->getDisplay();
        ANGLE_SKIP_TEST_IF(
            !eglDisplayExtensionEnabled(display, "EGL_ANGLE_stream_producer_d3d_texture"));
        ANGLE_SKIP_TEST_IF(!CheckNV12TextureSupport(mD3D));
    
        constexpr char kVertShader[] = R"(
            attribute vec4 aPos;
            varying vec2 vTexCoord;
            void main()
            {
                gl_Position = aPos;
                vTexCoord = gl_Position.xy * 0.5 + 0.5;
            }
        )";
        const char kFragShader[]     = R"(
            #extension GL_NV_EGL_stream_consumer_external : require
            precision mediump float;
            uniform samplerExternalOES uTexY;
            uniform samplerExternalOES uTexUV;
            varying vec2 vTexCoord;
            void main()
            {
                gl_FragColor.r = texture2D(uTexY, vTexCoord).r;
                gl_FragColor.gb = texture2D(uTexUV, vTexCoord).rg;
                gl_FragColor.a = 1.0;
            }
        )";
        ANGLE_GL_PROGRAM(prog, kVertShader, kFragShader);
        glUseProgram(prog);
        glUniform1i(glGetUniformLocation(prog, "uTexY"), 0);
        glUniform1i(glGetUniformLocation(prog, "uTexUV"), 1);
    
        ASSERT_GL_NO_ERROR();
    
        GLTexture tex[2];
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[0]);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[1]);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
    
        glActiveTexture(GL_TEXTURE3);  // Set to an unused slot to ensure StreamConsumer picks up
                                       // the current slot.
        constexpr EGLAttrib consumerAttributes[] = {
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            2,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            0,
            EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
            1,
            EGL_NONE,
        };
        EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
            mDisplay, mStream, const_cast<EGLAttrib *>(consumerAttributes));
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        result = eglCreateStreamProducerD3DTextureANGLE(mDisplay, mStream, nullptr);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        // Create and post the d3d11 texture
    
        D3D11_TEXTURE2D_DESC desc = {};
        desc.Width                = 2;
        desc.Height               = 2;
        desc.Format               = DXGI_FORMAT_NV12;
        desc.MipLevels            = 1;
        desc.ArraySize            = 1;
        desc.SampleDesc.Count     = 1;
        desc.Usage                = D3D11_USAGE_DEFAULT;
        desc.BindFlags            = D3D11_BIND_SHADER_RESOURCE;
    
        /* DXGI_FORMAT_NV12:
         * Width and height must be even.
         * Direct3D 11 staging resources and initData parameters for this format use
         * (rowPitch * (height + (height / 2))) bytes.
         * The first (SysMemPitch * height) bytes are the Y plane, the remaining
         * (SysMemPitch * (height / 2)) bytes are the UV plane.
         */
        constexpr uint8_t texData[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60};
        D3D11_SUBRESOURCE_DATA subres;
        subres.pSysMem     = texData;
        subres.SysMemPitch = 2;
    
        ID3D11Texture2D *texture = nullptr;
        (void)mD3D->CreateTexture2D(&desc, &subres, &texture);
        ASSERT_NE(nullptr, texture);
    
        result = eglStreamPostD3DTextureANGLE(mDisplay, mStream, (void *)texture, nullptr);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        // Sample and test
    
        result = eglStreamConsumerAcquireKHR(mDisplay, mStream);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        drawQuad(prog, "aPos", 0.0);
        uint32_t pixel;
        glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
        ASSERT_EQ(pixel, 0xff605010);
    
        glReadPixels(1, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
        ASSERT_EQ(pixel, 0xff605020);
    
        glReadPixels(0, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
        ASSERT_EQ(pixel, 0xff605030);
    
        glReadPixels(1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
        ASSERT_EQ(pixel, 0xff605040);
    
        result = eglStreamConsumerReleaseKHR(mDisplay, mStream);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    }
    
    // End2end test for rendering an NV12 texture. Renders a YUV quad, reads back the RGB values, and
    // ensures they are correct
    TEST_P(EGLStreamTest, StreamProducerTextureNV12End2End)
    {
        EGLWindow *window            = getEGLWindow();
        EGLDisplay display           = window->getDisplay();
        ANGLE_SKIP_TEST_IF(
            !eglDisplayExtensionEnabled(display, "EGL_ANGLE_stream_producer_d3d_texture"));
    
        bool useESSL3Shaders =
            getClientMajorVersion() >= 3 && extensionEnabled("GL_OES_EGL_image_external_essl3");
    
        // yuv to rgb conversion shader using Microsoft's given conversion formulas
        std::string yuvVS, yuvPS;
        if (useESSL3Shaders)
        {
            yuvVS =
                "#version 300 es\n"
                "in highp vec4 position;\n"
                "out vec2 texcoord;\n"
                "void main(void)\n"
                "{\n"
                "    gl_Position = position;\n"
                "    texcoord = (position.xy * 0.5) + 0.5;\n"
                "    texcoord.y = 1.0 - texcoord.y;\n"
                "}\n";
            yuvPS =
                "#version 300 es\n"
                "#extension GL_OES_EGL_image_external_essl3 : require\n"
                "#extension GL_NV_EGL_stream_consumer_external : require\n"
                "precision highp float;\n"
                "in vec2 texcoord;\n"
                "out vec4 color;\n"
                "uniform samplerExternalOES y;\n"
                "uniform samplerExternalOES uv\n;"
                "void main(void)\n"
                "{\n"
                "    float c = texture(y, texcoord).r - (16.0 / 256.0);\n"
                "    float d = texture(uv, texcoord).r - 0.5;\n"
                "    float e = texture(uv, texcoord).g - 0.5;\n"
                "    float r = 1.164383 * c + 1.596027 * e;\n"
                "    float g = 1.164383 * c - 0.391762 * d - 0.812968 * e;\n"
                "    float b = 1.164383 * c + 2.017232 * d;\n"
                "    color = vec4(r, g, b, 1.0);\n"
                "}\n";
        }
        else
        {
            yuvVS =
                "attribute highp vec4 position;\n"
                "varying vec2 texcoord;\n"
                "void main(void)\n"
                "{\n"
                "    gl_Position = position;\n"
                "    texcoord = (position.xy * 0.5) + 0.5;\n"
                "    texcoord.y = 1.0 - texcoord.y;\n"
                "}\n";
    
            yuvPS =
                "#extension GL_NV_EGL_stream_consumer_external : require\n"
                "precision highp float;\n"
                "varying vec2 texcoord;\n"
                "uniform samplerExternalOES y;\n"
                "uniform samplerExternalOES uv\n;"
                "void main(void)\n"
                "{\n"
                "    float c = texture2D(y, texcoord).r - (16.0 / 256.0);\n"
                "    float d = texture2D(uv, texcoord).r - 0.5;\n"
                "    float e = texture2D(uv, texcoord).g - 0.5;\n"
                "    float r = 1.164383 * c + 1.596027 * e;\n"
                "    float g = 1.164383 * c - 0.391762 * d - 0.812968 * e;\n"
                "    float b = 1.164383 * c + 2.017232 * d;\n"
                "    gl_FragColor = vec4(r, g, b, 1.0);\n"
                "}\n";
        }
    
        GLuint program = CompileProgram(yuvVS, yuvPS);
        ASSERT_NE(0u, program);
        GLuint yUniform  = glGetUniformLocation(program, "y");
        GLuint uvUniform = glGetUniformLocation(program, "uv");
    
        // Fetch the D3D11 device
        EGLDeviceEXT eglDevice;
        eglQueryDisplayAttribEXT(display, EGL_DEVICE_EXT, (EGLAttrib *)&eglDevice);
        ID3D11Device *device;
        eglQueryDeviceAttribEXT(eglDevice, EGL_D3D11_DEVICE_ANGLE, (EGLAttrib *)&device);
    
        ANGLE_SKIP_TEST_IF(!CheckNV12TextureSupport(device));
    
        // Create the NV12 D3D11 texture
        D3D11_TEXTURE2D_DESC desc;
        desc.Width              = yuvtest_width;
        desc.Height             = yuvtest_height;
        desc.Format             = DXGI_FORMAT_NV12;
        desc.MipLevels          = 1;
        desc.ArraySize          = 1;
        desc.SampleDesc.Count   = 1;
        desc.SampleDesc.Quality = 0;
        desc.Usage              = D3D11_USAGE_DEFAULT;
        desc.BindFlags          = D3D11_BIND_SHADER_RESOURCE;
        desc.CPUAccessFlags     = 0;
        desc.MiscFlags          = 0;
    
        D3D11_SUBRESOURCE_DATA subres;
        subres.pSysMem          = yuvtest_data;
        subres.SysMemPitch      = yuvtest_width;
        subres.SysMemSlicePitch = yuvtest_width * yuvtest_height * 3 / 2;
    
        ID3D11Texture2D *texture = nullptr;
        (void)device->CreateTexture2D(&desc, &subres, &texture);
        ASSERT_NE(nullptr, texture);
    
        // Create the stream
        const EGLint streamAttributes[] = {
            EGL_CONSUMER_LATENCY_USEC_KHR, 0, EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0, EGL_NONE,
        };
    
        EGLStreamKHR stream = eglCreateStreamKHR(display, streamAttributes);
        ASSERT_EGL_SUCCESS();
    
        EGLAttrib consumerAttributes[] = {
            EGL_COLOR_BUFFER_TYPE,
            EGL_YUV_BUFFER_EXT,
            EGL_YUV_NUMBER_OF_PLANES_EXT,
            2,
            EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
            0,
            EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
            1,
            EGL_NONE,
        };
    
        GLuint tex[2];
        glGenTextures(2, tex);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[0]);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex[1]);
        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ASSERT_GL_NO_ERROR();
    
        EGLBoolean result =
            eglStreamConsumerGLTextureExternalAttribsNV(display, stream, consumerAttributes);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        EGLAttrib producerAttributes[] = {
            EGL_NONE,
        };
    
        result = eglCreateStreamProducerD3DTextureANGLE(display, stream, producerAttributes);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        // Insert the frame
        EGLAttrib frameAttributes[] = {
            EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, 0, EGL_NONE,
        };
        result = eglStreamPostD3DTextureANGLE(display, stream, (void *)texture, frameAttributes);
        ASSERT_EGL_TRUE(result);
        ASSERT_EGL_SUCCESS();
    
        EGLint state;
        eglQueryStreamKHR(display, stream, EGL_STREAM_STATE_KHR, &state);
        ASSERT_EGL_SUCCESS();
        ASSERT_EQ(EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR, state);
    
        eglStreamConsumerAcquireKHR(display, stream);
        ASSERT_EGL_SUCCESS();
    
        glUseProgram(program);
        glUniform1i(yUniform, 0);
        glUniform1i(uvUniform, 1);
        drawQuad(program, "position", 0.0f);
        ASSERT_GL_NO_ERROR();
    
        eglStreamConsumerReleaseKHR(display, stream);
        ASSERT_EGL_SUCCESS();
    
        eglSwapBuffers(display, window->getSurface());
        SafeRelease(texture);
    }
    
    ANGLE_INSTANTIATE_TEST(EGLStreamTest,
                           ES2_D3D9(),
                           ES2_D3D11(),
                           ES3_D3D11(),
                           ES2_OPENGL(),
                           ES3_OPENGL());
    ANGLE_INSTANTIATE_TEST(D3D11TextureStreamSamplingTest, ES2_D3D11(), ES3_D3D11());
    }  // anonymous namespace