Bug 1322746 - Add general ID3D11Texture2D to EGLStream support to ANGLE. - r=jerry draft
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 17 Jul 2017 19:14:54 -0700
changeset 656201 3f8b8603ed7908a4855280f38afb04b4717b6528
parent 656200 ac5d7eaaa8e219c74232beb4319f2d22fb456cf1
child 656202 895673bc249f02a9718b69fa958df1441a1209a8
child 656213 1971277ea495c37861302b4a318728c5e4e2ef44
child 656965 cf8f7c4d4efa3dfe9b63870930d34f73720f885e
push id77113
push userbmo:jgilbert@mozilla.com
push dateWed, 30 Aug 2017 20:41:02 +0000
reviewersjerry
bugs1322746
milestone57.0a1
Bug 1322746 - Add general ID3D11Texture2D to EGLStream support to ANGLE. - r=jerry Also expose NV12 support, selecting Y or UV planes with EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG. MozReview-Commit-ID: IYzPAFEc84d
gfx/angle/src/libANGLE/Stream.cpp
gfx/angle/src/libANGLE/Stream.h
gfx/angle/src/libANGLE/renderer/StreamProducerImpl.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerNV12.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerNV12.h
gfx/angle/src/libANGLE/validationEGL.cpp
--- a/gfx/angle/src/libANGLE/Stream.cpp
+++ b/gfx/angle/src/libANGLE/Stream.cpp
@@ -120,17 +120,17 @@ Error Stream::createConsumerGLTextureExt
     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;
+        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
@@ -158,28 +158,29 @@ Error Stream::createConsumerGLTextureExt
     mState   = EGL_STREAM_STATE_CONNECTING_KHR;
 
     return Error(EGL_SUCCESS);
 }
 
 Error Stream::createProducerD3D11TextureNV12(const AttributeMap &attributes)
 {
     ASSERT(mState == EGL_STREAM_STATE_CONNECTING_KHR);
-    ASSERT(mConsumerType == ConsumerType::GLTextureYUV);
+    ASSERT(mConsumerType == ConsumerType::GLTextureRGB ||
+           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 Error(EGL_SUCCESS);
 }
 
+
 // Called when the consumer of this stream starts using the stream
 Error Stream::consumerAcquire()
 {
     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);
@@ -221,24 +222,24 @@ Error Stream::consumerRelease()
 }
 
 bool Stream::isConsumerBoundToContext(const gl::Context *context) const
 {
     ASSERT(context != nullptr);
     return (context == mContext);
 }
 
