Commit 7f448b58 by Geoff Lang

Add an EGL_ANGLE_surface_orientation extension.

BUG=angleproject:1262 Change-Id: Ifbb0f5302311a68a0c6f02baaea706cbb7055a52 Reviewed-on: https://chromium-review.googlesource.com/320011Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 666cb828
Name
ANGLE_surface_orientation
Name Strings
EGL_ANGLE_surface_orientation
Contributors
Geoff Lang, Google
Contacts
Geoff Lang, Google (geofflang 'at' google 'dot' com)
Status
Draft
Version
Version 1, 2015-12-15
Number
EGL Extension XXX
Extension Type
EGL display extension
Dependencies
Written based on the wording of the EGL 1.5 Specification
(August 7 2014).
Overview
This extension provides a mechanism for querying the most optimal
orientation of a window surface and creating window sufraces with
non-default orientations for the most performant rendering.
New Types
None
New Procedures and Functions
None
New Tokens
New EGLConfig bitmask attribute name:
EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
Accepted as an attribute name in the <attrib_list> argument of
eglCreateWindowSurface and attribute name in the <attribute>
argument of eglQuerySurface:
EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
Valid bitfields in the EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE bitmask
attribute of EGLConfig and EGL_SURFACE_ORIENTATION_ANGLE bitmask attribute
of eglCreateWindowSurface:
EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
Additions to the EGL Specification
Additions to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors)
Add to table 3.1 (EGLConfig Attributes)
Attribute Type Notes
------------------------------------- ------- ----------------------
EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE bitmask Optimal window surface
orientation.
Add a paragraph to section 3.4, section Other EGLConfig Attribute
Descriptions.
"EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE is a mask indicating which
window surface orientation will provide the best performance."
Add to table 3.4 (Default values and match criteria for EGLConfig
attributes):
Attribute Default Selection Sort Sort
Criteria Order Priority
------------------------------------- ------- --------- ------- --------
EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0 Exact None
Add a paragraph to section 3.5.1, section Creating On-Screen Rendering
Surfaces.
EGL_SURFACE_ORIENTATION_ANGLE attribute specifies how the surface's content
will appear on the screen. If its value contains
EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE then all displayed content will be
inverted along the vertical axis. Similarly, if its value contains
EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE then all displayed content will be
inverted along the horizontal axis.
Add to table 3.5 (Queryable surface attributes and types):
Attribute Type Description
----------------------------- ------- ----------------------
EGL_SURFACE_ORIENTATION_ANGLE bitmask Orientation of surface
Add a paragraph to section 3.5.6, Surface Attributes:
"Querying EGL_SURFACE_ORIENTATION_ANGLE returns the orientation of the
surface. For a window surface, this is the same attribute value specified
when the surface was created. For other types of surfaces, it is always
0."
Issues
1) What about dirty regions and sub regions specified by extensions such as
NV_post_sub_buffer?
These regions will be applied to the same region of the window as
before because they are often specified based on events from the
operating system. The content in these regions will be displayed
according to the value of EGL_SURFACE_ORIENTATION_ANGLE.
Revision History
Version 1, 2015-12-15 (Geoff Lang)
- Initial draft
......@@ -509,6 +509,14 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSu
#define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6
#endif /* EGL_ANGLE_flexible_surface_compatibility */
#ifndef EGL_ANGLE_surface_orientation
#define EGL_ANGLE_surface_orientation
#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
#endif /* EGL_ANGLE_surface_orientation */
#ifndef EGL_ARM_pixmap_multisample_discard
#define EGL_ARM_pixmap_multisample_discard 1
#define EGL_DISCARD_SAMPLES_ARM 0x3286
......
......@@ -609,6 +609,7 @@ DisplayExtensions::DisplayExtensions()
querySurfacePointer(false),
windowFixedSize(false),
keyedMutex(false),
surfaceOrientation(false),
postSubBuffer(false),
createContext(false),
deviceQuery(false),
......@@ -636,6 +637,7 @@ std::vector<std::string> DisplayExtensions::getStrings() const
InsertExtensionString("EGL_ANGLE_query_surface_pointer", querySurfacePointer, &extensionStrings);
InsertExtensionString("EGL_ANGLE_window_fixed_size", windowFixedSize, &extensionStrings);
InsertExtensionString("EGL_ANGLE_keyed_mutex", keyedMutex, &extensionStrings);
InsertExtensionString("EGL_ANGLE_surface_orientation", surfaceOrientation, &extensionStrings);
InsertExtensionString("EGL_NV_post_sub_buffer", postSubBuffer, &extensionStrings);
InsertExtensionString("EGL_KHR_create_context", createContext, &extensionStrings);
InsertExtensionString("EGL_EXT_device_query", deviceQuery, &extensionStrings);
......
......@@ -427,6 +427,9 @@ struct DisplayExtensions
// EGL_ANGLE_keyed_mutex
bool keyedMutex;
// EGL_ANGLE_surface_orientation
bool surfaceOrientation;
// EGL_NV_post_sub_buffer
bool postSubBuffer;
......
......@@ -57,7 +57,8 @@ Config::Config()
transparentType(EGL_NONE),
transparentRedValue(0),
transparentGreenValue(0),
transparentBlueValue(0)
transparentBlueValue(0),
optimalOrientation(0)
{
}
......@@ -251,6 +252,9 @@ std::vector<const Config*> ConfigSet::filter(const AttributeMap &attributeMap) c
case EGL_MAX_PBUFFER_WIDTH: match = config.maxPBufferWidth >= attributeValue; break;
case EGL_MAX_PBUFFER_HEIGHT: match = config.maxPBufferHeight >= attributeValue; break;
case EGL_MAX_PBUFFER_PIXELS: match = config.maxPBufferPixels >= attributeValue; break;
case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE:
match = config.optimalOrientation == attributeValue;
break;
default: UNREACHABLE();
}
......
......@@ -64,6 +64,7 @@ struct Config
EGLint transparentRedValue; // Transparent red value
EGLint transparentGreenValue; // Transparent green value
EGLint transparentBlueValue; // Transparent blue value
EGLint optimalOrientation; // Optimal window surface orientation
};
class ConfigSet
......
......@@ -483,6 +483,15 @@ bool Display::getConfigAttrib(const Config *configuration, EGLint attribute, EGL
case EGL_MAX_PBUFFER_WIDTH: *value = configuration->maxPBufferWidth; break;
case EGL_MAX_PBUFFER_HEIGHT: *value = configuration->maxPBufferHeight; break;
case EGL_MAX_PBUFFER_PIXELS: *value = configuration->maxPBufferPixels; break;
case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE:
if (!getExtensions().surfaceOrientation)
{
return false;
}
*value = configuration->optimalOrientation;
break;
default:
return false;
}
......
......@@ -41,7 +41,9 @@ Surface::Surface(rx::SurfaceImpl *impl,
// FIXME: Determine actual pixel aspect ratio
mPixelAspectRatio(static_cast<EGLint>(1.0 * EGL_DISPLAY_SCALING)),
mRenderBuffer(EGL_BACK_BUFFER),
mSwapBehavior(impl->getSwapBehavior())
mSwapBehavior(impl->getSwapBehavior()),
mOrientation(0),
mTexture()
{
mPostSubBufferRequested = (attributes.get(EGL_POST_SUB_BUFFER_SUPPORTED_NV, EGL_FALSE) == EGL_TRUE);
mFlexibleSurfaceCompatibilityRequested =
......@@ -60,6 +62,8 @@ Surface::Surface(rx::SurfaceImpl *impl,
mTextureTarget = attributes.get(EGL_TEXTURE_TARGET, EGL_NO_TEXTURE);
}
mOrientation = attributes.get(EGL_SURFACE_ORIENTATION_ANGLE, 0);
mDefaultFramebuffer = createDefaultFramebuffer();
ASSERT(mDefaultFramebuffer != nullptr);
}
......
......@@ -82,6 +82,7 @@ class Surface final : public gl::FramebufferAttachmentObject
{
return mFlexibleSurfaceCompatibilityRequested;
}
EGLint getOrientation() const { return mOrientation; }
private:
virtual ~Surface();
......@@ -116,6 +117,8 @@ class Surface final : public gl::FramebufferAttachmentObject
EGLenum mRenderBuffer; // Render buffer
EGLenum mSwapBehavior; // Buffer swap behavior
EGLint mOrientation;
BindingPointer<gl::Texture> mTexture;
};
......
......@@ -172,6 +172,7 @@ SurfaceImpl *DisplayD3D::createWindowSurface(const egl::Config *configuration,
EGLint width = attribs.get(EGL_WIDTH, 0);
EGLint height = attribs.get(EGL_HEIGHT, 0);
EGLint fixedSize = attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE);
EGLint orientation = attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0);
if (!fixedSize)
{
......@@ -179,8 +180,8 @@ SurfaceImpl *DisplayD3D::createWindowSurface(const egl::Config *configuration,
height = -1;
}
return SurfaceD3D::createFromWindow(
mRenderer, mDisplay, configuration, window, fixedSize, width, height);
return SurfaceD3D::createFromWindow(mRenderer, mDisplay, configuration, window, fixedSize,
width, height, orientation);
}
SurfaceImpl *DisplayD3D::createPbufferSurface(const egl::Config *configuration,
......
......@@ -143,7 +143,11 @@ class RendererD3D : public Renderer, public BufferFactoryD3D
// Direct3D Specific methods
virtual DeviceIdentifier getAdapterIdentifier() const = 0;
virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat) = 0;
virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation) = 0;
virtual gl::Error generateSwizzle(gl::Texture *texture) = 0;
virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler) = 0;
......
......@@ -24,13 +24,20 @@ namespace rx
SurfaceD3D *SurfaceD3D::createOffscreen(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLClientBuffer shareHandle,
EGLint width, EGLint height)
{
return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, shareHandle, NULL);
return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, 0, shareHandle, NULL);
}
SurfaceD3D *SurfaceD3D::createFromWindow(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLNativeWindowType window,
EGLint fixedSize, EGLint width, EGLint height)
SurfaceD3D *SurfaceD3D::createFromWindow(RendererD3D *renderer,
egl::Display *display,
const egl::Config *config,
EGLNativeWindowType window,
EGLint fixedSize,
EGLint width,
EGLint height,
EGLint orientation)
{
return new SurfaceD3D(renderer, display, config, width, height, fixedSize, static_cast<EGLClientBuffer>(0), window);
return new SurfaceD3D(renderer, display, config, width, height, fixedSize, orientation,
static_cast<EGLClientBuffer>(0), window);
}
SurfaceD3D::SurfaceD3D(RendererD3D *renderer,
......@@ -39,12 +46,14 @@ SurfaceD3D::SurfaceD3D(RendererD3D *renderer,
EGLint width,
EGLint height,
EGLint fixedSize,
EGLint orientation,
EGLClientBuffer shareHandle,
EGLNativeWindowType window)
: SurfaceImpl(),
mRenderer(renderer),
mDisplay(display),
mFixedSize(fixedSize == EGL_TRUE),
mOrientation(orientation),
mRenderTargetFormat(config->renderTargetFormat),
mDepthStencilFormat(config->depthStencilFormat),
mSwapChain(nullptr),
......@@ -128,7 +137,8 @@ egl::Error SurfaceD3D::resetSwapChain()
height = mHeight;
}
mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle, mRenderTargetFormat, mDepthStencilFormat);
mSwapChain = mRenderer->createSwapChain(mNativeWindow, mShareHandle, mRenderTargetFormat,
mDepthStencilFormat, mOrientation);
if (!mSwapChain)
{
return egl::Error(EGL_BAD_ALLOC);
......
......@@ -25,8 +25,14 @@ class RendererD3D;
class SurfaceD3D : public SurfaceImpl
{
public:
static SurfaceD3D *createFromWindow(RendererD3D *renderer, egl::Display *display, const egl::Config *config,
EGLNativeWindowType window, EGLint fixedSize, EGLint width, EGLint height);
static SurfaceD3D *createFromWindow(RendererD3D *renderer,
egl::Display *display,
const egl::Config *config,
EGLNativeWindowType window,
EGLint fixedSize,
EGLint width,
EGLint height,
EGLint orientation);
static SurfaceD3D *createOffscreen(RendererD3D *renderer, egl::Display *display, const egl::Config *config,
EGLClientBuffer shareHandle, EGLint width, EGLint height);
~SurfaceD3D() override;
......@@ -60,8 +66,15 @@ class SurfaceD3D : public SurfaceImpl
FramebufferAttachmentRenderTarget **rtOut) override;
private:
SurfaceD3D(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLint width, EGLint height,
EGLint fixedSize, EGLClientBuffer shareHandle, EGLNativeWindowType window);
SurfaceD3D(RendererD3D *renderer,
egl::Display *display,
const egl::Config *config,
EGLint width,
EGLint height,
EGLint fixedSize,
EGLint orientation,
EGLClientBuffer shareHandle,
EGLNativeWindowType window);
egl::Error swapRect(EGLint x, EGLint y, EGLint width, EGLint height);
egl::Error resetSwapChain(int backbufferWidth, int backbufferHeight);
......@@ -71,6 +84,7 @@ class SurfaceD3D : public SurfaceImpl
egl::Display *mDisplay;
bool mFixedSize;
GLint mOrientation;
GLenum mRenderTargetFormat;
GLenum mDepthStencilFormat;
......
......@@ -989,6 +989,8 @@ egl::ConfigSet Renderer11::generateConfigs() const
const gl::Caps &rendererCaps = getRendererCaps();
const gl::TextureCapsMap &rendererTextureCaps = getRendererTextureCaps();
const EGLint optimalSurfaceOrientation = EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE;
egl::ConfigSet configs;
for (size_t formatIndex = 0; formatIndex < ArraySize(colorBufferFormats); formatIndex++)
{
......@@ -1043,6 +1045,7 @@ egl::ConfigSet Renderer11::generateConfigs() const
config.transparentRedValue = 0;
config.transparentGreenValue = 0;
config.transparentBlueValue = 0;
config.optimalOrientation = optimalSurfaceOrientation;
configs.add(config);
}
......@@ -1070,6 +1073,7 @@ void Renderer11::generateDisplayExtensions(egl::DisplayExtensions *outExtensions
outExtensions->querySurfacePointer = true;
outExtensions->windowFixedSize = true;
outExtensions->surfaceOrientation = true;
// D3D11 does not support present with dirty rectangles until DXGI 1.2.
outExtensions->postSubBuffer = mRenderer11DeviceCaps.supportsDXGI1_2;
......@@ -1136,9 +1140,14 @@ gl::Error Renderer11::finish()
return gl::Error(GL_NO_ERROR);
}
SwapChainD3D *Renderer11::createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
SwapChainD3D *Renderer11::createSwapChain(NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation)
{
return new SwapChain11(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat);
return new SwapChain11(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat,
orientation);
}
void *Renderer11::getD3DDevice()
......@@ -2399,12 +2408,7 @@ void Renderer11::markAllStateDirty()
{
TRACE_EVENT0("gpu.angle", "Renderer11::markAllStateDirty");
for (size_t rtIndex = 0; rtIndex < ArraySize(mAppliedRTVs); rtIndex++)
{
mAppliedRTVs[rtIndex] = DirtyPointer;
}
mAppliedDSV = DirtyPointer;
mDepthStencilInitialized = false;
markRenderTargetStateDirty();
// We reset the current SRV data because it might not be in sync with D3D's state
// anymore. For example when a currently used SRV is used as an RTV, D3D silently
......@@ -2468,6 +2472,16 @@ void Renderer11::markAllStateDirty()
mCurrentPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
}
void Renderer11::markRenderTargetStateDirty()
{
for (size_t rtIndex = 0; rtIndex < ArraySize(mAppliedRTVs); rtIndex++)
{
mAppliedRTVs[rtIndex] = DirtyPointer;
}
mAppliedDSV = DirtyPointer;
mDepthStencilInitialized = false;
}
void Renderer11::releaseDeviceResources()
{
mStateCache.clear();
......
......@@ -111,7 +111,11 @@ class Renderer11 : public RendererD3D
gl::Error flush() override;
gl::Error finish() override;
virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
SwapChainD3D *createSwapChain(NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation) override;
virtual gl::Error generateSwizzle(gl::Texture *texture);
virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler);
......@@ -139,6 +143,7 @@ class Renderer11 : public RendererD3D
void applyTransformFeedbackBuffers(const gl::State &state) override;
virtual void markAllStateDirty();
void markRenderTargetStateDirty();
// lost device
bool testDeviceLost() override;
......
......@@ -8,6 +8,8 @@
#include "libANGLE/renderer/d3d/d3d11/SwapChain11.h"
#include <EGL/eglext.h>
#include "libANGLE/features.h"
#include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
#include "libANGLE/renderer/d3d/d3d11/NativeWindow.h"
......@@ -29,40 +31,43 @@
namespace rx
{
SwapChain11::SwapChain11(Renderer11 *renderer, NativeWindow nativeWindow, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat)
SwapChain11::SwapChain11(Renderer11 *renderer,
NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation)
: SwapChainD3D(nativeWindow, shareHandle, backBufferFormat, depthBufferFormat),
mRenderer(renderer),
mWidth(-1),
mHeight(-1),
mOrientation(orientation),
mAppCreatedShareHandle(mShareHandle != nullptr),
mSwapInterval(0),
mPassThroughResourcesInit(false),
mFirstSwap(true),
mSwapChain(nullptr),
mSwapChain1(nullptr),
mKeyedMutex(nullptr),
mNeedsOffscreenTexture(orientation != EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE),
mBackBufferTexture(nullptr),
mBackBufferRTView(nullptr),
mBackBufferSRView(nullptr),
mOffscreenTexture(nullptr),
mOffscreenRTView(nullptr),
mOffscreenSRView(nullptr),
mDepthStencilTexture(nullptr),
mDepthStencilDSView(nullptr),
mDepthStencilSRView(nullptr),
mQuadVB(nullptr),
mPassThroughSampler(nullptr),
mPassThroughIL(nullptr),
mPassThroughVS(nullptr),
mPassThroughPS(nullptr),
mPassThroughRS(nullptr),
mColorRenderTarget(this, renderer, false),
mDepthStencilRenderTarget(this, renderer, true)
{
mHeight = -1;
mWidth = -1;
mAppCreatedShareHandle = mShareHandle != NULL;
mSwapInterval = 0;
mSwapChain = NULL;
mSwapChain1 = nullptr;
mKeyedMutex = nullptr;
mBackBufferTexture = NULL;
mBackBufferRTView = NULL;
mOffscreenTexture = NULL;
mOffscreenRTView = NULL;
mOffscreenSRView = NULL;
mDepthStencilTexture = NULL;
mDepthStencilDSView = NULL;
mDepthStencilSRView = NULL;
mQuadVB = NULL;
mPassThroughSampler = NULL;
mPassThroughIL = NULL;
mPassThroughVS = NULL;
mPassThroughPS = NULL;
}
SwapChain11::~SwapChain11()
......@@ -77,6 +82,7 @@ void SwapChain11::release()
SafeRelease(mKeyedMutex);
SafeRelease(mBackBufferTexture);
SafeRelease(mBackBufferRTView);
SafeRelease(mBackBufferSRView);
SafeRelease(mOffscreenTexture);
SafeRelease(mOffscreenRTView);
SafeRelease(mOffscreenSRView);
......@@ -88,6 +94,7 @@ void SwapChain11::release()
SafeRelease(mPassThroughIL);
SafeRelease(mPassThroughVS);
SafeRelease(mPassThroughPS);
SafeRelease(mPassThroughRS);
if (!mAppCreatedShareHandle)
{
......@@ -95,18 +102,47 @@ void SwapChain11::release()
}
}
void SwapChain11::releaseOffscreenTexture()
void SwapChain11::releaseOffscreenColorBuffer()
{
SafeRelease(mOffscreenTexture);
SafeRelease(mOffscreenRTView);
SafeRelease(mOffscreenSRView);
}
void SwapChain11::releaseOffscreenDepthBuffer()
{
SafeRelease(mDepthStencilTexture);
SafeRelease(mDepthStencilDSView);
SafeRelease(mDepthStencilSRView);
}
EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHeight)
EGLint SwapChain11::resetOffscreenBuffers(int backbufferWidth, int backbufferHeight)
{
if (mNeedsOffscreenTexture)
{
EGLint result = resetOffscreenColorBuffer(backbufferWidth, backbufferHeight);
if (result != EGL_SUCCESS)
{
return result;
}
}
EGLint result = resetOffscreenDepthBuffer(backbufferWidth, backbufferHeight);
if (result != EGL_SUCCESS)
{
return result;
}
mWidth = backbufferWidth;
mHeight = backbufferHeight;
return EGL_SUCCESS;
}
EGLint SwapChain11::resetOffscreenColorBuffer(int backbufferWidth, int backbufferHeight)
{
ASSERT(mNeedsOffscreenTexture);
TRACE_EVENT0("gpu.angle", "SwapChain11::resetOffscreenTexture");
ID3D11Device *device = mRenderer->getDevice();
......@@ -125,7 +161,7 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei
const int previousWidth = mWidth;
const int previousHeight = mHeight;
releaseOffscreenTexture();
releaseOffscreenColorBuffer();
const d3d11::TextureFormat &backbufferFormatInfo = d3d11::GetTextureFormatInfo(mOffscreenRenderTargetFormat, mRenderer->getRenderer11DeviceCaps());
......@@ -230,7 +266,6 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei
mKeyedMutex = d3d11::DynamicCastComObject<IDXGIKeyedMutex>(mOffscreenTexture);
}
D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc;
offscreenRTVDesc.Format = backbufferFormatInfo.rtvFormat;
offscreenRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
......@@ -250,10 +285,41 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei
ASSERT(SUCCEEDED(result));
d3d11::SetDebugName(mOffscreenSRView, "Offscreen back buffer shader resource");
const d3d11::TextureFormat &depthBufferFormatInfo = d3d11::GetTextureFormatInfo(mDepthBufferFormat, mRenderer->getRenderer11DeviceCaps());
if (previousOffscreenTexture != nullptr)
{
D3D11_BOX sourceBox = {0};
sourceBox.left = 0;
sourceBox.right = std::min(previousWidth, backbufferWidth);
sourceBox.top = std::max(previousHeight - backbufferHeight, 0);
sourceBox.bottom = previousHeight;
sourceBox.front = 0;
sourceBox.back = 1;
ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
const int yoffset = std::max(backbufferHeight - previousHeight, 0);
deviceContext->CopySubresourceRegion(mOffscreenTexture, 0, 0, yoffset, 0,
previousOffscreenTexture, 0, &sourceBox);
SafeRelease(previousOffscreenTexture);
if (mSwapChain)
{
swapRect(0, 0, backbufferWidth, backbufferHeight);
}
}
return EGL_SUCCESS;
}
EGLint SwapChain11::resetOffscreenDepthBuffer(int backbufferWidth, int backbufferHeight)
{
releaseOffscreenDepthBuffer();
if (mDepthBufferFormat != GL_NONE)
{
const d3d11::TextureFormat &depthBufferFormatInfo =
d3d11::GetTextureFormatInfo(mDepthBufferFormat, mRenderer->getRenderer11DeviceCaps());
D3D11_TEXTURE2D_DESC depthStencilTextureDesc;
depthStencilTextureDesc.Width = backbufferWidth;
depthStencilTextureDesc.Height = backbufferHeight;
......@@ -273,7 +339,9 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei
depthStencilTextureDesc.CPUAccessFlags = 0;
depthStencilTextureDesc.MiscFlags = 0;
result = device->CreateTexture2D(&depthStencilTextureDesc, NULL, &mDepthStencilTexture);
ID3D11Device *device = mRenderer->getDevice();
HRESULT result =
device->CreateTexture2D(&depthStencilTextureDesc, NULL, &mDepthStencilTexture);
if (FAILED(result))
{
ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
......@@ -314,31 +382,6 @@ EGLint SwapChain11::resetOffscreenTexture(int backbufferWidth, int backbufferHei
}
}
mWidth = backbufferWidth;
mHeight = backbufferHeight;
if (previousOffscreenTexture != NULL)
{
D3D11_BOX sourceBox = {0};
sourceBox.left = 0;
sourceBox.right = std::min(previousWidth, mWidth);
sourceBox.top = std::max(previousHeight - mHeight, 0);
sourceBox.bottom = previousHeight;
sourceBox.front = 0;
sourceBox.back = 1;
ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
const int yoffset = std::max(mHeight - previousHeight, 0);
deviceContext->CopySubresourceRegion(mOffscreenTexture, 0, 0, yoffset, 0, previousOffscreenTexture, 0, &sourceBox);
SafeRelease(previousOffscreenTexture);
if (mSwapChain)
{
swapRect(0, 0, mWidth, mHeight);
}
}
return EGL_SUCCESS;
}
......@@ -358,11 +401,18 @@ EGLint SwapChain11::resize(EGLint backbufferWidth, EGLint backbufferHeight)
return EGL_SUCCESS;
}
// Don't resize unnecessarily
if (mWidth == backbufferWidth && mHeight == backbufferHeight)
{
return EGL_SUCCESS;
}
// Can only call resize if we have already created our swap buffer and resources
ASSERT(mSwapChain && mBackBufferTexture && mBackBufferRTView);
ASSERT(mSwapChain && mBackBufferTexture && mBackBufferRTView && mBackBufferSRView);
SafeRelease(mBackBufferTexture);
SafeRelease(mBackBufferRTView);
SafeRelease(mBackBufferSRView);
// Resize swap chain
DXGI_SWAP_CHAIN_DESC desc;
......@@ -398,14 +448,22 @@ EGLint SwapChain11::resize(EGLint backbufferWidth, EGLint backbufferHeight)
d3d11::SetDebugName(mBackBufferTexture, "Back buffer texture");
result = device->CreateRenderTargetView(mBackBufferTexture, NULL, &mBackBufferRTView);
ASSERT(SUCCEEDED(result));
}
if (SUCCEEDED(result))
{
d3d11::SetDebugName(mBackBufferRTView, "Back buffer render target");
}
if (SUCCEEDED(result))
{
d3d11::SetDebugName(mBackBufferRTView, "Back buffer render target");
result = device->CreateShaderResourceView(mBackBufferTexture, nullptr, &mBackBufferSRView);
ASSERT(SUCCEEDED(result));
if (SUCCEEDED(result))
{
d3d11::SetDebugName(mBackBufferSRView, "Back buffer shader resource");
}
}
return resetOffscreenTexture(backbufferWidth, backbufferHeight);
mFirstSwap = true;
return resetOffscreenBuffers(backbufferWidth, backbufferHeight);
}
DXGI_FORMAT SwapChain11::getSwapChainNativeFormat() const
......@@ -417,6 +475,20 @@ DXGI_FORMAT SwapChain11::getSwapChainNativeFormat() const
EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swapInterval)
{
mSwapInterval = static_cast<unsigned int>(swapInterval);
if (mSwapInterval > 4)
{
// IDXGISwapChain::Present documentation states that valid sync intervals are in the [0,4]
// range
return EGL_BAD_PARAMETER;
}
// If the swap chain already exists, just resize
if (mSwapChain != nullptr)
{
return resize(backbufferWidth, backbufferHeight);
}
TRACE_EVENT0("gpu.angle", "SwapChain11::reset");
ID3D11Device *device = mRenderer->getDevice();
......@@ -432,17 +504,10 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
SafeRelease(mBackBufferTexture);
SafeRelease(mBackBufferRTView);
mSwapInterval = static_cast<unsigned int>(swapInterval);
if (mSwapInterval > 4)
{
// IDXGISwapChain::Present documentation states that valid sync intervals are in the [0,4] range
return EGL_BAD_PARAMETER;
}
// EGL allows creating a surface with 0x0 dimension, however, DXGI does not like 0x0 swapchains
if (backbufferWidth < 1 || backbufferHeight < 1)
{
releaseOffscreenTexture();
releaseOffscreenColorBuffer();
return EGL_SUCCESS;
}
......@@ -479,9 +544,15 @@ EGLint SwapChain11::reset(int backbufferWidth, int backbufferHeight, EGLint swap
result = device->CreateRenderTargetView(mBackBufferTexture, NULL, &mBackBufferRTView);
ASSERT(SUCCEEDED(result));
d3d11::SetDebugName(mBackBufferRTView, "Back buffer render target");
result = device->CreateShaderResourceView(mBackBufferTexture, nullptr, &mBackBufferSRView);
ASSERT(SUCCEEDED(result));
d3d11::SetDebugName(mBackBufferSRView, "Back buffer shader resource view");
}
return resetOffscreenTexture(backbufferWidth, backbufferHeight);
mFirstSwap = true;
return resetOffscreenBuffers(backbufferWidth, backbufferHeight);
}
void SwapChain11::initPassThroughResources()
......@@ -549,12 +620,50 @@ void SwapChain11::initPassThroughResources()
ASSERT(SUCCEEDED(result));
d3d11::SetDebugName(mPassThroughPS, "Swap chain pass through pixel shader");
// Use the default rasterizer state but without culling
D3D11_RASTERIZER_DESC rasterizerDesc;
rasterizerDesc.FillMode = D3D11_FILL_SOLID;
rasterizerDesc.CullMode = D3D11_CULL_NONE;
rasterizerDesc.FrontCounterClockwise = FALSE;
rasterizerDesc.DepthBias = 0;
rasterizerDesc.SlopeScaledDepthBias = 0.0f;
rasterizerDesc.DepthBiasClamp = 0.0f;
rasterizerDesc.DepthClipEnable = TRUE;
rasterizerDesc.ScissorEnable = FALSE;
rasterizerDesc.MultisampleEnable = FALSE;
rasterizerDesc.AntialiasedLineEnable = FALSE;
result = device->CreateRasterizerState(&rasterizerDesc, &mPassThroughRS);
ASSERT(SUCCEEDED(result));
d3d11::SetDebugName(mPassThroughRS, "Swap chain pass through rasterizer state");
mPassThroughResourcesInit = true;
}
// parameters should be validated/clamped by caller
EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
{
if (mNeedsOffscreenTexture)
{
EGLint result = copyOffscreenToBackbuffer(x, y, width, height);
if (result != EGL_SUCCESS)
{
return result;
}
}
EGLint result = present(x, y, width, height);
if (result != EGL_SUCCESS)
{
return result;
}
mRenderer->onSwap();
return EGL_SUCCESS;
}
EGLint SwapChain11::copyOffscreenToBackbuffer(EGLint x, EGLint y, EGLint width, EGLint height)
{
if (!mSwapChain)
{
return EGL_SUCCESS;
......@@ -562,7 +671,6 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
initPassThroughResources();
ID3D11Device *device = mRenderer->getDevice();
ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
// Set vertices
......@@ -586,6 +694,16 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
float u2 = (x + width) / float(mWidth);
float v2 = (y + height) / float(mHeight);
// Invert the quad vertices depending on the surface orientation.
if ((mOrientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE) != 0)
{
std::swap(x1, x2);
}
if ((mOrientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) != 0)
{
std::swap(y1, y2);
}
d3d11::SetPositionTexCoordVertex(&vertices[0], x1, y1, u1, v1);
d3d11::SetPositionTexCoordVertex(&vertices[1], x1, y2, u1, v2);
d3d11::SetPositionTexCoordVertex(&vertices[2], x2, y1, u2, v1);
......@@ -603,7 +721,7 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
static const float blendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
deviceContext->OMSetBlendState(NULL, blendFactor, 0xFFFFFFF);
deviceContext->RSSetState(NULL);
deviceContext->RSSetState(mPassThroughRS);
// Apply shaders
deviceContext->IASetInputLayout(mPassThroughIL);
......@@ -640,28 +758,54 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
mRenderer->unapplyRenderTargets();
mRenderer->markAllStateDirty();
return EGL_SUCCESS;
}
EGLint SwapChain11::present(EGLint x, EGLint y, EGLint width, EGLint height)
{
if (!mSwapChain)
{
return EGL_SUCCESS;
}
UINT swapInterval = mSwapInterval;
#if ANGLE_VSYNC == ANGLE_DISABLED
result = mSwapChain->Present(0, 0);
#else
swapInterval = 0;
#endif
HRESULT result = S_OK;
// Use IDXGISwapChain1::Present1 with a dirty rect if DXGI 1.2 is available.
if (mSwapChain1 != nullptr)
{
RECT rect =
if (mFirstSwap)
{
// Can't swap with a dirty rect if this swap chain has never swapped before
DXGI_PRESENT_PARAMETERS params = {0, nullptr, nullptr, nullptr};
result = mSwapChain1->Present1(swapInterval, 0, &params);
}
else
{
static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)
};
DXGI_PRESENT_PARAMETERS params = { 1, &rect, nullptr, nullptr };
result = mSwapChain1->Present1(mSwapInterval, 0, &params);
RECT rect = {static_cast<LONG>(x), static_cast<LONG>(mHeight - y - height),
static_cast<LONG>(x + width), static_cast<LONG>(mHeight - y)};
DXGI_PRESENT_PARAMETERS params = {1, &rect, nullptr, nullptr};
result = mSwapChain1->Present1(swapInterval, 0, &params);
}
}
else
{
result = mSwapChain->Present(mSwapInterval, 0);
result = mSwapChain->Present(swapInterval, 0);
}
#endif
mFirstSwap = false;
// Some swapping mechanisms such as DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL unbind the current render
// target. Mark it dirty.
mRenderer->markRenderTargetStateDirty();
if (result == DXGI_ERROR_DEVICE_REMOVED)
{
ID3D11Device *device = mRenderer->getDevice();
HRESULT removedReason = device->GetDeviceRemovedReason();
UNUSED_TRACE_VARIABLE(removedReason);
ERR("Present failed: the D3D11 device was removed: 0x%08X", removedReason);
......@@ -677,24 +821,22 @@ EGLint SwapChain11::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
ERR("Present failed with error code 0x%08X", result);
}
mRenderer->onSwap();
return EGL_SUCCESS;
}
ID3D11Texture2D *SwapChain11::getOffscreenTexture()
{
return mOffscreenTexture;
return mNeedsOffscreenTexture ? mOffscreenTexture : mBackBufferTexture;
}
ID3D11RenderTargetView *SwapChain11::getRenderTarget()
{
return mOffscreenRTView;
return mNeedsOffscreenTexture ? mOffscreenRTView : mBackBufferRTView;
}
ID3D11ShaderResourceView *SwapChain11::getRenderTargetShaderResource()
{
return mOffscreenSRView;
return mNeedsOffscreenTexture ? mOffscreenSRView : mBackBufferSRView;
}
ID3D11DepthStencilView *SwapChain11::getDepthStencil()
......
......@@ -20,8 +20,12 @@ class Renderer11;
class SwapChain11 : public SwapChainD3D
{
public:
SwapChain11(Renderer11 *renderer, NativeWindow nativeWindow, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat);
SwapChain11(Renderer11 *renderer,
NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation);
virtual ~SwapChain11();
EGLint resize(EGLint backbufferWidth, EGLint backbufferHeight);
......@@ -47,24 +51,36 @@ class SwapChain11 : public SwapChainD3D
private:
void release();
void initPassThroughResources();
void releaseOffscreenTexture();
EGLint resetOffscreenTexture(int backbufferWidth, int backbufferHeight);
void releaseOffscreenColorBuffer();
void releaseOffscreenDepthBuffer();
EGLint resetOffscreenBuffers(int backbufferWidth, int backbufferHeight);
EGLint resetOffscreenColorBuffer(int backbufferWidth, int backbufferHeight);
EGLint resetOffscreenDepthBuffer(int backbufferWidth, int backbufferHeight);
DXGI_FORMAT getSwapChainNativeFormat() const;
EGLint copyOffscreenToBackbuffer(EGLint x, EGLint y, EGLint width, EGLint height);
EGLint present(EGLint x, EGLint y, EGLint width, EGLint height);
Renderer11 *mRenderer;
EGLint mHeight;
EGLint mWidth;
const EGLint mOrientation;
bool mAppCreatedShareHandle;
unsigned int mSwapInterval;
bool mPassThroughResourcesInit;
bool mFirstSwap;
DXGISwapChain *mSwapChain;
IDXGISwapChain1 *mSwapChain1;
IDXGIKeyedMutex *mKeyedMutex;
ID3D11Texture2D *mBackBufferTexture;
ID3D11RenderTargetView *mBackBufferRTView;
ID3D11ShaderResourceView *mBackBufferSRView;
const bool mNeedsOffscreenTexture;
ID3D11Texture2D *mOffscreenTexture;
ID3D11RenderTargetView *mOffscreenRTView;
ID3D11ShaderResourceView *mOffscreenSRView;
......@@ -78,6 +94,7 @@ class SwapChain11 : public SwapChainD3D
ID3D11InputLayout *mPassThroughIL;
ID3D11VertexShader *mPassThroughVS;
ID3D11PixelShader *mPassThroughPS;
ID3D11RasterizerState *mPassThroughRS;
SurfaceRenderTarget11 mColorRenderTarget;
SurfaceRenderTarget11 mDepthStencilRenderTarget;
......
......@@ -59,7 +59,8 @@ HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
swapChainDesc.BufferUsage =
DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER;
swapChainDesc.BufferCount = 1;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
......@@ -84,7 +85,8 @@ HRESULT NativeWindow::createSwapChain(ID3D11Device* device, DXGIFactory* factory
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
swapChainDesc.BufferUsage =
DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_BACK_BUFFER;
swapChainDesc.Flags = 0;
swapChainDesc.OutputWindow = mWindow;
swapChainDesc.SampleDesc.Count = 1;
......
......@@ -652,9 +652,14 @@ gl::Error Renderer9::finish()
return gl::Error(GL_NO_ERROR);
}
SwapChainD3D *Renderer9::createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat)
{
return new SwapChain9(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat);
SwapChainD3D *Renderer9::createSwapChain(NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation)
{
return new SwapChain9(this, nativeWindow, shareHandle, backBufferFormat, depthBufferFormat,
orientation);
}
void *Renderer9::getD3DDevice()
......
......@@ -79,7 +79,11 @@ class Renderer9 : public RendererD3D
gl::Error flush() override;
gl::Error finish() override;
virtual SwapChainD3D *createSwapChain(NativeWindow nativeWindow, HANDLE shareHandle, GLenum backBufferFormat, GLenum depthBufferFormat);
SwapChainD3D *createSwapChain(NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation) override;
gl::Error allocateEventQuery(IDirect3DQuery9 **outQuery);
void freeEventQuery(IDirect3DQuery9* query);
......
......@@ -15,21 +15,27 @@
namespace rx
{
SwapChain9::SwapChain9(Renderer9 *renderer, NativeWindow nativeWindow, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat)
SwapChain9::SwapChain9(Renderer9 *renderer,
NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation)
: SwapChainD3D(nativeWindow, shareHandle, backBufferFormat, depthBufferFormat),
mRenderer(renderer),
mWidth(-1),
mHeight(-1),
mOrientation(orientation),
mSwapInterval(-1),
mSwapChain(nullptr),
mBackBuffer(nullptr),
mRenderTarget(nullptr),
mDepthStencil(nullptr),
mOffscreenTexture(nullptr),
mColorRenderTarget(this, false),
mDepthStencilRenderTarget(this, true)
{
mSwapChain = NULL;
mBackBuffer = NULL;
mDepthStencil = NULL;
mRenderTarget = NULL;
mOffscreenTexture = NULL;
mWidth = -1;
mHeight = -1;
mSwapInterval = -1;
ASSERT(mOrientation == 0);
}
SwapChain9::~SwapChain9()
......
......@@ -20,8 +20,12 @@ class Renderer9;
class SwapChain9 : public SwapChainD3D
{
public:
SwapChain9(Renderer9 *renderer, NativeWindow nativeWindow, HANDLE shareHandle,
GLenum backBufferFormat, GLenum depthBufferFormat);
SwapChain9(Renderer9 *renderer,
NativeWindow nativeWindow,
HANDLE shareHandle,
GLenum backBufferFormat,
GLenum depthBufferFormat,
EGLint orientation);
virtual ~SwapChain9();
EGLint resize(EGLint backbufferWidth, EGLint backbufferHeight);
......@@ -47,6 +51,7 @@ class SwapChain9 : public SwapChainD3D
Renderer9 *mRenderer;
EGLint mHeight;
EGLint mWidth;
EGLint mOrientation;
EGLint mSwapInterval;
IDirect3DSwapChain9 *mSwapChain;
......
......@@ -372,6 +372,13 @@ Error ValidateCreateWindowSurface(Display *display, Config *config, EGLNativeWin
}
break;
case EGL_SURFACE_ORIENTATION_ANGLE:
if (!displayExtensions.surfaceOrientation)
{
return Error(EGL_BAD_ATTRIBUTE, "EGL_ANGLE_surface_orientation is not enabled.");
}
break;
case EGL_VG_COLORSPACE:
return Error(EGL_BAD_MATCH);
......
......@@ -437,6 +437,16 @@ EGLBoolean EGLAPIENTRY QuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint a
}
*value = eglSurface->flexibleSurfaceCompatibilityRequested();
break;
case EGL_SURFACE_ORIENTATION_ANGLE:
if (!display->getExtensions().surfaceOrientation)
{
SetGlobalError(Error(EGL_BAD_ATTRIBUTE,
"EGL_SURFACE_ORIENTATION_ANGLE cannot be queried without "
"EGL_ANGLE_surface_orientation support."));
return EGL_FALSE;
}
*value = eglSurface->getOrientation();
break;
default:
SetGlobalError(Error(EGL_BAD_ATTRIBUTE));
return EGL_FALSE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment