| // |
| // 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. |
| // |
| |
| // Stream.cpp: Implements the egl::Stream class, representing the stream |
| // where frames are streamed in. Implements EGLStreanKHR. |
| |
| #include "libANGLE/Stream.h" |
| |
| #include <EGL/eglext.h> |
| #include <platform/Platform.h> |
| |
| #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) |
| : mLabel(nullptr), |
| 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::setLabel(EGLLabelKHR label) |
| { |
| mLabel = label; |
| } |
| |
| EGLLabelKHR Stream::getLabel() const |
| { |
| return mLabel; |
| } |
| |
| 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->getState(); |
| EGLenum bufferType = attributes.getAsInt(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER); |
| if (bufferType == EGL_RGB_BUFFER) |
| { |
| mPlanes[0].texture = glState.getTargetTexture(gl::TextureType::External); |
| 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::TextureType::External); |
| 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::createProducerD3D11Texture(const AttributeMap &attributes) |
| { |
| ASSERT(mState == EGL_STREAM_STATE_CONNECTING_KHR); |
| ASSERT(mConsumerType == ConsumerType::GLTextureRGB || |
| mConsumerType == ConsumerType::GLTextureYUV); |
| ASSERT(mProducerType == ProducerType::NoProducer); |
| |
| mProducerImplementation = |
| mDisplay->getImplementation()->createStreamProducerD3DTexture(mConsumerType, attributes); |
| mProducerType = ProducerType::D3D11Texture; |
| 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::D3D11Texture); |
| |
| 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) |
| { |
| ANGLE_TRY(ResultToEGL(mPlanes[i].texture->acquireImageFromStream( |
| context, mProducerImplementation->getGLFrameDescription(i)))); |
| } |
| } |
| |
| 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::D3D11Texture); |
| |
| // Release the images |
| for (int i = 0; i < mPlaneCount; i++) |
| { |
| if (mPlanes[i].texture != nullptr) |
| { |
| ANGLE_TRY(ResultToEGL(mPlanes[i].texture->releaseImageFromStream(context))); |
| } |
| } |
| |
| return NoError(); |
| } |
| |
| bool Stream::isConsumerBoundToContext(const gl::Context *context) const |
| { |
| ASSERT(context != nullptr); |
| return (context == mContext); |
| } |
| |
| Error Stream::validateD3D11Texture(void *texture, const AttributeMap &attributes) const |
| { |
| ASSERT(mConsumerType == ConsumerType::GLTextureRGB || |
| mConsumerType == ConsumerType::GLTextureYUV); |
| ASSERT(mProducerType == ProducerType::D3D11Texture); |
| ASSERT(mProducerImplementation != nullptr); |
| |
| return mProducerImplementation->validateD3DTexture(texture, attributes); |
| } |
| |
| Error Stream::postD3D11Texture(void *texture, const AttributeMap &attributes) |
| { |
| ASSERT(mConsumerType == ConsumerType::GLTextureRGB || |
| mConsumerType == ConsumerType::GLTextureYUV); |
| ASSERT(mProducerType == ProducerType::D3D11Texture); |
| |
| mProducerImplementation->postD3DTexture(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 |