blob: 050fedd274c6f9ffb78f426766fd79c553f43cee [file] [log] [blame]
// Copyright 2024 The Dawn & Tint Authors
//
// 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT HOLDER OR 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 "dawn/native/opengl/SwapChainEGL.h"
#include <utility>
#include "dawn/native/Surface.h"
#include "dawn/native/opengl/ContextEGL.h"
#include "dawn/native/opengl/DeviceGL.h"
#include "dawn/native/opengl/DisplayEGL.h"
#include "dawn/native/opengl/PhysicalDeviceGL.h"
#include "dawn/native/opengl/TextureGL.h"
#include "dawn/native/opengl/UtilsEGL.h"
namespace dawn::native::opengl {
// static
ResultOrError<Ref<SwapChainEGL>> SwapChainEGL::Create(Device* device,
Surface* surface,
SwapChainBase* previousSwapChain,
const SurfaceConfiguration* config) {
Ref<SwapChainEGL> swapchain = AcquireRef(new SwapChainEGL(device, surface, config));
DAWN_TRY(swapchain->Initialize(previousSwapChain));
return swapchain;
}
SwapChainEGL::SwapChainEGL(DeviceBase* dev, Surface* sur, const SurfaceConfiguration* config)
: SwapChainBase(dev, sur, config) {}
SwapChainEGL::~SwapChainEGL() = default;
void SwapChainEGL::DestroyImpl() {
SwapChainBase::DestroyImpl();
DetachFromSurface();
}
MaybeError SwapChainEGL::Initialize(SwapChainBase* previousSwapChain) {
const Device* device = ToBackend(GetDevice());
if (previousSwapChain != nullptr) {
// TODO(crbug.com/dawn/269): figure out what should happen when surfaces are used by
// multiple backends one after the other. It probably needs to block until the backend
// and GPU are completely finished with the previous swapchain.
DAWN_INVALID_IF(previousSwapChain->GetBackendType() != GetBackendType(),
"OpenGL SwapChain cannot switch backend types from %s to %s.",
previousSwapChain->GetBackendType(), GetBackendType());
// TODO(crbug.com/dawn/269): figure out what should happen when surfaces are used by
// a different EGL display. We probably need to block until the GPU is completely
// finished with the previous work, and then a bit more.
DAWN_INVALID_IF(
previousSwapChain->GetDevice()->GetPhysicalDevice() != device->GetPhysicalDevice(),
"OpenGL SwapChain cannot switch between contexts for %s and %s.",
previousSwapChain->GetDevice(), device);
// The EGLSurface created depends on the format; only reuse it if we have the same one.
if (previousSwapChain->GetFormat() == GetFormat()) {
SwapChainEGL* previousEGLSwapChain =
reinterpret_cast<SwapChainEGL*>(ToBackend(previousSwapChain));
std::swap(previousEGLSwapChain->mEGLSurface, mEGLSurface);
}
previousSwapChain->DetachFromSurface();
}
const DisplayEGL* display = ToBackend(device->GetPhysicalDevice())->GetDisplay();
// Create the EGLSurface if needed.
if (mEGLSurface == EGL_NO_SURFACE) {
DAWN_TRY(CreateEGLSurface(display));
}
EGLint swapInterval = GetPresentMode() == wgpu::PresentMode::Immediate ? 0 : 1;
display->egl.SwapInterval(display->GetDisplay(), swapInterval);
return {};
}
MaybeError SwapChainEGL::PresentImpl() {
Device* device = ToBackend(GetDevice());
EGLDisplay display = device->GetEGLDisplay();
const EGLFunctions& egl = device->GetEGL(false);
// Do the reverse-Y blit from the fake surface texture to the default framebuffer.
{
auto surfaceCurrent = device->GetContext()->MakeSurfaceCurrentScope(mEGLSurface);
EGLint surfaceWidth;
EGLint surfaceHeight;
DAWN_TRY(CheckEGL(egl, egl.QuerySurface(display, mEGLSurface, EGL_WIDTH, &surfaceWidth),
"getting surface width"));
DAWN_TRY(CheckEGL(egl, egl.QuerySurface(display, mEGLSurface, EGL_HEIGHT, &surfaceHeight),
"getting surface height"));
const OpenGLFunctions& gl = device->GetGL();
GLuint readFbo = 0;
gl.GenFramebuffers(1, &readFbo);
gl.BindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
mTextureView->BindToFramebuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0);
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.Scissor(0, 0, surfaceWidth, surfaceHeight);
gl.BlitFramebuffer(0, 0, mTexture->GetWidth(Aspect::Color),
mTexture->GetHeight(Aspect::Color), 0, surfaceHeight, surfaceWidth, 0,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl.DeleteFramebuffers(1, &readFbo);
}
egl.SwapBuffers(display, mEGLSurface);
mTexture->APIDestroy();
mTexture = nullptr;
return {};
}
ResultOrError<SwapChainTextureInfo> SwapChainEGL::GetCurrentTextureImpl() {
// Create the fake surface texture that we'll blit from.
TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
Ref<TextureBase> texture;
Ref<TextureViewBase> view;
DAWN_TRY_ASSIGN(texture, GetDevice()->CreateTexture(&desc));
DAWN_TRY_ASSIGN(view, GetDevice()->CreateTextureView(texture.Get()));
mTexture = std::move(ToBackend(texture));
mTextureView = std::move(ToBackend(view));
SwapChainTextureInfo info;
info.texture = mTexture;
info.status = wgpu::SurfaceGetCurrentTextureStatus::Success;
// TODO(dawn:2320): Check for optimality
info.suboptimal = false;
return info;
}
void SwapChainEGL::DetachFromSurfaceImpl() {
DAWN_ASSERT(mTexture == nullptr);
if (mEGLSurface != EGL_NO_SURFACE) {
Device* device = ToBackend(GetDevice());
device->GetEGL(false).DestroySurface(device->GetEGLDisplay(), mEGLSurface);
mEGLSurface = EGL_NO_SURFACE;
}
if (mTexture != nullptr) {
mTexture->APIDestroy();
mTexture = nullptr;
mTextureView = nullptr;
}
}
MaybeError SwapChainEGL::CreateEGLSurface(const DisplayEGL* display) {
DAWN_ASSERT(mEGLSurface == EGL_NO_SURFACE);
EGLConfig config = display->ChooseConfig(EGL_WINDOW_BIT, GetFormat());
if (config == kNoConfig) {
return DAWN_FORMAT_INTERNAL_ERROR("Couldn't find an EGLConfig for %s on %s.", GetFormat(),
GetSurface());
}
// [[maybe_unused]] to prevent unused variable warnings when platform code is disabled.
[[maybe_unused]] const EGLFunctions& egl = display->egl;
[[maybe_unused]] EGLDisplay eglDisplay = display->GetDisplay();
Surface* surface = GetSurface();
absl::InlinedVector<EGLint, 3> attribs;
auto AddAttrib = [&](EGLint attrib, EGLint value) {
attribs.push_back(attrib);
attribs.push_back(value);
};
if (GetFormat() == wgpu::TextureFormat::RGBA8UnormSrgb) {
DAWN_ASSERT(egl.HasExt(EGLExt::GLColorspace));
AddAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
}
attribs.push_back(EGL_NONE);
auto TryCreateSurface = [&]() -> ResultOrError<EGLSurface> {
switch (surface->GetType()) {
#if DAWN_PLATFORM_IS(ANDROID)
case Surface::Type::AndroidWindow:
return egl.CreateWindowSurface(
eglDisplay, config,
static_cast<ANativeWindow*>(surface->GetAndroidNativeWindow()), attribs.data());
#endif // DAWN_PLATFORM_IS(ANDROID)
#if defined(DAWN_ENABLE_BACKEND_METAL)
case Surface::Type::MetalLayer:
return egl.CreateWindowSurface(eglDisplay, config, surface->GetMetalLayer(),
attribs.data());
#endif // defined(DAWN_ENABLE_BACKEND_METAL)
#if DAWN_PLATFORM_IS(WIN32)
case Surface::Type::WindowsHWND:
return egl.CreateWindowSurface(
eglDisplay, config, static_cast<HWND>(surface->GetHWND()), attribs.data());
#endif // DAWN_PLATFORM_IS(WIN32)
#if defined(DAWN_USE_X11)
case Surface::Type::XlibWindow:
return egl.CreateWindowSurface(eglDisplay, config, surface->GetXWindow(),
attribs.data());
#endif // defined(DAWN_USE_X11)
// TODO(344814083): Add support for creating surfaces using EGL_KHR_platform_base and
// friends.
case Surface::Type::WaylandSurface:
default:
return DAWN_FORMAT_INTERNAL_ERROR("%s cannot be supported on EGL.", surface);
}
};
DAWN_TRY_ASSIGN(mEGLSurface, TryCreateSurface());
if (mEGLSurface == EGL_NO_SURFACE) {
return DAWN_FORMAT_INTERNAL_ERROR("Couldn't create an EGLSurface for %s.", surface);
}
return {};
}
} // namespace dawn::native::opengl