// // Copyright (c) 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. // // Stream.cpp: Implements the egl::Stream class, representing the stream // where frames are streamed in. Implements EGLStreanKHR. #include "libANGLE/Stream.h" #include #include #include "common/debug.h" #include "common/mathutil.h" #include "common/platform.h" #include "common/utilities.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/renderer/DisplayImpl.h" #include "libANGLE/renderer/StreamProducerImpl.h" namespace egl { Stream::Stream(Display *display, const AttributeMap &attribs) : mDisplay(display), mProducerImplementation(nullptr), mState(EGL_STREAM_STATE_CREATED_KHR), mProducerFrame(0), mConsumerFrame(0), mConsumerLatency(attribs.getAsInt(EGL_CONSUMER_LATENCY_USEC_KHR, 0)), mConsumerAcquireTimeout(attribs.getAsInt(EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0)), mPlaneCount(0), mConsumerType(ConsumerType::NoConsumer), mProducerType(ProducerType::NoProducer) { for (auto &plane : mPlanes) { plane.textureUnit = -1; plane.texture = nullptr; } } Stream::~Stream() { SafeDelete(mProducerImplementation); for (auto &plane : mPlanes) { if (plane.texture != nullptr) { plane.texture->releaseStream(); } } } void Stream::setConsumerLatency(EGLint latency) { mConsumerLatency = latency; } EGLint Stream::getConsumerLatency() const { return mConsumerLatency; } EGLuint64KHR Stream::getProducerFrame() const { return mProducerFrame; } EGLuint64KHR Stream::getConsumerFrame() const { return mConsumerFrame; } EGLenum Stream::getState() const { return mState; } void Stream::setConsumerAcquireTimeout(EGLint timeout) { mConsumerAcquireTimeout = timeout; } EGLint Stream::getConsumerAcquireTimeout() const { return mConsumerAcquireTimeout; } Stream::ProducerType Stream::getProducerType() const { return mProducerType; } Stream::ConsumerType Stream::getConsumerType() const { return mConsumerType; } EGLint Stream::getPlaneCount() const { return mPlaneCount; } rx::StreamProducerImpl *Stream::getImplementation() { return mProducerImplementation; } Error Stream::createConsumerGLTextureExternal(const AttributeMap &attributes, gl::Context *context) { ASSERT(mState == EGL_STREAM_STATE_CREATED_KHR); ASSERT(mConsumerType == ConsumerType::NoConsumer); ASSERT(mProducerType == ProducerType::NoProducer); ASSERT(context != nullptr); const auto &glState = context->getGLState(); EGLenum bufferType = attributes.getAsInt(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER); if (bufferType == EGL_RGB_BUFFER) { mPlanes[0].texture = glState.getTargetTexture(GL_TEXTURE_EXTERNAL_OES); ASSERT(mPlanes[0].texture != nullptr); mPlanes[0].texture->bindStream(this); mConsumerType = ConsumerType::GLTextureRGB; mPlaneCount = 1; } else { mPlaneCount = attributes.getAsInt(EGL_YUV_NUMBER_OF_PLANES_EXT, 2); ASSERT(mPlaneCount <= 3); for (EGLint i = 0; i < mPlaneCount; i++) { // Fetch all the textures mPlanes[i].textureUnit = attributes.getAsInt(EGL_YUV_PLANE0_TEXTURE_UNIT_NV + i, -1); if (mPlanes[i].textureUnit != EGL_NONE) { mPlanes[i].texture = glState.getSamplerTexture(mPlanes[i].textureUnit, GL_TEXTURE_EXTERNAL_OES); ASSERT(mPlanes[i].texture != nullptr); } } // Bind them to the stream for (EGLint i = 0; i < mPlaneCount; i++) { if (mPlanes[i].textureUnit != EGL_NONE) { mPlanes[i].texture->bindStream(this); } } mConsumerType = ConsumerType::GLTextureYUV; } mContext = context; mState = EGL_STREAM_STATE_CONNECTING_KHR; return NoError(); } Error Stream::createProducerD3D11TextureNV12(const AttributeMap &attributes) { ASSERT(mState == EGL_STREAM_STATE_CONNECTING_KHR); ASSERT(mConsumerType == ConsumerType::GLTextureYUV); ASSERT(mProducerType == ProducerType::NoProducer); ASSERT(mPlaneCount == 2); mProducerImplementation = mDisplay->getImplementation()->createStreamProducerD3DTextureNV12( mConsumerType, attributes); mProducerType = ProducerType::D3D11TextureNV12; mState = EGL_STREAM_STATE_EMPTY_KHR; return NoError(); } // Called when the consumer of this stream starts using the stream Error Stream::consumerAcquire(const gl::Context *context) { ASSERT(mState == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR || mState == EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR); ASSERT(mConsumerType == ConsumerType::GLTextureRGB || mConsumerType == ConsumerType::GLTextureYUV); ASSERT(mProducerType == ProducerType::D3D11TextureNV12); mState = EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR; mConsumerFrame++; // Bind the planes to the gl textures for (int i = 0; i < mPlaneCount; i++) { if (mPlanes[i].texture != nullptr) { auto result = mPlanes[i].texture->acquireImageFromStream( context, mProducerImplementation->getGLFrameDescription(i)); ANGLE_TRY(Error(result)); } } return NoError(); } Error Stream::consumerRelease(const gl::Context *context) { ASSERT(mState == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR || mState == EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR); ASSERT(mConsumerType == ConsumerType::GLTextureRGB || mConsumerType == ConsumerType::GLTextureYUV); ASSERT(mProducerType == ProducerType::D3D11TextureNV12); // Release the images for (int i = 0; i < mPlaneCount; i++) { if (mPlanes[i].texture != nullptr) { auto result = mPlanes[i].texture->releaseImageFromStream(context); ANGLE_TRY(Error(result)); } } return NoError(); } bool Stream::isConsumerBoundToContext(const gl::Context *context) const { ASSERT(context != nullptr); return (context == mContext); } Error Stream::validateD3D11NV12Texture(void *texture) const { ASSERT(mConsumerType == ConsumerType::GLTextureRGB || mConsumerType == ConsumerType::GLTextureYUV); ASSERT(mProducerType == ProducerType::D3D11TextureNV12); ASSERT(mProducerImplementation != nullptr); return mProducerImplementation->validateD3DNV12Texture(texture); } Error Stream::postD3D11NV12Texture(void *texture, const AttributeMap &attributes) { ASSERT(mConsumerType == ConsumerType::GLTextureRGB || mConsumerType == ConsumerType::GLTextureYUV); ASSERT(mProducerType == ProducerType::D3D11TextureNV12); mProducerImplementation->postD3DNV12Texture(texture, attributes); mProducerFrame++; mState = EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR; return NoError(); } // This is called when a texture object associated with this stream is destroyed. Even if multiple // textures are bound, one being destroyed invalidates the stream, so all the remaining textures // will be released and the stream will be invalidated. void Stream::releaseTextures() { for (auto &plane : mPlanes) { if (plane.texture != nullptr) { plane.texture->releaseStream(); plane.texture = nullptr; } } mConsumerType = ConsumerType::NoConsumer; mProducerType = ProducerType::NoProducer; mState = EGL_STREAM_STATE_DISCONNECTED_KHR; } } // namespace egl