| // | |
| // 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 "media/yuvtest.inl" | |
| #include "test_utils/ANGLETest.h" | |
| #include "test_utils/gl_raii.h" | |
| #include "util/EGLWindow.h" | |
| #include "util/OSWindow.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 testSetUp() override | |
| { | |
| EGLWindow *window = getEGLWindow(); | |
| mDisplay = window->getDisplay(); | |
| if (!IsEGLDisplayExtensionEnabled(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 testTearDown() override | |
| { | |
| EGLBoolean result = eglDestroyStreamKHR(mDisplay, mStream); | |
| ASSERT_EGL_TRUE(result); | |
| ASSERT_EGL_SUCCESS(); | |
| glDeleteRenderbuffers(1, &mRB); | |
| glDeleteFramebuffers(1, &mFB); | |
| } | |
| 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( | |
| !IsEGLDisplayExtensionEnabled(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( | |
| !IsEGLDisplayExtensionEnabled(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( | |
| !IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_stream_producer_d3d_texture")); | |
| bool useESSL3Shaders = | |
| getClientMajorVersion() >= 3 && IsGLExtensionEnabled("GL_OES_EGL_image_external_essl3"); | |
| // yuv to rgb conversion shader using Microsoft's given conversion formulas | |
| const char *yuvVS = nullptr; | |
| const char *yuvPS = nullptr; | |
| 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"; | |
| } | |
| ANGLE_GL_PROGRAM(program, yuvVS, yuvPS); | |
| 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(), | |
| ES2_VULKAN()); | |
| ANGLE_INSTANTIATE_TEST(D3D11TextureStreamSamplingTest, ES2_D3D11(), ES3_D3D11(), ES31_D3D11()); | |
| } // anonymous namespace |