// // 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 #include #include "media/yuvtest.inl" #include "OSWindow.h" #include "test_utils/ANGLETest.h" using namespace angle; namespace { 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_nv12") == nullptr) { std::cout << "Stream producer d3d nv12 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 = eglCreateStreamProducerD3DTextureNV12ANGLE(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(); } // 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(); if (!eglDisplayExtensionEnabled(display, "EGL_ANGLE_stream_producer_d3d_texture_nv12")) { std::cout << "Stream producer d3d nv12 texture not supported" << std::endl; return; } 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); // Create the NV12 D3D11 texture HRESULT res; 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; res = device->CreateTexture2D(&desc, &subres, &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 = eglCreateStreamProducerD3DTextureNV12ANGLE(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 = eglStreamPostD3DTextureNV12ANGLE(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()); } // anonymous namespace