// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 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 "Surface.hpp"
#include "main.h"
#include "Display.h"
#include "Texture.hpp"
#include "common/Image.hpp"
#include "Context.hpp"
#include "common/debug.h"
#include "Main/FrameBuffer.hpp"
#if defined(USE_X11)
#include "Main/libX11.hpp"
#elif defined(_WIN32)
#include <tchar.h>
#elif defined(__APPLE__)
#include "OSXUtils.hpp"
#endif
#include <algorithm>
namespace gl
{
Surface::Surface()
{
}
Surface::~Surface()
{
}
}
namespace egl
{
Surface::Surface(const Display *display, const Config *config) : display(display), config(config)
{
}
Surface::~Surface()
{
Surface::deleteResources();
}
bool Surface::initialize()
{
ASSERT(!backBuffer && !depthStencil);
if(libGLESv2)
{
if(clientBuffer)
{
backBuffer = libGLESv2->createBackBufferFromClientBuffer(
egl::ClientBuffer(width, height, getClientBufferFormat(), clientBuffer, clientBufferPlane));
}
else
{
backBuffer = libGLESv2->createBackBuffer(width, height, config->mRenderTargetFormat, config->mSamples);
}
}
else if(libGLES_CM)
{
backBuffer = libGLES_CM->createBackBuffer(width, height, config->mRenderTargetFormat, config->mSamples);
}
if(!backBuffer)
{
ERR("Could not create back buffer");
deleteResources();
return error(EGL_BAD_ALLOC, false);
}
if(config->mDepthStencilFormat != sw::FORMAT_NULL)
{
if(libGLESv2)
{
depthStencil = libGLESv2->createDepthStencil(width, height, config->mDepthStencilFormat, config->mSamples);
}
else if(libGLES_CM)
{
depthStencil = libGLES_CM->createDepthStencil(width, height, config->mDepthStencilFormat, config->mSamples);
}
if(!depthStencil)
{
ERR("Could not create depth/stencil buffer for surface");
deleteResources();
return error(EGL_BAD_ALLOC, false);
}
}
return true;
}
void Surface::deleteResources()
{
if(depthStencil)
{
depthStencil->release();
depthStencil = nullptr;
}
if(texture)
{
texture->releaseTexImage();
texture = nullptr;
}
if(backBuffer)
{
backBuffer->release();
backBuffer = nullptr;
}
}
egl::Image *Surface::getRenderTarget()
{
if(backBuffer)
{
backBuffer->addRef();
}
return backBuffer;
}
egl::Image *Surface::getDepthStencil()
{
if(depthStencil)
{
depthStencil->addRef();
}
return depthStencil;
}
void Surface::setMipmapLevel(EGLint mipmapLevel)
{
this->mipmapLevel = mipmapLevel;
}
void Surface::setMultisampleResolve(EGLenum multisampleResolve)
{
this->multisampleResolve = multisampleResolve;
}
void Surface::setSwapBehavior(EGLenum swapBehavior)
{
this->swapBehavior = swapBehavior;
}
void Surface::setSwapInterval(EGLint interval)
{
if(swapInterval == interval)
{
return;
}
swapInterval = interval;
swapInterval = std::max(swapInterval, display->getMinSwapInterval());
swapInterval = std::min(swapInterval, display->getMaxSwapInterval());
}
EGLint Surface::getConfigID() const
{
return config->mConfigID;
}
EGLenum Surface::getSurfaceType() const
{
return config->mSurfaceType;
}
EGLint Surface::getWidth() const
{
return width;
}
EGLint Surface::getHeight() const
{
return height;
}
EGLint Surface::getMipmapLevel() const
{
return mipmapLevel;
}
EGLenum Surface::getMultisampleResolve() const
{
return multisampleResolve;
}
EGLint Surface::getPixelAspectRatio() const
{
return pixelAspectRatio;
}
EGLenum Surface::getRenderBuffer() const
{
return renderBuffer;
}
EGLenum Surface::getSwapBehavior() const
{
return swapBehavior;
}
EGLenum Surface::getTextureFormat() const
{
return textureFormat;
}
EGLenum Surface::getTextureTarget() const
{
return textureTarget;
}
EGLBoolean Surface::getLargestPBuffer() const
{
return largestPBuffer;
}
sw::Format Surface::getClientBufferFormat() const
{
switch(clientBufferType)
{
case GL_UNSIGNED_BYTE:
switch(clientBufferFormat)
{
case GL_RED:
return sw::FORMAT_R8;
case GL_RG:
return sw::FORMAT_G8R8;
case GL_BGRA_EXT:
return sw::FORMAT_A8R8G8B8;
default:
UNREACHABLE(clientBufferFormat);
break;
}
break;
case GL_UNSIGNED_SHORT:
switch(clientBufferFormat)
{
case GL_R16UI:
return sw::FORMAT_R16UI;
default:
UNREACHABLE(clientBufferFormat);
break;
}
break;
case GL_HALF_FLOAT_OES:
case GL_HALF_FLOAT:
switch(clientBufferFormat)
{
case GL_RGBA:
return sw::FORMAT_A16B16G16R16F;
default:
UNREACHABLE(clientBufferFormat);
break;
}
default:
UNREACHABLE(clientBufferType);
break;
}
return sw::FORMAT_NULL;
}
void Surface::setBoundTexture(egl::Texture *texture)
{
this->texture = texture;
}
egl::Texture *Surface::getBoundTexture() const
{
return texture;
}
WindowSurface::WindowSurface(Display *display, const Config *config, EGLNativeWindowType window)
: Surface(display, config), window(window)
{
pixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
}
WindowSurface::~WindowSurface()
{
WindowSurface::deleteResources();
}
bool WindowSurface::initialize()
{
ASSERT(!frameBuffer && !backBuffer && !depthStencil);
return checkForResize();
}
void WindowSurface::swap()
{
if(backBuffer && frameBuffer)
{
frameBuffer->flip(backBuffer);
checkForResize();
}
}
EGLNativeWindowType WindowSurface::getWindowHandle() const
{
return window;
}
bool WindowSurface::checkForResize()
{
#if defined(_WIN32)
RECT client;
BOOL status = GetClientRect(window, &client);
if(status == 0)
{
return error(EGL_BAD_NATIVE_WINDOW, false);
}
int windowWidth = client.right - client.left;
int windowHeight = client.bottom - client.top;
#elif defined(__ANDROID__)
int windowWidth; window->query(window, NATIVE_WINDOW_WIDTH, &windowWidth);
int windowHeight; window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
#elif defined(USE_X11)
XWindowAttributes windowAttributes;
Status status = libX11->XGetWindowAttributes((::Display*)display->getNativeDisplay(), window, &windowAttributes);
if(status == 0)
{
return error(EGL_BAD_NATIVE_WINDOW, false);
}
int windowWidth = windowAttributes.width;
int windowHeight = windowAttributes.height;
#elif defined(__linux__)
// Non X11 linux is headless only
int windowWidth = 100;
int windowHeight = 100;
#elif defined(__APPLE__)
int windowWidth;
int windowHeight;
sw::OSX::GetNativeWindowSize(window, windowWidth, windowHeight);
#elif defined(__Fuchsia__)
// TODO(crbug.com/800951): Integrate with Mozart.
int windowWidth = 100;
int windowHeight = 100;
#else
#error "WindowSurface::checkForResize unimplemented for this platform"
#endif
if((windowWidth != width) || (windowHeight != height))
{
bool success = reset(windowWidth, windowHeight);
if(getCurrentDrawSurface() == this)
{
getCurrentContext()->makeCurrent(this);
}
return success;
}
return true; // Success
}
void WindowSurface::deleteResources()
{
delete frameBuffer;
frameBuffer = nullptr;
Surface::deleteResources();
}
bool WindowSurface::reset(int backBufferWidth, int backBufferHeight)
{
width = backBufferWidth;
height = backBufferHeight;
deleteResources();
if(window)
{
if(libGLESv2)
{
frameBuffer = libGLESv2->createFrameBuffer(display->getNativeDisplay(), window, width, height);
}
else if(libGLES_CM)
{
frameBuffer = libGLES_CM->createFrameBuffer(display->getNativeDisplay(), window, width, height);
}
if(!frameBuffer)
{
ERR("Could not create frame buffer");
deleteResources();
return error(EGL_BAD_ALLOC, false);
}
}
return Surface::initialize();
}
PBufferSurface::PBufferSurface(Display *display, const Config *config, EGLint width, EGLint height,
EGLenum textureFormat, EGLenum textureTarget, EGLenum clientBufferFormat,
EGLenum clientBufferType, EGLBoolean largestPBuffer, EGLClientBuffer clientBuffer,
EGLint clientBufferPlane)
: Surface(display, config)
{
this->width = width;
this->height = height;
this->largestPBuffer = largestPBuffer;
this->textureFormat = textureFormat;
this->textureTarget = textureTarget;
this->clientBufferFormat = clientBufferFormat;
this->clientBufferType = clientBufferType;
this->clientBuffer = clientBuffer;
this->clientBufferPlane = clientBufferPlane;
}
PBufferSurface::~PBufferSurface()
{
PBufferSurface::deleteResources();
}
void PBufferSurface::swap()
{
// No effect
}
EGLNativeWindowType PBufferSurface::getWindowHandle() const
{
UNREACHABLE(-1); // Should not be called. Only WindowSurface has a window handle.
return 0;
}
void PBufferSurface::deleteResources()
{
Surface::deleteResources();
}
}