// // Copyright 2002 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. // // Surface.cpp: Implements the egl::Surface class, representing a drawing surface // such as the client area of a window, including any back buffers. // Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3. #include "libANGLE/Surface.h" #include #include "libANGLE/Config.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/Texture.h" #include "libANGLE/formatutils.h" #include "libANGLE/renderer/EGLImplFactory.h" #include "libANGLE/trace.h" namespace egl { namespace { angle::SubjectIndex kSurfaceImplSubjectIndex = 0; } // namespace SurfaceState::SurfaceState(const egl::Config *configIn, const AttributeMap &attributesIn) : label(nullptr), config((configIn != nullptr) ? new egl::Config(*configIn) : nullptr), attributes(attributesIn), timestampsEnabled(false), autoRefreshEnabled(false), directComposition(false), swapBehavior(EGL_NONE) { directComposition = attributes.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE) == EGL_TRUE; } SurfaceState::~SurfaceState() { delete config; } bool SurfaceState::isRobustResourceInitEnabled() const { return attributes.get(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE; } bool SurfaceState::hasProtectedContent() const { return attributes.get(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) == EGL_TRUE; } EGLint SurfaceState::getPreferredSwapInterval() const { return attributes.getAsInt(EGL_SWAP_INTERVAL_ANGLE, 1); } Surface::Surface(EGLint surfaceType, GLuint serialId, const egl::Config *config, const AttributeMap &attributes, bool forceRobustResourceInit, EGLenum buftype) : FramebufferAttachmentObject(), mState(config, attributes), mImplementation(nullptr), mRefCount(0), mDestroyed(false), mType(surfaceType), mBuftype(buftype), mPostSubBufferRequested(false), mLargestPbuffer(false), mGLColorspace(EGL_GL_COLORSPACE_LINEAR), mVGAlphaFormat(EGL_VG_ALPHA_FORMAT_NONPRE), mVGColorspace(EGL_VG_COLORSPACE_sRGB), mMipmapTexture(false), mMipmapLevel(0), mHorizontalResolution(EGL_UNKNOWN), mVerticalResolution(EGL_UNKNOWN), mMultisampleResolve(EGL_MULTISAMPLE_RESOLVE_DEFAULT), mFixedSize(false), mFixedWidth(0), mFixedHeight(0), mTextureFormat(TextureFormat::NoTexture), mTextureTarget(EGL_NO_TEXTURE), // FIXME: Determine actual pixel aspect ratio mPixelAspectRatio(static_cast(1.0 * EGL_DISPLAY_SCALING)), mRenderBuffer(EGL_BACK_BUFFER), mOrientation(0), mTexture(nullptr), mColorFormat(config->renderTargetFormat), mDSFormat(config->depthStencilFormat), mIsCurrentOnAnyContext(false), mLockBufferPtr(nullptr), mLockBufferPitch(0), mBufferAgeQueriedSinceLastSwap(false), mIsDamageRegionSet(false), mColorInitState(gl::InitState::Initialized), mDepthStencilInitState(gl::InitState::Initialized), mImplObserverBinding(this, kSurfaceImplSubjectIndex), mSerialId(serialId) { mPostSubBufferRequested = (attributes.get(EGL_POST_SUB_BUFFER_SUPPORTED_NV, EGL_FALSE) == EGL_TRUE); if (mType == EGL_PBUFFER_BIT) { mLargestPbuffer = (attributes.get(EGL_LARGEST_PBUFFER, EGL_FALSE) == EGL_TRUE); } if (mType == EGL_PIXMAP_BIT) { mRenderBuffer = EGL_SINGLE_BUFFER; } if (mType == EGL_WINDOW_BIT) { mRenderBuffer = mState.attributes.getAsInt(EGL_RENDER_BUFFER, EGL_BACK_BUFFER); } mGLColorspace = static_cast(attributes.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR)); mVGAlphaFormat = static_cast(attributes.get(EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_NONPRE)); mVGColorspace = static_cast(attributes.get(EGL_VG_COLORSPACE, EGL_VG_COLORSPACE_sRGB)); mMipmapTexture = (attributes.get(EGL_MIPMAP_TEXTURE, EGL_FALSE) == EGL_TRUE); mRobustResourceInitialization = forceRobustResourceInit || (attributes.get(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE); if (mRobustResourceInitialization) { mColorInitState = gl::InitState::MayNeedInit; mDepthStencilInitState = gl::InitState::MayNeedInit; } mFixedSize = (attributes.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE); if (mFixedSize) { mFixedWidth = static_cast(attributes.get(EGL_WIDTH, 0)); mFixedHeight = static_cast(attributes.get(EGL_HEIGHT, 0)); } if (mType != EGL_WINDOW_BIT) { mTextureFormat = attributes.getAsPackedEnum(EGL_TEXTURE_FORMAT, TextureFormat::NoTexture); mTextureTarget = static_cast(attributes.get(EGL_TEXTURE_TARGET, EGL_NO_TEXTURE)); } mOrientation = static_cast(attributes.get(EGL_SURFACE_ORIENTATION_ANGLE, 0)); mTextureOffset.x = static_cast(mState.attributes.get(EGL_TEXTURE_OFFSET_X_ANGLE, 0)); mTextureOffset.y = static_cast(mState.attributes.get(EGL_TEXTURE_OFFSET_Y_ANGLE, 0)); } Surface::~Surface() {} rx::FramebufferAttachmentObjectImpl *Surface::getAttachmentImpl() const { return mImplementation; } Error Surface::destroyImpl(const Display *display) { if (mImplementation) { mImplementation->destroy(display); } ASSERT(!mTexture); SafeDelete(mImplementation); delete this; return NoError(); } void Surface::postSwap(const gl::Context *context) { if (mRobustResourceInitialization && mState.swapBehavior != EGL_BUFFER_PRESERVED) { mColorInitState = gl::InitState::MayNeedInit; mDepthStencilInitState = gl::InitState::MayNeedInit; onStateChange(angle::SubjectMessage::SubjectChanged); } mBufferAgeQueriedSinceLastSwap = false; mIsDamageRegionSet = false; } Error Surface::initialize(const Display *display) { GLenum overrideRenderTargetFormat = mState.config->renderTargetFormat; // To account for color space differences, override the renderTargetFormat with the // non-linear format. If no suitable non-linear format was found, return // EGL_BAD_MATCH error if (!gl::ColorspaceFormatOverride(mGLColorspace, &overrideRenderTargetFormat)) { return egl::EglBadMatch(); } // If an override is required update mState.config as well if (mState.config->renderTargetFormat != overrideRenderTargetFormat) { egl::Config *overrideConfig = new egl::Config(*(mState.config)); overrideConfig->renderTargetFormat = overrideRenderTargetFormat; delete mState.config; mState.config = overrideConfig; mColorFormat = gl::Format(mState.config->renderTargetFormat); mDSFormat = gl::Format(mState.config->depthStencilFormat); } ANGLE_TRY(mImplementation->initialize(display)); // Initialized here since impl is nullptr in the constructor. // Must happen after implementation initialize for Android. mState.swapBehavior = mImplementation->getSwapBehavior(); if (mBuftype == EGL_IOSURFACE_ANGLE) { GLenum internalFormat = static_cast(mState.attributes.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE)); GLenum type = static_cast(mState.attributes.get(EGL_TEXTURE_TYPE_ANGLE)); // GL_RGBA + GL_HALF_FLOAT is not a valid format/type combination in GLES like it is in // desktop GL. Adjust the frontend format to be sized RGBA16F. if (internalFormat == GL_RGBA && type == GL_HALF_FLOAT) { internalFormat = GL_RGBA16F; } mColorFormat = gl::Format(internalFormat, type); } if (mBuftype == EGL_D3D_TEXTURE_ANGLE) { const angle::Format *colorFormat = mImplementation->getD3DTextureColorFormat(); ASSERT(colorFormat != nullptr); GLenum internalFormat = colorFormat->fboImplementationInternalFormat; mColorFormat = gl::Format(internalFormat, colorFormat->componentType); mGLColorspace = EGL_GL_COLORSPACE_LINEAR; if (mColorFormat.info->colorEncoding == GL_SRGB) { mGLColorspace = EGL_GL_COLORSPACE_SRGB; } } if (mType == EGL_WINDOW_BIT && display->getExtensions().getFrameTimestamps) { mState.supportedCompositorTimings = mImplementation->getSupportedCompositorTimings(); mState.supportedTimestamps = mImplementation->getSupportedTimestamps(); } mImplObserverBinding.bind(mImplementation); return NoError(); } Error Surface::makeCurrent(const gl::Context *context) { if (isLocked()) { return EglBadAccess(); } ANGLE_TRY(mImplementation->makeCurrent(context)); mIsCurrentOnAnyContext = true; addRef(); return NoError(); } Error Surface::unMakeCurrent(const gl::Context *context) { ANGLE_TRY(mImplementation->unMakeCurrent(context)); mIsCurrentOnAnyContext = false; return releaseRef(context->getDisplay()); } Error Surface::releaseRef(const Display *display) { ASSERT(mRefCount > 0); mRefCount--; if (mRefCount == 0 && mDestroyed) { ASSERT(display); return destroyImpl(display); } return NoError(); } Error Surface::onDestroy(const Display *display) { mDestroyed = true; if (mRefCount == 0) { return destroyImpl(display); } return NoError(); } void Surface::setLabel(EGLLabelKHR label) { mState.label = label; } EGLLabelKHR Surface::getLabel() const { return mState.label; } EGLint Surface::getType() const { return mType; } Error Surface::prepareSwap(const gl::Context *context) { ANGLE_TRACE_EVENT0("gpu.angle", "egl::Surface::prepareSwap"); return mImplementation->prepareSwap(context); } Error Surface::swap(const gl::Context *context) { ANGLE_TRACE_EVENT0("gpu.angle", "egl::Surface::swap"); context->onPreSwap(); context->getState().getOverlay()->onSwap(); ANGLE_TRY(mImplementation->swap(context)); postSwap(context); return NoError(); } Error Surface::swapWithDamage(const gl::Context *context, const EGLint *rects, EGLint n_rects) { ANGLE_TRACE_EVENT0("gpu.angle", "egl::Surface::swapWithDamage"); context->onPreSwap(); context->getState().getOverlay()->onSwap(); ANGLE_TRY(mImplementation->swapWithDamage(context, rects, n_rects)); postSwap(context); return NoError(); } Error Surface::swapWithFrameToken(const gl::Context *context, EGLFrameTokenANGLE frameToken) { ANGLE_TRACE_EVENT0("gpu.angle", "egl::Surface::swapWithFrameToken"); context->onPreSwap(); context->getState().getOverlay()->onSwap(); ANGLE_TRY(mImplementation->swapWithFrameToken(context, frameToken)); postSwap(context); return NoError(); } Error Surface::postSubBuffer(const gl::Context *context, EGLint x, EGLint y, EGLint width, EGLint height) { if (width == 0 || height == 0) { return egl::NoError(); } context->getState().getOverlay()->onSwap(); ANGLE_TRY(mImplementation->postSubBuffer(context, x, y, width, height)); postSwap(context); return NoError(); } Error Surface::setPresentationTime(EGLnsecsANDROID time) { return mImplementation->setPresentationTime(time); } Error Surface::querySurfacePointerANGLE(EGLint attribute, void **value) { return mImplementation->querySurfacePointerANGLE(attribute, value); } EGLint Surface::isPostSubBufferSupported() const { return mPostSubBufferRequested && mImplementation->isPostSubBufferSupported(); } void Surface::setSwapInterval(EGLint interval) { mImplementation->setSwapInterval(interval); } void Surface::setMipmapLevel(EGLint level) { // Level is set but ignored UNIMPLEMENTED(); mMipmapLevel = level; } void Surface::setMultisampleResolve(EGLenum resolve) { // Behaviour is set but ignored UNIMPLEMENTED(); mMultisampleResolve = resolve; } void Surface::setSwapBehavior(EGLenum behavior) { // Behaviour is set but ignored UNIMPLEMENTED(); mState.swapBehavior = behavior; } void Surface::setFixedWidth(EGLint width) { mFixedWidth = width; mImplementation->setFixedWidth(width); } void Surface::setFixedHeight(EGLint height) { mFixedHeight = height; mImplementation->setFixedHeight(height); } const Config *Surface::getConfig() const { return mState.config; } EGLint Surface::getPixelAspectRatio() const { return mPixelAspectRatio; } EGLenum Surface::getRenderBuffer() const { return mRenderBuffer; } EGLenum Surface::getSwapBehavior() const { return mState.swapBehavior; } TextureFormat Surface::getTextureFormat() const { return mTextureFormat; } EGLenum Surface::getTextureTarget() const { return mTextureTarget; } bool Surface::getLargestPbuffer() const { return mLargestPbuffer; } EGLenum Surface::getGLColorspace() const { return mGLColorspace; } EGLenum Surface::getVGAlphaFormat() const { return mVGAlphaFormat; } EGLenum Surface::getVGColorspace() const { return mVGColorspace; } bool Surface::getMipmapTexture() const { return mMipmapTexture; } EGLint Surface::getMipmapLevel() const { return mMipmapLevel; } EGLint Surface::getHorizontalResolution() const { return mHorizontalResolution; } EGLint Surface::getVerticalResolution() const { return mVerticalResolution; } EGLenum Surface::getMultisampleResolve() const { return mMultisampleResolve; } EGLint Surface::isFixedSize() const { return mFixedSize; } EGLint Surface::getWidth() const { return mFixedSize ? static_cast(mFixedWidth) : mImplementation->getWidth(); } EGLint Surface::getHeight() const { return mFixedSize ? static_cast(mFixedHeight) : mImplementation->getHeight(); } egl::Error Surface::getUserWidth(const egl::Display *display, EGLint *value) const { if (mFixedSize) { *value = static_cast(mFixedWidth); return NoError(); } else { return mImplementation->getUserWidth(display, value); } } egl::Error Surface::getUserHeight(const egl::Display *display, EGLint *value) const { if (mFixedSize) { *value = static_cast(mFixedHeight); return NoError(); } else { return mImplementation->getUserHeight(display, value); } } Error Surface::bindTexImage(gl::Context *context, gl::Texture *texture, EGLint buffer) { ASSERT(!mTexture); ANGLE_TRY(mImplementation->bindTexImage(context, texture, buffer)); if (texture->bindTexImageFromSurface(context, this) == angle::Result::Stop) { return Error(EGL_BAD_SURFACE); } mTexture = texture; addRef(); return NoError(); } Error Surface::releaseTexImage(const gl::Context *context, EGLint buffer) { ASSERT(context); ANGLE_TRY(mImplementation->releaseTexImage(context, buffer)); ASSERT(mTexture); ANGLE_TRY(ResultToEGL(mTexture->releaseTexImageFromSurface(context))); return releaseTexImageFromTexture(context); } Error Surface::getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) { return mImplementation->getSyncValues(ust, msc, sbc); } Error Surface::getMscRate(EGLint *numerator, EGLint *denominator) { return mImplementation->getMscRate(numerator, denominator); } Error Surface::releaseTexImageFromTexture(const gl::Context *context) { ASSERT(mTexture); mTexture = nullptr; return releaseRef(context->getDisplay()); } gl::Extents Surface::getAttachmentSize(const gl::ImageIndex & /*target*/) const { return gl::Extents(getWidth(), getHeight(), 1); } gl::Format Surface::getAttachmentFormat(GLenum binding, const gl::ImageIndex &target) const { return (binding == GL_BACK ? mColorFormat : mDSFormat); } GLsizei Surface::getAttachmentSamples(const gl::ImageIndex &target) const { return getConfig()->samples; } bool Surface::isRenderable(const gl::Context *context, GLenum binding, const gl::ImageIndex &imageIndex) const { return true; } bool Surface::isYUV() const { // EGL_EXT_yuv_surface is not implemented. return false; } bool Surface::isCreatedWithAHB() const { return false; } GLuint Surface::getId() const { return mSerialId; } Error Surface::getBufferAgeImpl(const gl::Context *context, EGLint *age) const { // When EGL_BUFFER_PRESERVED, the previous frame contents are copied to // current frame, so the buffer age is always 1. if (mState.swapBehavior == EGL_BUFFER_PRESERVED) { if (age != nullptr) { *age = 1; } return egl::NoError(); } return mImplementation->getBufferAge(context, age); } Error Surface::getBufferAge(const gl::Context *context, EGLint *age) { Error err = getBufferAgeImpl(context, age); if (!err.isError()) { mBufferAgeQueriedSinceLastSwap = true; } return err; } gl::InitState Surface::initState(GLenum binding, const gl::ImageIndex & /*imageIndex*/) const { switch (binding) { case GL_BACK: return mColorInitState; case GL_DEPTH: case GL_STENCIL: return mDepthStencilInitState; default: UNREACHABLE(); return gl::InitState::Initialized; } } void Surface::setInitState(GLenum binding, const gl::ImageIndex & /*imageIndex*/, gl::InitState initState) { switch (binding) { case GL_BACK: mColorInitState = initState; break; case GL_DEPTH: case GL_STENCIL: mDepthStencilInitState = initState; break; default: UNREACHABLE(); break; } } void Surface::setTimestampsEnabled(bool enabled) { mImplementation->setTimestampsEnabled(enabled); mState.timestampsEnabled = enabled; } bool Surface::isTimestampsEnabled() const { return mState.timestampsEnabled; } Error Surface::setAutoRefreshEnabled(bool enabled) { ANGLE_TRY(mImplementation->setAutoRefreshEnabled(enabled)); mState.autoRefreshEnabled = enabled; return NoError(); } bool Surface::hasProtectedContent() const { return mState.hasProtectedContent(); } const SupportedCompositorTiming &Surface::getSupportedCompositorTimings() const { return mState.supportedCompositorTimings; } Error Surface::getCompositorTiming(EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) const { return mImplementation->getCompositorTiming(numTimestamps, names, values); } Error Surface::getNextFrameId(EGLuint64KHR *frameId) const { return mImplementation->getNextFrameId(frameId); } const SupportedTimestamps &Surface::getSupportedTimestamps() const { return mState.supportedTimestamps; } Error Surface::getFrameTimestamps(EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values) const { return mImplementation->getFrameTimestamps(frameId, numTimestamps, timestamps, values); } void Surface::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) { ASSERT(index == kSurfaceImplSubjectIndex); switch (message) { case angle::SubjectMessage::SubjectChanged: onStateChange(angle::SubjectMessage::ContentsChanged); break; case angle::SubjectMessage::SurfaceChanged: onStateChange(angle::SubjectMessage::SurfaceChanged); break; case angle::SubjectMessage::SwapchainImageChanged: onStateChange(angle::SubjectMessage::SwapchainImageChanged); break; default: UNREACHABLE(); break; } } Error Surface::setRenderBuffer(EGLint renderBuffer) { ANGLE_TRY(mImplementation->setRenderBuffer(renderBuffer)); mRenderBuffer = renderBuffer; return NoError(); } bool Surface::isLocked() const { return (mLockBufferPtr != nullptr); } EGLint Surface::getBitmapPitch() const { return mLockBufferPitch; } EGLint Surface::getBitmapOrigin() const { return mImplementation->origin(); } EGLint Surface::getRedOffset() const { const gl::InternalFormat &format = *mColorFormat.info; if (gl::IsBGRAFormat(format.internalFormat)) { return format.blueBits + format.greenBits; } else { return 0; } } EGLint Surface::getGreenOffset() const { const gl::InternalFormat &format = *mColorFormat.info; if (gl::IsBGRAFormat(format.internalFormat)) { return format.blueBits; } else { return format.redBits; } } EGLint Surface::getBlueOffset() const { const gl::InternalFormat &format = *mColorFormat.info; if (gl::IsBGRAFormat(format.internalFormat)) { return 0; } else { return format.redBits + format.greenBits; } } EGLint Surface::getAlphaOffset() const { const gl::InternalFormat &format = *mColorFormat.info; if (format.isLUMA()) { return format.luminanceBits; // Luma always first, alpha optional } // For RGBA/BGRA alpha is last return format.blueBits + format.greenBits + format.redBits; } EGLint Surface::getLuminanceOffset() const { return 0; } EGLint Surface::getBitmapPixelSize() const { constexpr EGLint kBitsPerByte = 8; const gl::InternalFormat &format = *mColorFormat.info; return (format.pixelBytes * kBitsPerByte); } EGLAttribKHR Surface::getBitmapPointer() const { return static_cast((intptr_t)mLockBufferPtr); } egl::Error Surface::lockSurfaceKHR(const egl::Display *display, const AttributeMap &attributes) { EGLint lockBufferUsageHint = attributes.getAsInt( EGL_LOCK_USAGE_HINT_KHR, (EGL_READ_SURFACE_BIT_KHR | EGL_WRITE_SURFACE_BIT_KHR)); bool preservePixels = ((attributes.getAsInt(EGL_MAP_PRESERVE_PIXELS_KHR, false) == EGL_TRUE) || (mState.swapBehavior == EGL_BUFFER_PRESERVED)); return mImplementation->lockSurface(display, lockBufferUsageHint, preservePixels, &mLockBufferPtr, &mLockBufferPitch); } egl::Error Surface::unlockSurfaceKHR(const egl::Display *display) { mLockBufferPtr = nullptr; mLockBufferPitch = 0; return mImplementation->unlockSurface(display, true); } WindowSurface::WindowSurface(rx::EGLImplFactory *implFactory, const egl::Config *config, EGLNativeWindowType window, const AttributeMap &attribs, bool robustResourceInit) : Surface(EGL_WINDOW_BIT, implFactory->getNextSurfaceID(), config, attribs, robustResourceInit) { mImplementation = implFactory->createWindowSurface(mState, window, attribs); } void Surface::setDamageRegion(const EGLint *rects, EGLint n_rects) { mIsDamageRegionSet = true; } WindowSurface::~WindowSurface() {} PbufferSurface::PbufferSurface(rx::EGLImplFactory *implFactory, const Config *config, const AttributeMap &attribs, bool robustResourceInit) : Surface(EGL_PBUFFER_BIT, implFactory->getNextSurfaceID(), config, attribs, robustResourceInit) { mImplementation = implFactory->createPbufferSurface(mState, attribs); } PbufferSurface::PbufferSurface(rx::EGLImplFactory *implFactory, const Config *config, EGLenum buftype, EGLClientBuffer clientBuffer, const AttributeMap &attribs, bool robustResourceInit) : Surface(EGL_PBUFFER_BIT, implFactory->getNextSurfaceID(), config, attribs, robustResourceInit, buftype) { mImplementation = implFactory->createPbufferFromClientBuffer(mState, buftype, clientBuffer, attribs); } PbufferSurface::~PbufferSurface() {} PixmapSurface::PixmapSurface(rx::EGLImplFactory *implFactory, const Config *config, NativePixmapType nativePixmap, const AttributeMap &attribs, bool robustResourceInit) : Surface(EGL_PIXMAP_BIT, implFactory->getNextSurfaceID(), config, attribs, robustResourceInit) { mImplementation = implFactory->createPixmapSurface(mState, nativePixmap, attribs); } PixmapSurface::~PixmapSurface() {} // SurfaceDeleter implementation. SurfaceDeleter::SurfaceDeleter(const Display *display) : mDisplay(display) {} SurfaceDeleter::~SurfaceDeleter() {} void SurfaceDeleter::operator()(Surface *surface) { ANGLE_SWALLOW_ERR(surface->onDestroy(mDisplay)); } } // namespace egl