-Error Stream::validateD3D11NV12Texture(void *texture) const
+Error Stream::validateD3D11NV12Texture(void *texture, const AttributeMap &attributes) const
 {
     ASSERT(mConsumerType == ConsumerType::GLTextureRGB ||
            mConsumerType == ConsumerType::GLTextureYUV);
     ASSERT(mProducerType == ProducerType::D3D11TextureNV12);
     ASSERT(mProducerImplementation != nullptr);
 
-    return mProducerImplementation->validateD3DNV12Texture(texture);
+    return mProducerImplementation->validateD3DNV12Texture(texture, attributes);
 }
 
 Error Stream::postD3D11NV12Texture(void *texture, const AttributeMap &attributes)
 {
     ASSERT(mConsumerType == ConsumerType::GLTextureRGB ||
            mConsumerType == ConsumerType::GLTextureYUV);
     ASSERT(mProducerType == ProducerType::D3D11TextureNV12);
 
--- a/gfx/angle/src/libANGLE/Stream.h
+++ b/gfx/angle/src/libANGLE/Stream.h
@@ -90,17 +90,17 @@ class Stream final : angle::NonCopyable
     Error consumerAcquire();
     Error consumerRelease();
 
     // Some consumers are bound to GL contexts. This validates that a given context is bound to the
     // stream's consumer
     bool isConsumerBoundToContext(const gl::Context *context) const;
 
     // Producer methods
-    Error validateD3D11NV12Texture(void *texture) const;
+    Error validateD3D11NV12Texture(void *texture, const AttributeMap &attributes) const;
     Error postD3D11NV12Texture(void *texture, const AttributeMap &attributes);
 
   private:
     // Associated display
     Display *mDisplay;
 
     // Producer Implementation
     rx::StreamProducerImpl *mProducerImplementation;
--- a/gfx/angle/src/libANGLE/renderer/StreamProducerImpl.h
+++ b/gfx/angle/src/libANGLE/renderer/StreamProducerImpl.h
@@ -18,17 +18,17 @@ namespace rx
 class StreamProducerImpl : angle::NonCopyable
 {
   public:
     explicit StreamProducerImpl() {}
     virtual ~StreamProducerImpl() {}
 
     // Validates the ability for the producer to accept an arbitrary pointer to a frame. All
     // pointers should be validated through this function before being used to produce a frame.
-    virtual egl::Error validateD3DNV12Texture(void *pointer) const = 0;
+    virtual egl::Error validateD3DNV12Texture(void *pointer, const egl::AttributeMap &attributes) const = 0;
 
     // Constructs a frame from an arbitrary external pointer that points to producer specific frame
     // data. Replaces the internal frame with the new one.
     virtual void postD3DNV12Texture(void *pointer, const egl::AttributeMap &attributes) = 0;
 
     // Returns an OpenGL texture interpretation of some frame attributes for the purpose of
     // constructing an OpenGL texture from a frame. Depending on the producer and consumer, some
     // frames may have multiple "planes" with different OpenGL texture representations.
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerNV12.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerNV12.cpp
@@ -10,88 +10,143 @@
 
 #include "common/utilities.h"
 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
 
 namespace rx
 {
 
+static egl::Stream::GLTextureDescription getGLDescFromTex(ID3D11Texture2D* tex,
+                                                          UINT planeIndex,
+                                                          const char** const out_error)
+{
+    *out_error = "Undocumented error";
+
+    egl::Stream::GLTextureDescription ret = { 0 };
+    if (!tex)
+    {
+        *out_error = "Texture is null";
+        return ret;
+    }
+
+    D3D11_TEXTURE2D_DESC desc;
+    tex->GetDesc(&desc);
+
+    if (desc.Width < 1 || desc.Height < 1)
+    {
+        *out_error = "Width or height < 1";
+        return ret;
+    }
+
+    ret.width = desc.Width;
+    ret.height = desc.Height;
+    ret.mipLevels = 0;
+
+    UINT maxPlaneIndex = 0;
+    switch (desc.Format) {
+    case DXGI_FORMAT_NV12:
+        // The UV plane of NV12 textures has half the width/height of the Y plane
+        if ((desc.Width % 2) != 0 || (desc.Height % 2) != 0)
+        {
+            *out_error = "NV12 tetxures must have even width and height.";
+            break; // Bad width/height.
+        }
+
+        maxPlaneIndex = 1;
+        if (planeIndex == 0)
+        {
+            ret.internalFormat = GL_R8;
+        }
+        else
+        {
+            ret.internalFormat = GL_RG8;
+            ret.width  /= 2;
+            ret.height /= 2;
+        }
+        break;
+
+    case DXGI_FORMAT_R8_UNORM:
+        ret.internalFormat = GL_R8;
+        break;
+    case DXGI_FORMAT_R8G8_UNORM:
+        ret.internalFormat = GL_RG8;
+        break;
+    case DXGI_FORMAT_R8G8B8A8_UNORM:
+        ret.internalFormat = GL_RGBA8;
+        break;
+
+    default:
+        *out_error = "Unsupported format";
+        return ret;
+    }
+
+    if (planeIndex > maxPlaneIndex)
+    {
+        // Just kidding, there's no plane out there.
+        ret.internalFormat = 0;
+        *out_error = "Plane out of range";
+    }
+
+    return ret;
+}
+
+
 StreamProducerNV12::StreamProducerNV12(Renderer11 *renderer)
-    : mRenderer(renderer), mTexture(nullptr), mArraySlice(0), mTextureWidth(0), mTextureHeight(0)
+    : mRenderer(renderer), mTexture(nullptr), mArraySlice(0), mPlaneOffset(0)
 {
 }
 
 StreamProducerNV12::~StreamProducerNV12()
 {
     SafeRelease(mTexture);
 }
 
-egl::Error StreamProducerNV12::validateD3DNV12Texture(void *pointer) const
+egl::Error StreamProducerNV12::validateD3DNV12Texture(void *pointer, const egl::AttributeMap &attributes) const
 {
     ID3D11Texture2D *textureD3D = static_cast<ID3D11Texture2D *>(pointer);
 
     // Check that the texture originated from our device
     ID3D11Device *device;
     textureD3D->GetDevice(&device);
     if (device != mRenderer->getDevice())
     {
         return egl::Error(EGL_BAD_PARAMETER, "Texture not created on ANGLE D3D device");
     }
 
-    // Get the description and validate it
-    D3D11_TEXTURE2D_DESC desc;
-    textureD3D->GetDesc(&desc);
-    if (desc.Format != DXGI_FORMAT_NV12)
+    const auto planeId = static_cast<UINT>(attributes.get(EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 0));
+    const char* errorText;
+    const auto glDesc = getGLDescFromTex(textureD3D, planeId, &errorText);
+    if (!glDesc.internalFormat)
     {
-        return egl::Error(EGL_BAD_PARAMETER, "Texture format not DXGI_FORMAT_NV12");
+        return egl::Error(EGL_BAD_PARAMETER, errorText);
     }
-    if (desc.Width < 1 || desc.Height < 1)
-    {
-        return egl::Error(EGL_BAD_PARAMETER, "Texture is of size 0");
-    }
-    if ((desc.Width % 2) != 0 || (desc.Height % 2) != 0)
-    {
-        return egl::Error(EGL_BAD_PARAMETER, "Texture dimensions are not even");
-    }
+
     return egl::Error(EGL_SUCCESS);
 }
 
 void StreamProducerNV12::postD3DNV12Texture(void *pointer, const egl::AttributeMap &attributes)
 {
     ASSERT(pointer != nullptr);
     ID3D11Texture2D *textureD3D = static_cast<ID3D11Texture2D *>(pointer);
 
-    // Check that the texture originated from our device
-    ID3D11Device *device;
-    textureD3D->GetDevice(&device);
-
-    // Get the description
-    D3D11_TEXTURE2D_DESC desc;
-    textureD3D->GetDesc(&desc);
-
     // Release the previous texture if there is one
     SafeRelease(mTexture);
 
     mTexture = textureD3D;
     mTexture->AddRef();
-    mTextureWidth  = desc.Width;
-    mTextureHeight = desc.Height;
-    mArraySlice    = static_cast<UINT>(attributes.get(EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, 0));
+    mPlaneOffset = static_cast<UINT>(attributes.get(EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 0));
+    mArraySlice = static_cast<UINT>(attributes.get(EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, 0));
 }
 
 egl::Stream::GLTextureDescription StreamProducerNV12::getGLFrameDescription(int planeIndex)
 {
-    // The UV plane of NV12 textures has half the width/height of the Y plane
-    egl::Stream::GLTextureDescription desc;
-    desc.width          = (planeIndex == 0) ? mTextureWidth : (mTextureWidth / 2);
-    desc.height         = (planeIndex == 0) ? mTextureHeight : (mTextureHeight / 2);
-    desc.internalFormat = (planeIndex == 0) ? GL_R8 : GL_RG8;
-    desc.mipLevels      = 0;
-    return desc;
+    const char* errorText;
+    return getGLDescFromTex(mTexture, static_cast<UINT>(planeIndex + mPlaneOffset),
+                            &errorText);
 }
 
 ID3D11Texture2D *StreamProducerNV12::getD3DTexture()
 {
     return mTexture;
 }
 
 UINT StreamProducerNV12::getArraySlice()
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerNV12.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StreamProducerNV12.h
@@ -16,29 +16,28 @@ namespace rx
 class Renderer11;
 
 class StreamProducerNV12 : public StreamProducerImpl
 {
   public:
     StreamProducerNV12(Renderer11 *renderer);
     ~StreamProducerNV12() override;
 
-    egl::Error validateD3DNV12Texture(void *pointer) const override;
+    egl::Error validateD3DNV12Texture(void *pointer, const egl::AttributeMap &attributes) const override;
     void postD3DNV12Texture(void *pointer, const egl::AttributeMap &attributes) override;
     egl::Stream::GLTextureDescription getGLFrameDescription(int planeIndex) override;
 
     // Gets a pointer to the internal D3D texture
     ID3D11Texture2D *getD3DTexture();
 
     // Gets the slice index for the D3D texture that the frame is in
     UINT getArraySlice();
 
   private:
     Renderer11 *mRenderer;
 
     ID3D11Texture2D *mTexture;
     UINT mArraySlice;
-    UINT mTextureWidth;
-    UINT mTextureHeight;
+    UINT mPlaneOffset;
 };
 }  // namespace rx
 
 #endif  // LIBANGLE_RENDERER_D3D_D3D11_STREAM11_H_
--- a/gfx/angle/src/libANGLE/validationEGL.cpp
+++ b/gfx/angle/src/libANGLE/validationEGL.cpp
@@ -1510,19 +1510,32 @@ Error ValidateCreateStreamProducerD3DTex
         return Error(EGL_BAD_ATTRIBUTE, "Invalid attribute");
     }
 
     if (stream->getState() != EGL_STREAM_STATE_CONNECTING_KHR)
     {
         return Error(EGL_BAD_STATE_KHR, "Stream not in connecting state");
     }
 
-    if (stream->getConsumerType() != Stream::ConsumerType::GLTextureYUV ||
-        stream->getPlaneCount() != 2)
-    {
+    switch (stream->getConsumerType()) {
+    case Stream::ConsumerType::GLTextureYUV:
+        if (stream->getPlaneCount() != 2)
+        {
+            return Error(EGL_BAD_MATCH, "Incompatible stream consumer type");
+        }
+        break;
+
+    case Stream::ConsumerType::GLTextureRGB:
+        if (stream->getPlaneCount() != 1)
+        {
+            return Error(EGL_BAD_MATCH, "Incompatible stream consumer type");
+        }
+        break;
+
+    default:
         return Error(EGL_BAD_MATCH, "Incompatible stream consumer type");
     }
 
     return Error(EGL_SUCCESS);
 }
 
 Error ValidateStreamPostD3DTextureNV12ANGLE(const Display *display,
                                             const Stream *stream,
@@ -1547,16 +1560,22 @@ Error ValidateStreamPostD3DTextureNV12AN
         switch (attribute)
         {
             case EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE:
                 if (value < 0)
                 {
                     return Error(EGL_BAD_PARAMETER, "Invalid subresource index");
                 }
                 break;
+            case EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG:
+                if (value < 0)
+                {
+                    return Error(EGL_BAD_PARAMETER, "Invalid plane offset");
+                }
+                break;
             default:
                 return Error(EGL_BAD_ATTRIBUTE, "Invalid attribute");
         }
     }
 
     if (stream->getState() != EGL_STREAM_STATE_EMPTY_KHR &&
         stream->getState() != EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR &&
         stream->getState() != EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR)
@@ -1569,17 +1588,17 @@ Error ValidateStreamPostD3DTextureNV12AN
         return Error(EGL_BAD_MATCH, "Incompatible stream producer");
     }
 
     if (texture == nullptr)
     {
         return egl::Error(EGL_BAD_PARAMETER, "Texture is null");
     }
 
-    return stream->validateD3D11NV12Texture(texture);
+    return stream->validateD3D11NV12Texture(texture, attribs);
 }
 
 Error ValidateSwapBuffersWithDamageEXT(const Display *display,
                                        const Surface *surface,
                                        EGLint *rects,
                                        EGLint n_rects)
 {
     Error error = ValidateSurface(display, surface);