| /* |
| * Copyright (C) 2022 Metrological Group B.V. |
| * Copyright (C) 2022 Igalia S.L. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "GraphicsContextGLGBM.h" |
| |
| #if ENABLE(WEBGL) && USE(LIBGBM) && USE(ANGLE) |
| |
| #include "ANGLEHeaders.h" |
| #include "DMABufEGLUtilities.h" |
| #include "Logging.h" |
| #include "PixelBuffer.h" |
| |
| #if ENABLE(MEDIA_STREAM) |
| #include "VideoFrame.h" |
| #endif |
| |
| namespace WebCore { |
| |
| GraphicsContextGLANGLE::GraphicsContextGLANGLE(GraphicsContextGLAttributes attributes) |
| : GraphicsContextGL(attributes) |
| { |
| } |
| |
| GraphicsContextGLANGLE::~GraphicsContextGLANGLE() |
| { |
| bool success = makeContextCurrent(); |
| ASSERT_UNUSED(success, success); |
| if (m_texture) |
| GL_DeleteTextures(1, &m_texture); |
| |
| auto attributes = contextAttributes(); |
| |
| if (attributes.antialias) { |
| GL_DeleteRenderbuffers(1, &m_multisampleColorBuffer); |
| if (attributes.stencil || attributes.depth) |
| GL_DeleteRenderbuffers(1, &m_multisampleDepthStencilBuffer); |
| GL_DeleteFramebuffers(1, &m_multisampleFBO); |
| } else if (attributes.stencil || attributes.depth) { |
| if (m_depthStencilBuffer) |
| GL_DeleteRenderbuffers(1, &m_depthStencilBuffer); |
| } |
| GL_DeleteFramebuffers(1, &m_fbo); |
| } |
| |
| GCGLDisplay GraphicsContextGLANGLE::platformDisplay() const |
| { |
| return m_displayObj; |
| } |
| |
| GCGLConfig GraphicsContextGLANGLE::platformConfig() const |
| { |
| return m_configObj; |
| } |
| |
| bool GraphicsContextGLANGLE::makeContextCurrent() |
| { |
| static thread_local TLS_MODEL_INITIAL_EXEC GraphicsContextGLANGLE* s_currentContext { nullptr }; |
| |
| auto madeCurrent = |
| [&] { |
| if (s_currentContext == this) |
| return true; |
| bool current = !!EGL_MakeCurrent(m_displayObj, EGL_NO_SURFACE, EGL_NO_SURFACE, m_contextObj); |
| if (current) |
| s_currentContext = this; |
| return current; |
| }(); |
| |
| auto& contextSwapchain = static_cast<GraphicsContextGLGBM&>(*this).swapchain(); |
| if (madeCurrent && contextSwapchain.swapchain && !contextSwapchain.drawBO) { |
| auto size = getInternalFramebufferSize(); |
| contextSwapchain.drawBO = contextSwapchain.swapchain->getBuffer( |
| GBMBufferSwapchain::BufferDescription { |
| .format = DMABufFormat::create(uint32_t(contextAttributes().alpha ? DMABufFormat::FourCC::ARGB8888 : DMABufFormat::FourCC::XRGB8888)), |
| .width = std::clamp<uint32_t>(size.width(), 0, UINT_MAX), |
| .height = std::clamp<uint32_t>(size.height(), 0, UINT_MAX), |
| .flags = GBMBufferSwapchain::BufferDescription::NoFlags, |
| }); |
| |
| GLenum textureTarget = drawingBufferTextureTarget(); |
| ScopedRestoreTextureBinding restoreBinding(drawingBufferTextureTargetQueryForDrawingTarget(textureTarget), textureTarget, textureTarget != TEXTURE_RECTANGLE_ARB); |
| |
| auto result = contextSwapchain.images.ensure(contextSwapchain.drawBO->handle(), |
| [&] { |
| auto dmabufObject = contextSwapchain.drawBO->createDMABufObject(0); |
| auto attributes = DMABufEGLUtilities::constructEGLCreateImageAttributes(dmabufObject, 0, |
| DMABufEGLUtilities::PlaneModifiersUsage { static_cast<GraphicsContextGLGBM&>(*this).eglExtensions().EXT_image_dma_buf_import_modifiers }); |
| return EGL_CreateImageKHR(contextSwapchain.platformDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, (EGLClientBuffer)nullptr, attributes.data()); |
| }); |
| |
| GL_BindTexture(textureTarget, m_texture); |
| GL_EGLImageTargetTexture2DOES(textureTarget, result.iterator->value); |
| |
| // If just created, the dmabuf has to be cleared to provide a zeroed-out buffer. |
| // Current color-clear and framebuffer state has to be preserved and re-established after this. |
| if (result.isNewEntry) { |
| GCGLuint boundFBO { 0 }; |
| GL_GetIntegerv(GL_FRAMEBUFFER_BINDING, reinterpret_cast<GCGLint*>(&boundFBO)); |
| |
| GCGLuint targetFBO = contextAttributes().antialias ? m_multisampleFBO : m_fbo; |
| if (targetFBO != boundFBO) |
| GL_BindFramebuffer(GL_FRAMEBUFFER, targetFBO); |
| |
| std::array<float, 4> clearColor { 0, 0, 0, 0 }; |
| GL_GetFloatv(GL_COLOR_CLEAR_VALUE, clearColor.data()); |
| |
| GL_ClearColor(0, 0, 0, 0); |
| GL_Clear(GL_COLOR_BUFFER_BIT); |
| |
| GL_ClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); |
| |
| if (targetFBO != boundFBO) |
| GL_BindFramebuffer(GL_FRAMEBUFFER, boundFBO); |
| } |
| } |
| |
| return madeCurrent; |
| } |
| |
| void GraphicsContextGLANGLE::checkGPUStatus() |
| { |
| } |
| |
| void GraphicsContextGLANGLE::platformReleaseThreadResources() |
| { |
| } |
| |
| RefPtr<PixelBuffer> GraphicsContextGLANGLE::readCompositedResults() |
| { |
| return readRenderingResults(); |
| } |
| |
| RefPtr<GraphicsContextGLGBM> GraphicsContextGLGBM::create(GraphicsContextGLAttributes&& attributes) |
| { |
| auto context = adoptRef(*new GraphicsContextGLGBM(WTFMove(attributes))); |
| if (!context->initialize()) |
| return nullptr; |
| return context; |
| } |
| |
| GraphicsContextGLGBM::GraphicsContextGLGBM(GraphicsContextGLAttributes&& attributes) |
| : GraphicsContextGLANGLE(WTFMove(attributes)) |
| { } |
| |
| GraphicsContextGLGBM::~GraphicsContextGLGBM() = default; |
| |
| RefPtr<GraphicsLayerContentsDisplayDelegate> GraphicsContextGLGBM::layerContentsDisplayDelegate() |
| { |
| return { }; |
| } |
| |
| #if ENABLE(MEDIA_STREAM) |
| RefPtr<VideoFrame> GraphicsContextGLGBM::paintCompositedResultsToVideoFrame() |
| { |
| return { }; |
| } |
| #endif |
| |
| #if ENABLE(VIDEO) |
| bool GraphicsContextGLGBM::copyTextureFromMedia(MediaPlayer&, PlatformGLObject, GCGLenum, GCGLint, GCGLenum, GCGLenum, GCGLenum, bool, bool) |
| { |
| return false; |
| } |
| #endif |
| |
| void GraphicsContextGLGBM::setContextVisibility(bool) |
| { |
| } |
| |
| void GraphicsContextGLGBM::prepareForDisplay() |
| { |
| if (m_layerComposited || !makeContextCurrent()) |
| return; |
| |
| prepareTexture(); |
| markLayerComposited(); |
| |
| m_swapchain.displayBO = WTFMove(m_swapchain.drawBO); |
| } |
| |
| bool GraphicsContextGLGBM::platformInitializeContext() |
| { |
| #if ENABLE(WEBGL2) |
| m_isForWebGL2 = contextAttributes().webGLVersion == GraphicsContextGLWebGLVersion::WebGL2; |
| #endif |
| |
| Vector<EGLint> displayAttributes { |
| EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE, |
| EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE, |
| EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE, EGL_PLATFORM_SURFACELESS_MESA, |
| EGL_NONE, |
| }; |
| |
| m_displayObj = EGL_GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttributes.data()); |
| if (m_displayObj == EGL_NO_DISPLAY) |
| return false; |
| |
| EGLint majorVersion, minorVersion; |
| if (EGL_Initialize(m_displayObj, &majorVersion, &minorVersion) == EGL_FALSE) { |
| LOG(WebGL, "EGLDisplay Initialization failed."); |
| return false; |
| } |
| LOG(WebGL, "ANGLE initialised Major: %d Minor: %d", majorVersion, minorVersion); |
| |
| const char* displayExtensions = EGL_QueryString(m_displayObj, EGL_EXTENSIONS); |
| LOG(WebGL, "Extensions: %s", displayExtensions); |
| |
| EGLint configAttributes[] = { |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| EGL_RED_SIZE, 8, |
| EGL_GREEN_SIZE, 8, |
| EGL_BLUE_SIZE, 8, |
| EGL_ALPHA_SIZE, 8, |
| EGL_DEPTH_SIZE, 0, |
| EGL_STENCIL_SIZE, 0, |
| EGL_NONE |
| }; |
| EGLint numberConfigsReturned = 0; |
| EGL_ChooseConfig(m_displayObj, configAttributes, &m_configObj, 1, &numberConfigsReturned); |
| if (numberConfigsReturned != 1) { |
| LOG(WebGL, "EGLConfig Initialization failed."); |
| return false; |
| } |
| LOG(WebGL, "Got EGLConfig"); |
| |
| EGL_BindAPI(EGL_OPENGL_ES_API); |
| if (EGL_GetError() != EGL_SUCCESS) { |
| LOG(WebGL, "Unable to bind to OPENGL_ES_API"); |
| return false; |
| } |
| |
| Vector<EGLint> eglContextAttributes; |
| if (m_isForWebGL2) { |
| eglContextAttributes.append(EGL_CONTEXT_CLIENT_VERSION); |
| eglContextAttributes.append(3); |
| } else { |
| eglContextAttributes.append(EGL_CONTEXT_CLIENT_VERSION); |
| eglContextAttributes.append(2); |
| // ANGLE will upgrade the context to ES3 automatically unless this is specified. |
| eglContextAttributes.append(EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE); |
| eglContextAttributes.append(EGL_FALSE); |
| } |
| eglContextAttributes.append(EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE); |
| eglContextAttributes.append(EGL_TRUE); |
| // WebGL requires that all resources are cleared at creation. |
| eglContextAttributes.append(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE); |
| eglContextAttributes.append(EGL_TRUE); |
| // WebGL doesn't allow client arrays. |
| eglContextAttributes.append(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE); |
| eglContextAttributes.append(EGL_FALSE); |
| // WebGL doesn't allow implicit creation of objects on bind. |
| eglContextAttributes.append(EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM); |
| eglContextAttributes.append(EGL_FALSE); |
| |
| if (strstr(displayExtensions, "EGL_ANGLE_power_preference")) { |
| eglContextAttributes.append(EGL_POWER_PREFERENCE_ANGLE); |
| // EGL_LOW_POWER_ANGLE is the default. Change to |
| // EGL_HIGH_POWER_ANGLE if desired. |
| eglContextAttributes.append(EGL_LOW_POWER_ANGLE); |
| } |
| eglContextAttributes.append(EGL_NONE); |
| |
| m_contextObj = EGL_CreateContext(m_displayObj, m_configObj, EGL_NO_CONTEXT, eglContextAttributes.data()); |
| if (m_contextObj == EGL_NO_CONTEXT) { |
| LOG(WebGL, "EGLContext Initialization failed."); |
| return false; |
| } |
| if (!makeContextCurrent()) { |
| LOG(WebGL, "ANGLE makeContextCurrent failed."); |
| return false; |
| } |
| |
| if (strstr(displayExtensions, "EGL_EXT_image_dma_buf_import_modifiers")) |
| m_eglExtensions.EXT_image_dma_buf_import_modifiers = true; |
| |
| LOG(WebGL, "Got EGLContext"); |
| return true; |
| } |
| |
| bool GraphicsContextGLGBM::platformInitialize() |
| { |
| bool success = makeContextCurrent(); |
| ASSERT_UNUSED(success, success); |
| |
| // We require this extension to render into the dmabuf-backed EGLImage. |
| RELEASE_ASSERT(supportsExtension("GL_OES_EGL_image"_s)); |
| GL_RequestExtensionANGLE("GL_OES_EGL_image"); |
| |
| validateAttributes(); |
| auto attributes = contextAttributes(); // They may have changed during validation. |
| |
| GLenum textureTarget = drawingBufferTextureTarget(); |
| // Create a texture to render into. |
| GL_GenTextures(1, &m_texture); |
| GL_BindTexture(textureTarget, m_texture); |
| GL_TexParameterf(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| GL_TexParameterf(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| GL_TexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| GL_TexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| GL_BindTexture(textureTarget, 0); |
| |
| // Create an FBO. |
| GL_GenFramebuffers(1, &m_fbo); |
| GL_BindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| |
| // Create a multisample FBO. |
| ASSERT(m_state.boundReadFBO == m_state.boundDrawFBO); |
| if (attributes.antialias) { |
| GL_GenFramebuffers(1, &m_multisampleFBO); |
| GL_BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); |
| m_state.boundDrawFBO = m_state.boundReadFBO = m_multisampleFBO; |
| GL_GenRenderbuffers(1, &m_multisampleColorBuffer); |
| if (attributes.stencil || attributes.depth) |
| GL_GenRenderbuffers(1, &m_multisampleDepthStencilBuffer); |
| } else { |
| // Bind canvas FBO. |
| GL_BindFramebuffer(GL_FRAMEBUFFER, m_fbo); |
| m_state.boundDrawFBO = m_state.boundReadFBO = m_fbo; |
| if (attributes.stencil || attributes.depth) |
| GL_GenRenderbuffers(1, &m_depthStencilBuffer); |
| } |
| |
| GL_ClearColor(0, 0, 0, 0); |
| return GraphicsContextGLANGLE::platformInitialize(); |
| } |
| |
| void GraphicsContextGLGBM::prepareTexture() |
| { |
| ASSERT(!m_layerComposited); |
| |
| if (contextAttributes().antialias) |
| resolveMultisamplingIfNecessary(); |
| |
| GL_Flush(); |
| } |
| |
| bool GraphicsContextGLGBM::reshapeDisplayBufferBacking() |
| { |
| auto attrs = contextAttributes(); |
| const auto size = getInternalFramebufferSize(); |
| const int width = size.width(); |
| const int height = size.height(); |
| GLuint colorFormat = attrs.alpha ? GL_RGBA : GL_RGB; |
| GLenum textureTarget = drawingBufferTextureTarget(); |
| GLuint internalColorFormat = textureTarget == GL_TEXTURE_2D ? colorFormat : m_internalColorFormat; |
| ScopedRestoreTextureBinding restoreBinding(drawingBufferTextureTargetQueryForDrawingTarget(textureTarget), textureTarget, textureTarget != TEXTURE_RECTANGLE_ARB); |
| |
| m_swapchain = Swapchain(platformDisplay()); |
| |
| GL_BindTexture(textureTarget, m_texture); |
| GL_TexImage2D(textureTarget, 0, internalColorFormat, width, height, 0, colorFormat, GL_UNSIGNED_BYTE, 0); |
| GL_FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureTarget, m_texture, 0); |
| |
| return true; |
| } |
| |
| GraphicsContextGLGBM::Swapchain::Swapchain(GCGLDisplay platformDisplay) |
| : platformDisplay(platformDisplay) |
| , swapchain(adoptRef(new GBMBufferSwapchain(GBMBufferSwapchain::BufferSwapchainSize::Four))) |
| { } |
| |
| GraphicsContextGLGBM::Swapchain::~Swapchain() |
| { |
| for (EGLImageKHR image : images.values()) { |
| if (image != EGL_NO_IMAGE_KHR) |
| EGL_DestroyImageKHR(platformDisplay, image); |
| } |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(WEBGL) && USE(LIBGBM) && USE(ANGLE) |