You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2998 lines
108 KiB
C++
2998 lines
108 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
// Description : Implementation of the type CDevice and the functions to
|
|
// initialize OpenGL contexts and detect hardware
|
|
// capabilities
|
|
|
|
#include "RenderDll_precompiled.h"
|
|
|
|
#include "GLDevice.hpp"
|
|
#include "GLResource.hpp"
|
|
#include <Common/RenderCapabilities.h>
|
|
#include <SFunctor.h>
|
|
|
|
#include <AzCore/std/smart_ptr/make_shared.h>
|
|
#include <AzCore/Module/DynamicModuleHandle.h>
|
|
|
|
#if defined(ANDROID)
|
|
#include <AzCore/Android/Utils.h>
|
|
#include <android/native_window.h>
|
|
#endif
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#undef AZ_RESTRICTED_SECTION
|
|
#define GLDEVICE_CPP_SECTION_1 1
|
|
#define GLDEVICE_CPP_SECTION_2 2
|
|
#define GLDEVICE_CPP_SECTION_3 3
|
|
#define GLDEVICE_CPP_SECTION_4 4
|
|
#define GLDEVICE_CPP_SECTION_5 5
|
|
#define GLDEVICE_CPP_SECTION_6 6
|
|
#define GLDEVICE_CPP_SECTION_7 7
|
|
#define GLDEVICE_CPP_SECTION_8 8
|
|
#define GLDEVICE_CPP_SECTION_9 9
|
|
#define GLDEVICE_CPP_SECTION_10 10
|
|
#endif
|
|
|
|
#if defined(DEBUG) && !defined(MAC)
|
|
#define DXGL_DEBUG_CONTEXT 1
|
|
#else
|
|
#define DXGL_DEBUG_CONTEXT 0
|
|
#endif
|
|
|
|
#if defined(RELEASE)
|
|
#define DXGL_DEBUG_OUTPUT_VERBOSITY 0
|
|
#elif DXGL_SUPPORT_DEBUG_OUTPUT
|
|
#define DXGL_DEBUG_OUTPUT_VERBOSITY 1
|
|
#if defined(DEBUG)
|
|
#define DXGL_DEBUG_OUTPUT_SYNCHRONOUS 1
|
|
#endif //defined(DEBUG)
|
|
#endif
|
|
|
|
// This is the minimum number of uniform buffers required by the engine in order to run.
|
|
// Used when checking the capabilities of an adapter.
|
|
static const int MIN_UNIFORM_BUFFERS_REQUIRED = 8;
|
|
|
|
namespace NCryOpenGL
|
|
{
|
|
extern const DXGI_FORMAT DXGI_FORMAT_INVALID = DXGI_FORMAT_FORCE_UINT;
|
|
|
|
CDevice::WindowSizeList CDevice::m_windowSizes;
|
|
|
|
#if defined(AZ_PLATFORM_LINUX)
|
|
// Unfortunately this cannot be a member variable because it gets initialized in the static function CreateWindow,
|
|
// which only intends to return the handle of the created window. That function is called before CDevice is ever created,
|
|
// so we can't simply initialize the display in that class.
|
|
Display* s_defaultDisplay = nullptr;
|
|
#endif
|
|
|
|
#if DXGL_DEBUG_OUTPUT_VERBOSITY
|
|
#if defined(WIN32)
|
|
#define DXGL_DEBUG_CALLBACK_CONVENTION APIENTRY
|
|
#else
|
|
#define DXGL_DEBUG_CALLBACK_CONVENTION
|
|
#endif
|
|
void DXGL_DEBUG_CALLBACK_CONVENTION DebugCallback(GLenum eSource, GLenum eType, GLuint uId, GLenum eSeverity, GLsizei uLength, const GLchar* szMessage, void* pUserParam);
|
|
#endif //DXGL_DEBUG_OUTPUT_VERBOSITY
|
|
|
|
SVersion GetRequiredGLVersion()
|
|
{
|
|
#if defined(OPENGL_ES)
|
|
uint32 version = DXGLES_REQUIRED_VERSION;
|
|
#else
|
|
uint32 version = DXGL_REQUIRED_VERSION;
|
|
#endif
|
|
return SVersion(version);
|
|
}
|
|
|
|
#if defined(DXGL_USE_EGL)
|
|
SDisplayConnection::SDisplayConnection()
|
|
: m_display(EGL_NO_DISPLAY)
|
|
, m_surface(EGL_NO_SURFACE)
|
|
, m_config(nullptr)
|
|
, m_window(EGL_NULL_VALUE)
|
|
, m_dirtyFlag(false)
|
|
{}
|
|
|
|
SDisplayConnection::~SDisplayConnection()
|
|
{
|
|
if (m_display != EGL_NO_DISPLAY)
|
|
{
|
|
if (m_surface != EGL_NO_SURFACE)
|
|
{
|
|
eglDestroySurface(m_display, m_surface);
|
|
}
|
|
eglTerminate(m_display);
|
|
}
|
|
}
|
|
|
|
SDisplayConnection* SDisplayConnection::Create(const SPixelFormatSpec& pixelFormatSpec, const TNativeDisplay& defaultNativeDisplay)
|
|
{
|
|
SDisplayConnection* displayConnection = new SDisplayConnection();
|
|
if (!displayConnection->Init(pixelFormatSpec, defaultNativeDisplay))
|
|
{
|
|
delete displayConnection;
|
|
return nullptr;
|
|
}
|
|
|
|
return displayConnection;
|
|
}
|
|
|
|
bool SDisplayConnection::Init(const SPixelFormatSpec& kPixelFormatSpec, const TNativeDisplay& kDefaultNativeDisplay)
|
|
{
|
|
# if defined(OPENGL_ES)
|
|
EGLenum api = EGL_OPENGL_ES_API;
|
|
EGLint renderableType = EGL_OPENGL_ES3_BIT;
|
|
# else
|
|
EGLenum api = EGL_OPENGL_API;
|
|
EGLint renderableType = EGL_OPENGL_BIT;
|
|
# endif
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
|
|
|
m_window = kDefaultNativeDisplay->second;
|
|
|
|
// Desktop egl platforms only currently support OpenGL ES while mobile egl platforms support OpenGL and OpenGL ES.
|
|
// The api selection function is unavailable on desktop egl platforms
|
|
if (eglBindAPI != NULL)
|
|
{
|
|
if (eglBindAPI(api) == EGL_FALSE)
|
|
{
|
|
DXGL_ERROR("eglBindAPI failed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if !defined(AZ_PLATFORM_LINUX)
|
|
m_display = eglGetDisplay(kDefaultNativeDisplay->first);
|
|
#else
|
|
// If we use the EGL_DEFAULT_DISPLAY for the remaining code in this function when doing some of these operations
|
|
// many operations fail and we end up failing to initialize anything. Always use the default egl dislpay created
|
|
// from the XOpenDisplay results instead of whatever the kDefaultNativeDisplay has
|
|
m_display = eglGetDisplay(s_defaultDisplay);
|
|
#endif
|
|
|
|
if (m_display == EGL_NO_DISPLAY)
|
|
{
|
|
DXGL_ERROR("eglGetDisplay failed");
|
|
return false;
|
|
}
|
|
|
|
if (eglInitialize(m_display, NULL, NULL) != EGL_TRUE)
|
|
{
|
|
DXGL_ERROR("eglInitialize failed");
|
|
return false;
|
|
}
|
|
|
|
bool usePbuffer = m_window == EGL_NULL_VALUE;
|
|
EGLint aiAttributes[] =
|
|
{
|
|
EGL_RENDERABLE_TYPE, renderableType,
|
|
EGL_SURFACE_TYPE, usePbuffer ? EGL_PBUFFER_BIT : EGL_WINDOW_BIT,
|
|
EGL_RED_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->m_uRedBits),
|
|
EGL_GREEN_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->m_uGreenBits),
|
|
EGL_BLUE_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->m_uBlueBits),
|
|
EGL_ALPHA_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->m_uAlphaBits),
|
|
EGL_BUFFER_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->GetColorBits()),
|
|
EGL_DEPTH_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->m_uDepthBits),
|
|
EGL_STENCIL_SIZE, static_cast<EGLint>(kPixelFormatSpec.m_pLayout->m_uStencilBits),
|
|
EGL_SAMPLE_BUFFERS, static_cast<EGLint>(kPixelFormatSpec.m_uNumSamples > 1 ? 1 : 0),
|
|
EGL_SAMPLES, static_cast<EGLint>(kPixelFormatSpec.m_uNumSamples > 1 ? kPixelFormatSpec.m_uNumSamples : 0),
|
|
EGL_NONE
|
|
};
|
|
|
|
EGLint iFoundConfigs;
|
|
if (eglChooseConfig(m_display, aiAttributes, &m_config, 1, &iFoundConfigs) != EGL_TRUE || iFoundConfigs < 1)
|
|
{
|
|
DXGL_ERROR("eglChooseConfig failed");
|
|
return false;
|
|
}
|
|
|
|
#if !defined(AZ_PLATFORM_LINUX)
|
|
CreateSurface();
|
|
#else
|
|
// If we want to run in headless mode and not create a window, for optimal setup when rendering video from the server,
|
|
// then do not create an X11 window and only create an offscreen pixel buffer surface.
|
|
// r_GetScreenShot can then be triggered to capture a screenshot to user/screenshots/
|
|
ICVar* skipWindowCreationCvar = gEnv->pConsole->GetCVar("r_linuxSkipWindowCreation");
|
|
if (skipWindowCreationCvar && (skipWindowCreationCvar->GetIVal() > 0))
|
|
{
|
|
usePbuffer = true;
|
|
}
|
|
|
|
if (usePbuffer)
|
|
{
|
|
CreateSurface();
|
|
}
|
|
else
|
|
{
|
|
bool result = CreateX11Window();
|
|
if (!result)
|
|
{
|
|
DXGL_ERROR("Failed to create X11 window");
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (m_surface == EGL_NO_SURFACE)
|
|
{
|
|
DXGL_ERROR("Failed to create EGL surface");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SDisplayConnection::CreateSurface()
|
|
{
|
|
if (m_display == EGL_NO_DISPLAY || m_config == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_window)
|
|
{
|
|
m_surface = eglCreateWindowSurface(m_display, m_config, m_window, NULL);
|
|
}
|
|
else
|
|
{
|
|
const EGLint surfaceAttributes[] = {
|
|
EGL_WIDTH, 1,
|
|
EGL_HEIGHT, 1,
|
|
EGL_NONE
|
|
};
|
|
m_surface = eglCreatePbufferSurface(m_display, m_config, surfaceAttributes);
|
|
}
|
|
|
|
return m_surface != EGL_NO_SURFACE;
|
|
}
|
|
|
|
bool SDisplayConnection::DestroySurface()
|
|
{
|
|
if (m_display == EGL_NO_DISPLAY || m_surface == EGL_NO_SURFACE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
EGLBoolean result = eglDestroySurface(m_display, m_surface);
|
|
m_surface = EGL_NO_SURFACE;
|
|
return result == EGL_TRUE;
|
|
}
|
|
|
|
#if defined(AZ_PLATFORM_LINUX)
|
|
bool SDisplayConnection::CreateX11Window()
|
|
{
|
|
// We need to create an actual window and a window-renderable surface
|
|
EGLint visualID;
|
|
if (!eglGetConfigAttrib(m_display, m_config, EGL_NATIVE_VISUAL_ID, &visualID))
|
|
{
|
|
AZ_Assert(false, "Error: eglGetConfigAttrib failed - [0x%08x]", eglGetError());
|
|
return false;
|
|
}
|
|
|
|
// TODO Linux - Get these from somewhere else besides the cvars
|
|
ICVar* widthCVar = gEnv->pConsole->GetCVar("r_width");
|
|
ICVar* heightCVar = gEnv->pConsole->GetCVar("r_height");
|
|
int width = static_cast<int>(widthCVar->GetIVal());
|
|
int height = static_cast<int>(heightCVar->GetIVal());
|
|
const char* title = "Placeholder Title";
|
|
|
|
|
|
// Get the XVisualInfo config that matches the EGL config.
|
|
int numberXVisuals = 0;
|
|
XVisualInfo visualInfoTemplate;
|
|
visualInfoTemplate.visualid = visualID;
|
|
XVisualInfo* visualInfo = XGetVisualInfo(s_defaultDisplay, VisualIDMask, &visualInfoTemplate, &numberXVisuals);
|
|
if (numberXVisuals == 0)
|
|
{
|
|
AZ_Assert(false, "XGetVisualInfo failed to match egl configurations");
|
|
return false;
|
|
}
|
|
|
|
Window rootWindow = DefaultRootWindow(s_defaultDisplay);
|
|
Colormap colorMap = XCreateColormap(s_defaultDisplay, rootWindow, visualInfo->visual, AllocNone);
|
|
|
|
XSetWindowAttributes windowAttributes;
|
|
windowAttributes.colormap = colorMap;
|
|
windowAttributes.event_mask = ExposureMask | KeyPressMask;
|
|
|
|
Window applicationWindow = XCreateWindow(s_defaultDisplay, rootWindow, 0, 0, width, height, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWColormap | CWEventMask, &windowAttributes);
|
|
|
|
m_surface = eglCreateWindowSurface(m_display, m_config, applicationWindow, NULL);
|
|
if (!m_surface)
|
|
{
|
|
AZ_Assert(false, "Error: eglCreateWindowSurface failed - [0x%08x]", eglGetError());
|
|
return false;
|
|
}
|
|
|
|
// Map the window and set the name
|
|
XMapWindow(s_defaultDisplay, applicationWindow);
|
|
XStoreName(s_defaultDisplay, applicationWindow, title);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
void SDisplayConnection::SetWindow(EGLNativeWindowType window)
|
|
{
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
|
if (window != m_window)
|
|
{
|
|
if (m_window)
|
|
{
|
|
DestroySurface();
|
|
}
|
|
|
|
m_window = window;
|
|
|
|
if (window)
|
|
{
|
|
CreateSurface();
|
|
m_dirtyFlag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SDisplayConnection::MakeCurrent(const TRenderingContext context) const
|
|
{
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
|
EGLSurface surface = context ? m_surface : EGL_NO_SURFACE;
|
|
EGLBoolean res = eglMakeCurrent(m_display, surface, surface, context);
|
|
AZ_Warning("Rendering", res == EGL_TRUE, "eglMakeCurrent failed [0x%08x]", eglGetError());
|
|
return res == EGL_TRUE;
|
|
}
|
|
|
|
bool SDisplayConnection::SwapBuffers(const TRenderingContext context)
|
|
{
|
|
if (m_dirtyFlag)
|
|
{
|
|
// The surface was recreated so we need to make current again before doing the swap.
|
|
if (!MakeCurrent(context))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
|
m_dirtyFlag = false;
|
|
if (m_surface == EGL_NO_SURFACE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return eglSwapBuffers(m_display, m_surface) == EGL_TRUE;
|
|
}
|
|
|
|
#elif defined(DXGL_USE_WGL)
|
|
|
|
bool SetWindowPixelFormat(const TWindowContext& kWindowContext, const SPixelFormatSpec* pPixelFormatSpec)
|
|
{
|
|
int32 iPixelFormat(0);
|
|
PIXELFORMATDESCRIPTOR kPixDesc;
|
|
ZeroMemory(&kPixDesc, sizeof(PIXELFORMATDESCRIPTOR));
|
|
// Check for wgl pixel format extension availability
|
|
if (DXGL_WGL_EXTENSION_SUPPORTED(ARB_pixel_format) && pPixelFormatSpec)
|
|
{
|
|
int32 aiAttributes[128];
|
|
int32* pAttrCursor = aiAttributes;
|
|
|
|
*pAttrCursor++ = WGL_DRAW_TO_WINDOW_ARB;
|
|
*pAttrCursor++ = GL_TRUE;
|
|
*pAttrCursor++ = WGL_SUPPORT_OPENGL_ARB;
|
|
*pAttrCursor++ = GL_TRUE;
|
|
*pAttrCursor++ = WGL_DOUBLE_BUFFER_ARB;
|
|
*pAttrCursor++ = GL_TRUE;
|
|
*pAttrCursor++ = WGL_PIXEL_TYPE_ARB;
|
|
*pAttrCursor++ = WGL_TYPE_RGBA_ARB;
|
|
*pAttrCursor++ = WGL_RED_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uRedBits;
|
|
*pAttrCursor++ = WGL_GREEN_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uGreenBits;
|
|
*pAttrCursor++ = WGL_BLUE_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uBlueBits;
|
|
*pAttrCursor++ = WGL_ALPHA_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uAlphaBits;
|
|
*pAttrCursor++ = WGL_RED_SHIFT_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uRedShift;
|
|
*pAttrCursor++ = WGL_GREEN_SHIFT_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uGreenShift;
|
|
*pAttrCursor++ = WGL_BLUE_SHIFT_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uBlueShift;
|
|
*pAttrCursor++ = WGL_ALPHA_SHIFT_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uAlphaShift;
|
|
*pAttrCursor++ = WGL_COLOR_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->GetColorBits();
|
|
*pAttrCursor++ = WGL_DEPTH_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uDepthBits;
|
|
*pAttrCursor++ = WGL_STENCIL_BITS_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_pLayout->m_uStencilBits;
|
|
|
|
// Sample counts 0 and 1 do not require multisampling attribute
|
|
if (pPixelFormatSpec->m_uNumSamples > 1)
|
|
{
|
|
*pAttrCursor++ = WGL_SAMPLES_ARB;
|
|
*pAttrCursor++ = pPixelFormatSpec->m_uNumSamples;
|
|
}
|
|
|
|
// Request SRGB pixel format only when needed, skip this attribute otherwise (fix for pedantic drivers)
|
|
if (pPixelFormatSpec->m_bSRGB)
|
|
{
|
|
*pAttrCursor++ = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
|
|
*pAttrCursor++ = GL_TRUE;
|
|
}
|
|
|
|
// Mark end of the attribute list
|
|
*pAttrCursor = 0;
|
|
|
|
uint32 uNumFormats;
|
|
if (!DXGL_UNWRAPPED_FUNCTION(wglChoosePixelFormatARB)(kWindowContext, aiAttributes, NULL, 1, &iPixelFormat, &uNumFormats))
|
|
{
|
|
DXGL_ERROR("wglChoosePixelFormatARB failed");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kPixDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
kPixDesc.nVersion = 1;
|
|
kPixDesc.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
|
|
kPixDesc.iPixelType = PFD_TYPE_RGBA;
|
|
kPixDesc.iLayerType = PFD_MAIN_PLANE;
|
|
if (pPixelFormatSpec != NULL)
|
|
{
|
|
kPixDesc.cRedBits = pPixelFormatSpec->m_pLayout->m_uRedBits;
|
|
kPixDesc.cGreenBits = pPixelFormatSpec->m_pLayout->m_uGreenBits;
|
|
kPixDesc.cBlueBits = pPixelFormatSpec->m_pLayout->m_uBlueBits;
|
|
kPixDesc.cAlphaBits = pPixelFormatSpec->m_pLayout->m_uAlphaBits;
|
|
kPixDesc.cRedShift = pPixelFormatSpec->m_pLayout->m_uRedShift;
|
|
kPixDesc.cGreenShift = pPixelFormatSpec->m_pLayout->m_uGreenShift;
|
|
kPixDesc.cBlueShift = pPixelFormatSpec->m_pLayout->m_uBlueShift;
|
|
kPixDesc.cAlphaShift = pPixelFormatSpec->m_pLayout->m_uAlphaShift;
|
|
kPixDesc.cColorBits = pPixelFormatSpec->m_pLayout->GetColorBits();
|
|
kPixDesc.cDepthBits = pPixelFormatSpec->m_pLayout->m_uDepthBits;
|
|
kPixDesc.cStencilBits = pPixelFormatSpec->m_pLayout->m_uStencilBits;
|
|
if (pPixelFormatSpec->m_uNumSamples > 1 || pPixelFormatSpec->m_bSRGB)
|
|
{
|
|
DXGL_WARNING("wglChoosePixelFormatARB not available - multisampling and sRGB settings will be ignored");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kPixDesc.cColorBits = 32;
|
|
}
|
|
|
|
iPixelFormat = ChoosePixelFormat(kWindowContext, &kPixDesc);
|
|
if (iPixelFormat == 0)
|
|
{
|
|
DXGL_ERROR("ChoosePixelFormat failed");
|
|
return false;
|
|
}
|
|
|
|
if (pPixelFormatSpec == NULL &&
|
|
DescribePixelFormat(kWindowContext, iPixelFormat, sizeof(kPixDesc), &kPixDesc) == 0)
|
|
{
|
|
DXGL_ERROR("DescribePixelFormat failed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SetPixelFormat(kWindowContext, iPixelFormat, &kPixDesc))
|
|
{
|
|
DXGL_ERROR("SetPixelFormat failed");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
|
|
namespace NWin32Helper
|
|
{
|
|
enum
|
|
{
|
|
eWS_Windowed = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
|
eWS_FullScreen = WS_POPUP
|
|
};
|
|
|
|
bool GetDisplayRect(RECT* pRect, SOutput* pOutput)
|
|
{
|
|
DEVMODEA kCurrentMode;
|
|
if (EnumDisplaySettingsA(pOutput->m_strDeviceName.c_str(), ENUM_CURRENT_SETTINGS, &kCurrentMode) != TRUE)
|
|
{
|
|
DXGL_ERROR("Could not retrieve the current display settings for display %s", pOutput->m_strDeviceName.c_str());
|
|
return false;
|
|
}
|
|
|
|
DXGL_TODO("Check if scaling according to the GetDeviceCaps is required");
|
|
pRect->left = kCurrentMode.dmPosition.x;
|
|
pRect->top = kCurrentMode.dmPosition.y;
|
|
pRect->right = kCurrentMode.dmPosition.x + kCurrentMode.dmPelsWidth;
|
|
pRect->bottom = kCurrentMode.dmPosition.y + kCurrentMode.dmPelsHeight;
|
|
return true;
|
|
}
|
|
|
|
bool SetFullScreenContext(SOutput* pOutput, TNativeDisplay kNativeDisplay, DEVMODEA& kDevMode)
|
|
{
|
|
if (ChangeDisplaySettingsExA(pOutput->m_strDeviceName.c_str(), &kDevMode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
DXGL_ERROR("Could not change display settings");
|
|
return false;
|
|
}
|
|
|
|
RECT kFullScreenRect;
|
|
if (!GetDisplayRect(&kFullScreenRect, pOutput))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HWND kWindowHandle(WindowFromDC(kNativeDisplay));
|
|
DWORD uStyle(GetWindowLong(kWindowHandle, GWL_STYLE));
|
|
uStyle &= ~eWS_Windowed;
|
|
uStyle |= eWS_FullScreen;
|
|
if (SetWindowLong(kWindowHandle, GWL_STYLE, uStyle) == 0)
|
|
{
|
|
DXGL_WARNING("Could not set the window style");
|
|
}
|
|
if (SetWindowPos(kWindowHandle, NULL, kFullScreenRect.left, kFullScreenRect.top, kFullScreenRect.right - kFullScreenRect.left, kFullScreenRect.bottom - kFullScreenRect.top, SWP_NOCOPYBITS) != TRUE)
|
|
{
|
|
DXGL_WARNING("Could not set window posititon");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool UnsetFullScreenContext(SOutput* pOutput)
|
|
{
|
|
if (ChangeDisplaySettingsExA(pOutput->m_strDeviceName.c_str(), NULL, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
DXGL_ERROR("Could not restore original display settings");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ResizeWindowContext(TNativeDisplay kNativeDisplay, uint32 uWidth, uint32 uHeight)
|
|
{
|
|
HWND kWindowHandle(WindowFromDC(kNativeDisplay));
|
|
|
|
RECT kWindowRect;
|
|
if (!GetWindowRect(kWindowHandle, &kWindowRect))
|
|
{
|
|
DXGL_WARNING("Could not retrieve window rectangle - moving to origin");
|
|
kWindowRect.left = kWindowRect.right = kWindowRect.top = kWindowRect.bottom = 0;
|
|
}
|
|
kWindowRect.right = kWindowRect.left + GetSystemMetrics(SM_CXDLGFRAME) * 2 + uWidth;
|
|
kWindowRect.bottom = kWindowRect.top + GetSystemMetrics(SM_CXDLGFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION) + uHeight;
|
|
|
|
DWORD uStyle(GetWindowLong(kWindowHandle, GWL_STYLE));
|
|
uStyle &= ~eWS_FullScreen;
|
|
uStyle |= eWS_Windowed;
|
|
|
|
if (SetWindowLong(kWindowHandle, GWL_STYLE, uStyle) == 0)
|
|
{
|
|
DXGL_WARNING("Could not set the window style");
|
|
}
|
|
if (SetWindowPos(kWindowHandle, NULL, kWindowRect.left, kWindowRect.top, kWindowRect.right - kWindowRect.left, kWindowRect.bottom - kWindowRect.top, SWP_NOCOPYBITS) != TRUE)
|
|
{
|
|
DXGL_WARNING("Could not set window posititon");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
SOutput* GetWindowOutput(SAdapter* pAdapter, const TNativeDisplay& kNativeDisplay)
|
|
{
|
|
RECT kWindowRect;
|
|
HWND kWindowHandle(WindowFromDC(kNativeDisplay));
|
|
if (kWindowHandle == NULL || GetWindowRect(kWindowHandle, &kWindowRect) != TRUE)
|
|
{
|
|
DXGL_ERROR("Could not retrieve the device window rectangle");
|
|
return nullptr;
|
|
}
|
|
|
|
int32 iWindowCenterX((kWindowRect.left + kWindowRect.right) / 2);
|
|
int32 iWindowCenterY((kWindowRect.top + kWindowRect.bottom) / 2);
|
|
|
|
uint32 uOutput;
|
|
SOutput* pMinDistOutput(NULL);
|
|
uint32 uMinDistSqr;
|
|
for (uOutput = 0; uOutput < pAdapter->m_kOutputs.size(); ++uOutput)
|
|
{
|
|
SOutput* pOutput(pAdapter->m_kOutputs.at(uOutput));
|
|
RECT kDisplayRect;
|
|
if (!NWin32Helper::GetDisplayRect(&kDisplayRect, pOutput))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (kWindowRect.left >= kDisplayRect.left &&
|
|
kWindowRect.right <= kDisplayRect.right &&
|
|
kWindowRect.top >= kDisplayRect.top &&
|
|
kWindowRect.bottom <= kDisplayRect.bottom)
|
|
{
|
|
return pOutput; // Window completely inside the display rectangle
|
|
}
|
|
int32 iDistX(iWindowCenterX - (kDisplayRect.left + kDisplayRect.right) / 2);
|
|
int32 iDistY(iWindowCenterY - (kDisplayRect.top + kDisplayRect.bottom) / 2);
|
|
uint32 uCenterDistSqr(iDistX * iDistX + iDistY * iDistY);
|
|
if (uOutput == 0 || uCenterDistSqr < uMinDistSqr)
|
|
{
|
|
uMinDistSqr = uCenterDistSqr;
|
|
pMinDistOutput = pOutput;
|
|
}
|
|
}
|
|
|
|
return pMinDistOutput;
|
|
}
|
|
#endif // defined(WIN32)
|
|
|
|
#if defined(WIN32)
|
|
|
|
void DevModeToDisplayMode(SDisplayMode* pDisplayMode, const DEVMODEA& kDevMode)
|
|
{
|
|
pDisplayMode->m_uBitsPerPixel = kDevMode.dmBitsPerPel;
|
|
pDisplayMode->m_uWidth = kDevMode.dmPelsWidth;
|
|
pDisplayMode->m_uHeight = kDevMode.dmPelsHeight;
|
|
pDisplayMode->m_uFrequency = kDevMode.dmDisplayFrequency;
|
|
}
|
|
|
|
#endif // defined(WIN32)
|
|
|
|
struct SDummyWindow
|
|
{
|
|
TNativeDisplay m_kNativeDisplay;
|
|
#if defined(WIN32)
|
|
HWND m_kWndHandle;
|
|
ATOM m_kWndClassAtom;
|
|
|
|
static LRESULT CALLBACK DummyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
#endif //defined(WIN32)
|
|
|
|
SDummyWindow()
|
|
: m_kNativeDisplay()
|
|
#if defined(WIN32)
|
|
, m_kWndHandle(NULL)
|
|
, m_kWndClassAtom(0)
|
|
#endif //defined(WIN32)
|
|
{
|
|
}
|
|
|
|
bool Initialize(const SPixelFormatSpec* pPixelFormatSpec)
|
|
{
|
|
#if defined(DXGL_USE_EGL)
|
|
// No need to create a window because we are going to use an EGL pixel buffer surface.
|
|
m_kNativeDisplay = AZStd::make_shared<EGLNativePlatform>(EGL_DEFAULT_DISPLAY, EGL_NULL_VALUE);
|
|
#elif defined(WIN32)
|
|
WNDCLASSW kWndClass;
|
|
kWndClass.style = CS_HREDRAW | CS_VREDRAW;
|
|
kWndClass.lpfnWndProc = (WNDPROC)iSystem->GetRootWindowMessageHandler();
|
|
kWndClass.cbClsExtra = 0;
|
|
kWndClass.cbWndExtra = 0;
|
|
kWndClass.hInstance = NULL;
|
|
kWndClass.hIcon = LoadIconA(NULL, (LPCSTR)IDI_WINLOGO);
|
|
kWndClass.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
|
|
kWndClass.hbrBackground = NULL;
|
|
kWndClass.lpszMenuName = NULL;
|
|
kWndClass.lpszClassName = L"Dummy DXGL window class";
|
|
|
|
if ((m_kWndClassAtom = RegisterClassW(&kWndClass)) == NULL ||
|
|
(m_kWndHandle = CreateWindowW((LPCWSTR)MAKEINTATOM(m_kWndClassAtom), L"Dummy DXGL window", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, NULL, NULL)) == NULL ||
|
|
(m_kNativeDisplay = GetDC(m_kWndHandle)) == NULL)
|
|
{
|
|
DXGL_ERROR("Creation of the dummy DXGL window failed (%d)", GetLastError());
|
|
return false;
|
|
}
|
|
|
|
#if defined(DXGL_USE_WGL)
|
|
if (!SetWindowPixelFormat(m_kNativeDisplay, pPixelFormatSpec))
|
|
{
|
|
return false;
|
|
}
|
|
#endif //DXGL_USE_WGL
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
#if defined(WIN32)
|
|
if (m_kWndHandle != NULL)
|
|
{
|
|
DestroyWindow(m_kWndHandle);
|
|
}
|
|
if (m_kWndClassAtom != 0)
|
|
{
|
|
UnregisterClassW((LPCWSTR)MAKEINTATOM(m_kWndClassAtom), NULL);
|
|
}
|
|
#endif
|
|
}
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// CDevice implementation
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#if DXGL_FULL_EMULATION
|
|
uint32 CDevice::ms_uNumContextsPerDevice = 16;
|
|
#else
|
|
uint32 CDevice::ms_uNumContextsPerDevice = 1;
|
|
#endif
|
|
|
|
#if DXGL_EXTENSION_LOADER && defined(AZ_PLATFORM_LINUX)
|
|
// Linux needs an early call to LoadEarlyGLEntryPoints, so we need to forward declare it.
|
|
bool LoadEarlyGLEntryPoints();
|
|
#endif // DXGL_EXTENSION_LOADER
|
|
|
|
CDevice* CDevice::ms_pCurrentDevice = NULL;
|
|
|
|
CDevice::CDevice(SAdapter* pAdapter, const SFeatureSpec& kFeatureSpec, const SPixelFormatSpec& kPixelFormatSpec)
|
|
: m_spAdapter(pAdapter)
|
|
, m_kFeatureSpec(kFeatureSpec)
|
|
, m_kPixelFormatSpec(kPixelFormatSpec)
|
|
, m_kContextFenceIssued(false)
|
|
, m_texturesStreamingFunctorId(0)
|
|
{
|
|
if (ms_pCurrentDevice == NULL)
|
|
{
|
|
ms_pCurrentDevice = this;
|
|
}
|
|
m_pCurrentContextTLS = CreateTLS();
|
|
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
|
|
}
|
|
|
|
CDevice::~CDevice()
|
|
{
|
|
Shutdown();
|
|
DestroyTLS(m_pCurrentContextTLS);
|
|
if (ms_pCurrentDevice == this)
|
|
{
|
|
ms_pCurrentDevice = NULL;
|
|
}
|
|
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
|
|
}
|
|
|
|
#if !defined(WIN32)
|
|
bool CDevice::CreateWindow(const char* title, uint32 width, uint32 height, bool fullscreen, HWND * handle)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_1
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(ANDROID)
|
|
// Windows is already created by the Native Activity. We just return a pointer to the ANativeWindow.
|
|
ANativeWindow* nativeWindow = AZ::Android::Utils::GetWindow();
|
|
*handle = reinterpret_cast<HWND>(nativeWindow);
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
// Get the default display and root window handles.
|
|
// We are currently only rendering to a pixel buffer and not yet creating an actual window via X11
|
|
Display* defaultDisplay = XOpenDisplay(NULL);
|
|
AZ_Assert(defaultDisplay, "XOpenDisplay failed");
|
|
|
|
Window rootWindow = DefaultRootWindow(defaultDisplay);
|
|
|
|
#if DXGL_EXTENSION_LOADER
|
|
if (!LoadEarlyGLEntryPoints())
|
|
{
|
|
return false;
|
|
}
|
|
#endif // DXGL_EXTENSION_LOADER
|
|
|
|
*handle = reinterpret_cast<HWND>(rootWindow);
|
|
s_defaultDisplay = defaultDisplay;
|
|
#else
|
|
#error "Not implemented for this platform"
|
|
#endif
|
|
InitWindow(*handle, width, height);
|
|
m_windowSizes[handle] = std::make_pair(width, height);
|
|
return true;
|
|
}
|
|
|
|
void CDevice::DestroyWindow(HWND handle)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_2
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(ANDROID)
|
|
// Nothing to do since the window is destroyed by the OS when the Native Activity is destroyed
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
AZ_Assert(0, "TODO Linux OpenGL");
|
|
#else
|
|
#error "Not implemented for this platform"
|
|
#endif
|
|
m_windowSizes.erase(handle);
|
|
}
|
|
|
|
void CDevice::InitWindow(HWND handle, uint32 width, uint32 height)
|
|
{
|
|
#if defined(ANDROID)
|
|
ANativeWindow* nativeWindow = reinterpret_cast<ANativeWindow*>(handle);
|
|
// We need to set the windows size to match the engine width and height. The Android compositor will upscale it to fullscreen.
|
|
ANativeWindow_setBuffersGeometry(nativeWindow, width, height, WINDOW_FORMAT_RGBX_8888); // discard alpha
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
// TODO Linux - What needs done here?
|
|
//AZ_Assert(0, "TODO Linux OpenGL");
|
|
#endif
|
|
}
|
|
#endif // !defined(WIN32)
|
|
|
|
void CDevice::OnApplicationWindowCreated()
|
|
{
|
|
#if defined(ANDROID)
|
|
uint32 width, height;
|
|
if (m_windowSizes.empty())
|
|
{
|
|
AZ_Error("OpenGL", false, "Could not find window size. Using backbuffer size.");
|
|
width = gcpRendD3D->GetBackbufferWidth();
|
|
height = gcpRendD3D->GetBackbufferHeight();
|
|
}
|
|
else
|
|
{
|
|
// Android only uses one screen. Just use the first one in the list.
|
|
std::pair<uint32, uint32> windowSize = m_windowSizes.begin()->second;
|
|
width = windowSize.first;
|
|
height = windowSize.second;
|
|
}
|
|
|
|
HWND window = reinterpret_cast<HWND>(AZ::Android::Utils::GetWindow());
|
|
CDevice::InitWindow(window, width, height);
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
AZ_Assert(0, "TODO Linux OpenGL");
|
|
#endif
|
|
}
|
|
|
|
void CDevice::OnApplicationWindowRedrawNeeded()
|
|
{
|
|
#if defined(ANDROID)
|
|
if (gEnv && gEnv->pConsole && gEnv->pRenderer)
|
|
{
|
|
ICVar* widthCVar = gEnv->pConsole->GetCVar("r_width");
|
|
ICVar* heightCVar = gEnv->pConsole->GetCVar("r_height");
|
|
|
|
int width, height;
|
|
if (AZ::Android::Utils::GetWindowSize(width, height))
|
|
{
|
|
gcpRendD3D->GetClampedWindowSize(width, height);
|
|
|
|
widthCVar->Set(width);
|
|
heightCVar->Set(height);
|
|
|
|
// We need to wait for the render thread to finish before we set the new dimensions.
|
|
// Since Android has a separate render thread, it'll be in the middle of rendering the scene when this function is called.
|
|
if (!gRenDev->m_pRT->IsRenderThread(true))
|
|
{
|
|
gEnv->pRenderer->GetRenderThread()->WaitFlushFinishedCond();
|
|
}
|
|
|
|
gcpRendD3D->SetWidth(widthCVar->GetIVal());
|
|
gcpRendD3D->SetHeight(heightCVar->GetIVal());
|
|
|
|
InitWindow(AZ::Android::Utils::GetWindow(), width, height);
|
|
DetectOutputs(*m_spAdapter, m_spAdapter->m_kOutputs);
|
|
}
|
|
}
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
AZ_Assert(0, "TODO Linux OpenGL");
|
|
#endif
|
|
}
|
|
|
|
void CDevice::Configure(uint32 uNumSharedContexts)
|
|
{
|
|
ms_uNumContextsPerDevice = min((uint32)MAX_NUM_CONTEXT_PER_DEVICE, 1 + uNumSharedContexts);
|
|
}
|
|
|
|
|
|
static void OnChangeTexturesStreaming(ICVar* pCVar, CDevice* device)
|
|
{
|
|
int32 newVal = pCVar->GetIVal();
|
|
if (newVal > 0 && !device->IsFeatureSupported(NCryOpenGL::eF_CopyImage))
|
|
{
|
|
AZ_Warning("Rendering", false, "Disabling Textures Streaming because is not supported on this device.");
|
|
newVal = 0;
|
|
}
|
|
|
|
pCVar->Set(newVal);
|
|
}
|
|
|
|
bool CDevice::Initialize(const TNativeDisplay& kDefaultNativeDisplay)
|
|
{
|
|
if (kDefaultNativeDisplay == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_kDefaultNativeDisplay = kDefaultNativeDisplay;
|
|
}
|
|
|
|
std::vector<TRenderingContext> kRenderingContexts;
|
|
if (!CreateRenderingContexts(m_kDefaultWindowContext, kRenderingContexts, m_kFeatureSpec, m_kPixelFormatSpec, m_kDefaultNativeDisplay))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_kContexts.reserve(kRenderingContexts.size());
|
|
uint32 uContext;
|
|
for (uContext = 0; uContext < kRenderingContexts.size(); ++uContext)
|
|
{
|
|
const TRenderingContext& kRenderingContext(kRenderingContexts.at(uContext));
|
|
TWindowContext windowContext = m_kDefaultWindowContext;
|
|
CContext::ContextType type = uContext == 0 ? CContext::RenderingType : CContext::ResourceType;
|
|
#if defined(DXGL_USE_EGL)
|
|
// We use the window's surface for the context that will do the actual rendering and 1x1 PBuffer surfaces for the loading threads.
|
|
if (type == CContext::ResourceType)
|
|
{
|
|
windowContext.reset(SDisplayConnection::Create(m_kPixelFormatSpec, AZStd::make_shared<EGLNativePlatform>(EGL_DEFAULT_DISPLAY, EGL_NULL_VALUE)));
|
|
}
|
|
#endif // ANDROID
|
|
|
|
CContext* pContext(new CContext(this, kRenderingContext, windowContext, uContext, type));
|
|
MakeCurrent(windowContext, kRenderingContext);
|
|
|
|
if (uContext == 0)
|
|
{
|
|
InitializeResourceUnitPartitions();
|
|
}
|
|
|
|
if (!pContext->Initialize())
|
|
{
|
|
delete pContext;
|
|
return false;
|
|
}
|
|
|
|
m_kFreeContexts[pContext->GetType()].Push(*pContext);
|
|
m_kContexts.push_back(pContext);
|
|
}
|
|
|
|
MakeCurrent(m_kDefaultWindowContext, NULL);
|
|
|
|
// Check for textures streaming support
|
|
if (ICVar* cVar = gEnv->pConsole->GetCVar("r_texturesStreaming"))
|
|
{
|
|
SFunctor onChange;
|
|
onChange.Set(OnChangeTexturesStreaming, cVar, this);
|
|
m_texturesStreamingFunctorId = cVar->AddOnChangeFunctor(onChange);
|
|
onChange.Call();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CDevice::Shutdown()
|
|
{
|
|
MakeCurrent(m_kDefaultWindowContext, NULL);
|
|
|
|
TContexts::const_iterator kContextIter = m_kContexts.begin();
|
|
const TContexts::const_iterator kContextEnd = m_kContexts.end();
|
|
while (kContextIter != kContextEnd)
|
|
{
|
|
TRenderingContext kRenderingContext((*kContextIter)->GetRenderingContext());
|
|
delete *kContextIter;
|
|
// Delete context after all resources have been released. Avoids memory
|
|
// leaks an crashes with non-nvidia drivers
|
|
#if defined(DXGL_USE_EGL)
|
|
eglDestroyContext(m_kDefaultWindowContext->GetDisplay(), kRenderingContext);
|
|
#elif defined(DXGL_USE_WGL)
|
|
wglDeleteContext(kRenderingContext);
|
|
#else
|
|
#error "Not supported on this platform"
|
|
#endif
|
|
++kContextIter;
|
|
}
|
|
m_kContexts.clear();
|
|
|
|
if (m_kDefaultWindowContext != NULL)
|
|
{
|
|
ReleaseWindowContext(m_kDefaultWindowContext);
|
|
}
|
|
|
|
if (ICVar* cVar = gEnv->pConsole->GetCVar("r_texturesStreaming"))
|
|
{
|
|
cVar->RemoveOnChangeFunctor(m_texturesStreamingFunctorId);
|
|
}
|
|
}
|
|
|
|
bool CDevice::Present(const TWindowContext& kTargetWindowContext)
|
|
{
|
|
#if defined(DXGL_USE_EGL)
|
|
CContext* currentContext = GetCurrentContext();
|
|
return kTargetWindowContext->SwapBuffers(currentContext ? currentContext->GetRenderingContext() : nullptr);
|
|
#elif defined(WIN32)
|
|
return SwapBuffers(kTargetWindowContext) == TRUE;
|
|
#else
|
|
DXGL_NOT_IMPLEMENTED;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
CContext* CDevice::ReserveContext()
|
|
{
|
|
CContext* pCurrentContext(static_cast<CContext*>(GetTLSValue(m_pCurrentContextTLS)));
|
|
CContext* pReservedContext(NULL);
|
|
if (pCurrentContext != NULL)
|
|
{
|
|
pReservedContext = pCurrentContext->GetReservedContext();
|
|
}
|
|
|
|
if (pReservedContext == NULL)
|
|
{
|
|
pReservedContext = AllocateContext();
|
|
if (pReservedContext == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (pCurrentContext == NULL)
|
|
{
|
|
pCurrentContext = pReservedContext;
|
|
SetCurrentContext(pCurrentContext);
|
|
}
|
|
|
|
pReservedContext->IncReservationCount();
|
|
pCurrentContext->SetReservedContext(pReservedContext);
|
|
|
|
return pCurrentContext;
|
|
}
|
|
|
|
void CDevice::ReleaseContext()
|
|
{
|
|
CContext* pCurrentContext(GetCurrentContext());
|
|
assert(pCurrentContext != NULL);
|
|
CContext* pReservedContext(pCurrentContext->GetReservedContext());
|
|
assert(pReservedContext != NULL);
|
|
|
|
if (pReservedContext->DecReservationCount() == 0)
|
|
{
|
|
if (pCurrentContext == pReservedContext)
|
|
{
|
|
SetCurrentContext(NULL);
|
|
}
|
|
pCurrentContext->SetReservedContext(NULL);
|
|
FreeContext(pReservedContext);
|
|
}
|
|
}
|
|
|
|
CContext* CDevice::AllocateContext(CContext::ContextType type /*= CContext::ResourceType*/)
|
|
{
|
|
CContext* pContext(static_cast<CContext*>(m_kFreeContexts[type].Pop()));
|
|
if (pContext == NULL)
|
|
{
|
|
DXGL_ERROR("CDevice::AllocateContext failed - no free context available. Please increase the number of contexts at initialization time (currently %d).", (int)m_kContexts.size());
|
|
}
|
|
return pContext;
|
|
}
|
|
|
|
void CDevice::FreeContext(CContext* pContext)
|
|
{
|
|
m_kFreeContexts[pContext->GetType()].Push(*pContext);
|
|
}
|
|
|
|
void CDevice::BindContext(CContext* pContext)
|
|
{
|
|
CContext* pCurrentContext(GetCurrentContext());
|
|
if (pCurrentContext != NULL)
|
|
{
|
|
pContext->SetReservedContext(pCurrentContext->GetReservedContext());
|
|
}
|
|
|
|
SetCurrentContext(pContext);
|
|
}
|
|
|
|
void CDevice::UnbindContext(CContext* pContext)
|
|
{
|
|
CContext* pCurrentContext(static_cast<CContext*>(GetTLSValue(m_pCurrentContextTLS)));
|
|
assert(pCurrentContext != NULL);
|
|
SetCurrentContext(pCurrentContext->GetReservedContext());
|
|
}
|
|
|
|
void CDevice::SetCurrentContext(CContext* pContext)
|
|
{
|
|
CContext* pPreviousContext(static_cast<CContext*>(GetTLSValue(m_pCurrentContextTLS)));
|
|
|
|
bool bSuccess = false;
|
|
if (pContext != NULL)
|
|
{
|
|
bSuccess = MakeCurrent(pContext->GetWindowContext(), pContext->GetRenderingContext());
|
|
}
|
|
else if (pPreviousContext != NULL)
|
|
{
|
|
bSuccess = MakeCurrent(pPreviousContext->GetWindowContext(), NULL);
|
|
}
|
|
|
|
SetTLSValue(m_pCurrentContextTLS, pContext);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
DXGL_ERROR("SetCurrentContext failed");
|
|
}
|
|
}
|
|
|
|
NCryOpenGL::CContext* CDevice::GetCurrentContext()
|
|
{
|
|
return static_cast<CContext*>(GetTLSValue(m_pCurrentContextTLS));
|
|
}
|
|
|
|
uint32 CDevice::GetMaxContextCount()
|
|
{
|
|
return ms_uNumContextsPerDevice;
|
|
}
|
|
|
|
void CDevice::IssueFrameFences()
|
|
{
|
|
for (uint32 uContext = 0; uContext < m_kContexts.size(); ++uContext)
|
|
{
|
|
m_kContextFenceIssued.Set(uContext, true);
|
|
}
|
|
}
|
|
|
|
bool CDevice::CreateRenderingContexts(
|
|
TWindowContext& kWindowContext,
|
|
std::vector<TRenderingContext>& kRenderingContexts,
|
|
const SFeatureSpec& kFeatureSpec,
|
|
const SPixelFormatSpec& kPixelFormatSpec,
|
|
const TNativeDisplay& kNativeDisplay)
|
|
{
|
|
if (!CreateWindowContext(kWindowContext, kFeatureSpec, kPixelFormatSpec, kNativeDisplay))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if defined(DXGL_USE_EGL)
|
|
#if defined(AZ_PLATFORM_LINUX)
|
|
EGLint aiContextAttributes[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL_NONE};
|
|
#else
|
|
SVersion version = GetRequiredGLVersion();
|
|
EGLint aiContextAttributes[] =
|
|
{
|
|
EGL_CONTEXT_MAJOR_VERSION, static_cast<EGLint>(version.m_uMajorVersion),
|
|
EGL_CONTEXT_MINOR_VERSION, static_cast<EGLint>(version.m_uMinorVersion),
|
|
EGL_NONE
|
|
};
|
|
#endif
|
|
#elif defined(DXGL_USE_WGL)
|
|
int32 aiAttributes[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, kFeatureSpec.m_kVersion.m_uMajorVersion,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, kFeatureSpec.m_kVersion.m_uMinorVersion,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
#if DXGL_DEBUG_CONTEXT
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
|
|
#endif // DXGL_DEBUG_CONTEXT
|
|
0
|
|
};
|
|
#endif
|
|
|
|
kRenderingContexts.reserve(ms_uNumContextsPerDevice);
|
|
for (uint32 uContext = 0; uContext < ms_uNumContextsPerDevice; ++uContext)
|
|
{
|
|
#if defined(DXGL_USE_EGL)
|
|
TRenderingContext kRenderingContext(eglCreateContext(kWindowContext->GetDisplay(),
|
|
kWindowContext->GetConfig(),
|
|
uContext > 0 ? kRenderingContexts.at(0) : EGL_NO_CONTEXT,
|
|
aiContextAttributes));
|
|
#elif defined(DXGL_USE_WGL)
|
|
TRenderingContext kSharedRenderingContext(NULL);
|
|
if (uContext > 0)
|
|
{
|
|
kSharedRenderingContext = kRenderingContexts.at(0);
|
|
}
|
|
TRenderingContext kRenderingContext(DXGL_UNWRAPPED_FUNCTION(wglCreateContextAttribsARB)(kWindowContext, kSharedRenderingContext, aiAttributes));
|
|
#endif
|
|
|
|
if (!kRenderingContext)
|
|
{
|
|
DXGL_ERROR("Failed to create context %d", uContext);
|
|
return false;
|
|
}
|
|
|
|
kRenderingContexts.push_back(kRenderingContext);
|
|
}
|
|
|
|
#if DXGL_DEBUG_OUTPUT_VERBOSITY
|
|
if (kFeatureSpec.m_kFeatures.Get(eF_DebugOutput))
|
|
{
|
|
GLenum aeSeverityLevels[] =
|
|
{
|
|
GL_DEBUG_SEVERITY_HIGH,
|
|
GL_DEBUG_SEVERITY_MEDIUM,
|
|
GL_DEBUG_SEVERITY_LOW,
|
|
GL_DEBUG_SEVERITY_NOTIFICATION
|
|
};
|
|
|
|
for (uint32 uContext = 0; uContext < kRenderingContexts.size(); ++uContext)
|
|
{
|
|
MakeCurrent(kWindowContext, kRenderingContexts.at(uContext));
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
#if defined(DXGL_DEBUG_OUTPUT_SYNCHRONOUS)
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
|
#endif //defined(DXGL_DEBUG_OUTPUT_SYNCHRONOUS)
|
|
if (glDebugMessageCallback)
|
|
{
|
|
glDebugMessageCallback((GLDEBUGPROC)&DebugCallback, NULL);
|
|
}
|
|
#if defined(OPENGL_ES)
|
|
else
|
|
{
|
|
glDebugMessageCallbackKHR((GLDEBUGPROC)&DebugCallback, NULL);
|
|
}
|
|
#endif //defined(OPENGL_ES)
|
|
|
|
for (uint32 uSeverityLevel = 0; uSeverityLevel < DXGL_ARRAY_SIZE(aeSeverityLevels); ++uSeverityLevel)
|
|
{
|
|
if (glDebugMessageControl)
|
|
{
|
|
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, aeSeverityLevels[uSeverityLevel], 0, NULL, (uSeverityLevel <= DXGL_DEBUG_OUTPUT_VERBOSITY) ? GL_TRUE : GL_FALSE);
|
|
}
|
|
#if defined(OPENGL_ES)
|
|
else
|
|
{
|
|
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, aeSeverityLevels[uSeverityLevel], 0, NULL, (uSeverityLevel <= DXGL_DEBUG_OUTPUT_VERBOSITY) ? GL_TRUE : GL_FALSE);
|
|
}
|
|
#endif //defined(OPENGL_ES)
|
|
}
|
|
}
|
|
MakeCurrent(kWindowContext, NULL);
|
|
}
|
|
#endif // DXGL_DEBUG_OUTPUT_VERBOSITY
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CDevice::SetFullScreenState(const SFrameBufferSpec& kFrameBufferSpec, bool bFullScreen, SOutput* pOutput)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_3
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(WIN32)
|
|
if (bFullScreen)
|
|
{
|
|
if (pOutput == NULL)
|
|
{
|
|
pOutput = GetWindowOutput(m_spAdapter, m_kDefaultNativeDisplay);
|
|
}
|
|
if (pOutput == NULL)
|
|
{
|
|
DXGL_ERROR("Could not retrieve the display output corresponding to the window context");
|
|
return false;
|
|
}
|
|
|
|
if (pOutput != m_spFullScreenOutput)
|
|
{
|
|
DEVMODEA kReqDevMode;
|
|
memset(&kReqDevMode, 0, sizeof(kReqDevMode));
|
|
kReqDevMode.dmSize = sizeof(kReqDevMode);
|
|
kReqDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
|
|
kReqDevMode.dmPelsWidth = kFrameBufferSpec.m_uWidth;
|
|
kReqDevMode.dmPelsHeight = kFrameBufferSpec.m_uHeight;
|
|
kReqDevMode.dmBitsPerPel = kFrameBufferSpec.m_pLayout->GetPixelBits();
|
|
|
|
if (!NWin32Helper::SetFullScreenContext(pOutput, m_kDefaultNativeDisplay, kReqDevMode))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_spFullScreenOutput = pOutput;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_spFullScreenOutput != NULL)
|
|
{
|
|
if (!NWin32Helper::UnsetFullScreenContext(m_spFullScreenOutput))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_spFullScreenOutput = NULL;
|
|
}
|
|
}
|
|
return true;
|
|
#elif defined(ANDROID)
|
|
// Android is always full screen.
|
|
return true;
|
|
#else
|
|
DXGL_NOT_IMPLEMENTED;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool CDevice::ResizeTarget(const SDisplayMode& kTargetMode)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_4
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(WIN32)
|
|
if (kTargetMode.m_uBitsPerPixel != m_kPixelFormatSpec.m_pLayout->GetPixelBits())
|
|
{
|
|
DXGL_WARNING("ResizeTarget does not support changing the window pixel format");
|
|
return false;
|
|
}
|
|
|
|
if (m_spFullScreenOutput != NULL)
|
|
{
|
|
DEVMODEA kDevMode;
|
|
memset(&kDevMode, 0, sizeof(kDevMode));
|
|
kDevMode.dmSize = sizeof(kDevMode);
|
|
kDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
|
|
kDevMode.dmPelsWidth = kTargetMode.m_uWidth;
|
|
kDevMode.dmPelsHeight = kTargetMode.m_uHeight;
|
|
kDevMode.dmBitsPerPel = kTargetMode.m_uBitsPerPixel;
|
|
if (kTargetMode.m_uFrequency != 0)
|
|
{
|
|
kDevMode.dmFields |= DM_DISPLAYFREQUENCY;
|
|
kDevMode.dmDisplayFrequency = kTargetMode.m_uFrequency;
|
|
}
|
|
|
|
return NWin32Helper::SetFullScreenContext(m_spFullScreenOutput, m_kDefaultNativeDisplay, kDevMode);
|
|
}
|
|
else
|
|
{
|
|
return NWin32Helper::ResizeWindowContext(m_kDefaultNativeDisplay, kTargetMode.m_uWidth, kTargetMode.m_uHeight);
|
|
}
|
|
#elif defined(ANDROID)
|
|
DXGL_WARNING("ResizeTarget is not supported on this platform");
|
|
return false;
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
AZ_Assert(0, "TODO Linux OpenGL");
|
|
#else
|
|
#error "Not implemented on this platform"
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool CDevice::MakeCurrent(const TWindowContext& kWindowContext, TRenderingContext kRenderingContext)
|
|
{
|
|
#if defined(DXGL_USE_EGL)
|
|
return kWindowContext->MakeCurrent(kRenderingContext);
|
|
#elif defined(DXGL_USE_WGL)
|
|
return wglMakeCurrent(kRenderingContext == NULL ? NULL : kWindowContext, kRenderingContext) == TRUE;
|
|
#else
|
|
DXGL_NOT_IMPLEMENTED;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
DXGL_TODO("Move default partitions somewhere else/find a better way since it's not engine-related - possibly pass through DXGLInitialize");
|
|
|
|
#define _PARTITION_PRED(_First, _Count) { _First, _Count },
|
|
#define PARTITION_PRED(_Argument) _PARTITION_PRED _Argument
|
|
#define PARTITION(_Vertex, _Fragment, _Geometry, _TessControl, _TessEvaluation, _Compute) \
|
|
{ DXGL_SHADER_TYPE_MAP(PARTITION_PRED, _Vertex, _Fragment, _Geometry, _TessControl, _TessEvaluation, _Compute) },
|
|
|
|
const TPipelineResourceUnitPartitionBound g_akTextureUnitBounds[] =
|
|
{
|
|
// VERTEX FRAGMENT GEOMETRY TESSCTL TESSEVAL COMPUTE
|
|
PARTITION((0, 10), (0, 16), (0, 6), (0, 0), (0, 0), (0, 0)) // Graphics
|
|
PARTITION((0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 32)) // Compute
|
|
};
|
|
|
|
const TPipelineResourceUnitPartitionBound g_akUniformBufferUnitBounds[] =
|
|
{
|
|
// VERTEX FRAGMENT GEOMETRY TESSCTL TESSEVAL COMPUTE
|
|
PARTITION((0, 12), (0, 12), (0, 12), (0, 12), (0, 12), (0, 0)) // Graphics
|
|
PARTITION((0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 16)) // Compute
|
|
};
|
|
|
|
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
|
|
|
|
const TPipelineResourceUnitPartitionBound g_akStorageBufferUnitBounds[] =
|
|
{
|
|
// VERTEX FRAGMENT GEOMETRY TESSCTL TESSEVAL COMPUTE
|
|
PARTITION((14, 2), (16, 8), (0, 0), (0, 0), (0, 0), (0, 0)) // Graphics
|
|
PARTITION((0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (16, 8)) // Compute
|
|
};
|
|
|
|
#endif
|
|
|
|
#if DXGL_SUPPORT_SHADER_IMAGES
|
|
|
|
const TPipelineResourceUnitPartitionBound g_akImageUnitBounds[] =
|
|
{
|
|
// VERTEX FRAGMENT GEOMETRY TESSCTL TESSEVAL COMPUTE
|
|
PARTITION((0, 0), (0, 8), (0, 0), (0, 0), (0, 0), (0, 0)) // Graphics
|
|
PARTITION((0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 8)) // Compute
|
|
};
|
|
|
|
#endif
|
|
|
|
#undef PARTITION
|
|
#undef PARTITION_PRED
|
|
#undef _PARTITION_PRED
|
|
|
|
void CDevice::InitializeResourceUnitPartitions()
|
|
{
|
|
const SCapabilities& kCapabilities(m_spAdapter->m_kCapabilities);
|
|
|
|
PartitionResourceIndices(eRUT_Texture, g_akTextureUnitBounds, DXGL_ARRAY_SIZE(g_akTextureUnitBounds));
|
|
PartitionResourceIndices(eRUT_UniformBuffer, g_akUniformBufferUnitBounds, DXGL_ARRAY_SIZE(g_akUniformBufferUnitBounds));
|
|
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
|
|
PartitionResourceIndices(eRUT_StorageBuffer, g_akStorageBufferUnitBounds, DXGL_ARRAY_SIZE(g_akStorageBufferUnitBounds));
|
|
#endif
|
|
#if DXGL_SUPPORT_SHADER_IMAGES
|
|
if (m_spAdapter->m_kFeatures.Get(eF_ShaderImages))
|
|
{
|
|
PartitionResourceIndices(eRUT_Image, g_akImageUnitBounds, DXGL_ARRAY_SIZE(g_akImageUnitBounds));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool CDevice::IsFeatureSupported(EFeature feature) const
|
|
{
|
|
AZ_Assert(feature < eF_NUM, "Invalid EFeature %d", feature);
|
|
return m_kFeatureSpec.m_kFeatures.Get(feature);
|
|
}
|
|
|
|
const char* GetResourceUnitTypeName(EResourceUnitType eUnitType)
|
|
{
|
|
switch (eUnitType)
|
|
{
|
|
case eRUT_Texture:
|
|
return "Texture unit";
|
|
case eRUT_UniformBuffer:
|
|
return "Uniform buffer unit";
|
|
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
|
|
case eRUT_StorageBuffer:
|
|
return "Storage buffer unit";
|
|
#endif
|
|
#if DXGL_SUPPORT_SHADER_IMAGES
|
|
case eRUT_Image:
|
|
return "Image unit";
|
|
#endif
|
|
default:
|
|
assert(false);
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
bool TryDistributeResourceIndices(
|
|
SIndexPartition& kPartition,
|
|
const SResourceUnitCapabilities& kCapabilities,
|
|
const TPipelineResourceUnitPartitionBound& akStageBounds)
|
|
{
|
|
uint32 uTotBelowLimit = 0;
|
|
uint32 uTotAvailable = kCapabilities.m_aiMaxTotal;
|
|
|
|
uint32 uTotUsed = 0;
|
|
for (uint32 uStage = 0; uStage < eST_NUM; ++uStage)
|
|
{
|
|
kPartition.m_akStages[uStage].m_uCount = akStageBounds[uStage].m_uNumUnits;
|
|
uTotUsed += akStageBounds[uStage].m_uNumUnits;
|
|
|
|
if (kCapabilities.m_aiMaxPerStage[uStage] < kPartition.m_akStages[uStage].m_uCount)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uTotBelowLimit += kCapabilities.m_aiMaxPerStage[uStage] - kPartition.m_akStages[uStage].m_uCount;
|
|
}
|
|
|
|
if (uTotUsed > uTotAvailable)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while (uTotAvailable > uTotUsed && uTotBelowLimit > 0)
|
|
{
|
|
uint32 uTotRemaining = uTotAvailable - uTotUsed;
|
|
uint32 uTotAssigned = 0;
|
|
for (uint32 uStage = 0; uStage < eST_NUM; ++uStage)
|
|
{
|
|
uint32 uBelowLimit = kCapabilities.m_aiMaxPerStage[uStage] - kPartition.m_akStages[uStage].m_uCount;
|
|
if (uBelowLimit > 0)
|
|
{
|
|
uint32 uAssigned = min(uTotRemaining - uTotAssigned, max(1u, uTotRemaining * uBelowLimit / uTotBelowLimit));
|
|
kPartition.m_akStages[uStage].m_uCount += uAssigned;
|
|
uTotAssigned += uAssigned;
|
|
}
|
|
}
|
|
assert(uTotAssigned > 0 && uTotAssigned <= uTotRemaining);
|
|
uTotUsed += uTotAssigned;
|
|
}
|
|
|
|
for (uint32 uStage = 0, uFirstSlot = 0; uStage < eST_NUM; ++uStage)
|
|
{
|
|
kPartition.m_akStages[uStage].m_uFirstIn = akStageBounds[uStage].m_uFirstUnit;
|
|
kPartition.m_akStages[uStage].m_uFirstOut = uFirstSlot;
|
|
uFirstSlot += kPartition.m_akStages[uStage].m_uCount;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CDevice::PartitionResourceIndices(
|
|
EResourceUnitType eUnitType,
|
|
const TPipelineResourceUnitPartitionBound* akPartitionBounds,
|
|
uint32 uNumPartitions)
|
|
{
|
|
CDevice::TPartitions& kPartitions = m_kResourceUnitPartitions[eUnitType];
|
|
const SResourceUnitCapabilities& kCapabilities = m_spAdapter->m_kCapabilities.m_akResourceUnits[eUnitType];
|
|
|
|
kPartitions.reserve(uNumPartitions);
|
|
|
|
for (uint32 uPartition = 0; uPartition < uNumPartitions; ++uPartition)
|
|
{
|
|
const TPipelineResourceUnitPartitionBound& akStageBounds(akPartitionBounds[uPartition]);
|
|
|
|
SIndexPartition kPartition;
|
|
if (TryDistributeResourceIndices(kPartition, kCapabilities, akStageBounds))
|
|
{
|
|
kPartitions.push_back(kPartition);
|
|
}
|
|
else
|
|
{
|
|
DXGL_WARNING("%s partition %d is not supported by this configuration - it will not be used", GetResourceUnitTypeName(eUnitType), uPartition);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SDummyContext
|
|
{
|
|
SDummyWindow m_kDummyWindow;
|
|
TRenderingContext m_kRenderingContext;
|
|
#if defined(DXGL_USE_EGL)
|
|
TWindowContext m_kDisplayConnection;
|
|
#endif //DXGL_USE_EGL
|
|
bool m_kIsInitialized;
|
|
|
|
SDummyContext()
|
|
: m_kRenderingContext(NULL)
|
|
, m_kIsInitialized(false)
|
|
{
|
|
}
|
|
|
|
~SDummyContext()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
bool Initialize()
|
|
{
|
|
if (m_kIsInitialized)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!m_kDummyWindow.Initialize(NULL))
|
|
{
|
|
return false;
|
|
}
|
|
#if defined(DXGL_USE_EGL)
|
|
SPixelFormatSpec pixelFormat;
|
|
SUncompressedLayout layout;
|
|
ZeroMemory(&pixelFormat, sizeof(pixelFormat));
|
|
ZeroMemory(&layout, sizeof(layout));
|
|
pixelFormat.m_pLayout = &layout;
|
|
|
|
m_kDisplayConnection.reset(SDisplayConnection::Create(pixelFormat, m_kDummyWindow.m_kNativeDisplay));
|
|
if (!m_kDisplayConnection)
|
|
{
|
|
DXGL_ERROR("Creation of the dummy DXGL window failed");
|
|
return false;
|
|
}
|
|
|
|
#if defined(AZ_PLATFORM_LINUX)
|
|
SVersion version = GetRequiredGLVersion();
|
|
EGLint aiContextAttributes[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL_NONE};
|
|
#else
|
|
SVersion version = GetRequiredGLVersion();
|
|
EGLint aiContextAttributes[] = {
|
|
EGL_CONTEXT_MAJOR_VERSION, static_cast<EGLint>(version.m_uMajorVersion),
|
|
EGL_CONTEXT_MINOR_VERSION, static_cast<EGLint>(version.m_uMinorVersion),
|
|
EGL_NONE};
|
|
#endif
|
|
m_kRenderingContext = eglCreateContext(m_kDisplayConnection->GetDisplay(),
|
|
m_kDisplayConnection->GetConfig(),
|
|
EGL_NO_CONTEXT,
|
|
aiContextAttributes);
|
|
if (m_kRenderingContext == EGL_NO_CONTEXT)
|
|
{
|
|
DXGL_ERROR("Dummy DXGL context creation failed: [0x%08x]", eglGetError());
|
|
return false;
|
|
}
|
|
if (!m_kDisplayConnection->MakeCurrent(m_kRenderingContext))
|
|
{
|
|
DXGL_ERROR("Dummy DXGL context MakeCurrent failed: [0x%08x]", eglGetError());
|
|
return false;
|
|
}
|
|
#elif defined(DXGL_USE_WGL)
|
|
m_kRenderingContext = wglCreateContext(m_kDummyWindow.m_kNativeDisplay);
|
|
if (m_kRenderingContext == NULL ||
|
|
wglMakeCurrent(m_kDummyWindow.m_kNativeDisplay, m_kRenderingContext) != TRUE)
|
|
{
|
|
DXGL_ERROR("Dummy DXGL context creation failed");
|
|
return false;
|
|
}
|
|
#else
|
|
# error "Not Implemented"
|
|
#endif
|
|
m_kIsInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
if (!m_kIsInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if defined(DXGL_USE_EGL)
|
|
m_kDisplayConnection->MakeCurrent(nullptr);
|
|
if (m_kRenderingContext != NULL)
|
|
{
|
|
eglDestroyContext(m_kDisplayConnection->GetDisplay(), m_kRenderingContext);
|
|
}
|
|
#elif defined(DXGL_USE_WGL)
|
|
wglMakeCurrent(NULL, NULL);
|
|
if (m_kRenderingContext != 0)
|
|
{
|
|
wglDeleteContext(m_kRenderingContext);
|
|
}
|
|
#else
|
|
# error "Not Implemented"
|
|
#endif
|
|
m_kDummyWindow.Shutdown();
|
|
m_kIsInitialized = false;
|
|
}
|
|
};
|
|
|
|
bool FeatureLevelToFeatureSpec(SFeatureSpec& kFeatureSpec, D3D_FEATURE_LEVEL eFeatureLevel, NCryOpenGL::SAdapter* pGLAdapter)
|
|
{
|
|
#if DXGLES && !defined(DXGL_ES_SUBSET)
|
|
//LYTODO: seems like our ES version should line up differently with eFeatureLevel
|
|
if (eFeatureLevel == D3D_FEATURE_LEVEL_11_0)
|
|
{
|
|
uint32 adapterGLVersion = pGLAdapter->m_sVersion.ToUint();
|
|
if (adapterGLVersion >= DXGLES_VERSION_31)
|
|
{
|
|
kFeatureSpec.m_kVersion.m_uMajorVersion = 3;
|
|
kFeatureSpec.m_kVersion.m_uMinorVersion = 1;
|
|
}
|
|
else if (adapterGLVersion == DXGLES_VERSION_30)
|
|
{
|
|
kFeatureSpec.m_kVersion.m_uMajorVersion = 3;
|
|
kFeatureSpec.m_kVersion.m_uMinorVersion = 0;
|
|
}
|
|
else
|
|
{
|
|
DXGL_ERROR("Could not match feature level to openGL version. Feature level = %d, Adapter GL Version = %u", eFeatureLevel, adapterGLVersion);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DXGL_ERROR("Feature level not implemented on OpenGL ES");
|
|
return false;
|
|
}
|
|
#else
|
|
switch (eFeatureLevel)
|
|
{
|
|
case D3D_FEATURE_LEVEL_9_1:
|
|
case D3D_FEATURE_LEVEL_9_2:
|
|
case D3D_FEATURE_LEVEL_9_3:
|
|
kFeatureSpec.m_kVersion.m_uMajorVersion = 2;
|
|
kFeatureSpec.m_kVersion.m_uMinorVersion = 0;
|
|
break;
|
|
case D3D_FEATURE_LEVEL_10_0:
|
|
case D3D_FEATURE_LEVEL_10_1:
|
|
kFeatureSpec.m_kVersion.m_uMajorVersion = 3;
|
|
kFeatureSpec.m_kVersion.m_uMinorVersion = 3;
|
|
break;
|
|
case D3D_FEATURE_LEVEL_11_0:
|
|
kFeatureSpec.m_kVersion.m_uMajorVersion = 4;
|
|
kFeatureSpec.m_kVersion.m_uMinorVersion = 3;
|
|
break;
|
|
default:
|
|
DXGL_ERROR("Unknown feature level");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
kFeatureSpec.m_kFeatures.Set(eF_ComputeShader, eFeatureLevel >= D3D_FEATURE_LEVEL_10_0);
|
|
return true;
|
|
}
|
|
|
|
void GetStandardPixelFormatSpec(SPixelFormatSpec& kPixelFormatSpec)
|
|
{
|
|
kPixelFormatSpec.m_pLayout = GetGIFormatInfo(eGIF_R8G8B8A8_UNORM_SRGB)->m_pUncompressed;
|
|
kPixelFormatSpec.m_uNumSamples = 1;
|
|
kPixelFormatSpec.m_bSRGB = true;
|
|
}
|
|
|
|
bool SwapChainDescToFrameBufferSpec(SFrameBufferSpec& kFrameBufferSpec, const DXGI_SWAP_CHAIN_DESC& kSwapChainDesc)
|
|
{
|
|
EGIFormat eGIFormat(GetGIFormat(kSwapChainDesc.BufferDesc.Format));
|
|
if (eGIFormat == eGIF_NUM)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(eGIFormat));
|
|
|
|
kFrameBufferSpec.m_uWidth = kSwapChainDesc.BufferDesc.Width;
|
|
kFrameBufferSpec.m_uHeight = kSwapChainDesc.BufferDesc.Height;
|
|
kFrameBufferSpec.m_uNumSamples = kSwapChainDesc.SampleDesc.Count;
|
|
kFrameBufferSpec.m_bSRGB = pFormatInfo->m_pTexture->m_bSRGB;
|
|
kFrameBufferSpec.m_pLayout = pFormatInfo->m_pUncompressed;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetNativeDisplay(TNativeDisplay& kNativeDisplay, HWND kWindowHandle)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_5
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(WIN32)
|
|
HDC deviceContext = GetDC(kWindowHandle);
|
|
if (deviceContext == NULL)
|
|
{
|
|
DXGL_ERROR("Could not retrieve the DC of the swap chain output window");
|
|
return false;
|
|
}
|
|
# if defined(DXGL_USE_EGL)
|
|
HWND nativeWindow = WindowFromDC(deviceContext);
|
|
if (!nativeWindow)
|
|
{
|
|
DXGL_ERROR("Could not retrieve window from device context");
|
|
return false;
|
|
}
|
|
kNativeDisplay = AZStd::make_shared<EGLNativePlatform>(deviceContext, nativeWindow);
|
|
# else
|
|
kNativeDisplay = deviceContext;
|
|
# endif // defined(DXGL_USE_EGL)
|
|
return true;
|
|
#elif defined(ANDROID)
|
|
kNativeDisplay = AZStd::make_shared<EGLNativePlatform>(static_cast<EGLNativeDisplayType>(EGL_DEFAULT_DISPLAY), static_cast<EGLNativeWindowType>(kWindowHandle));
|
|
return true;
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
kNativeDisplay = AZStd::make_shared<EGLNativePlatform>(s_defaultDisplay, reinterpret_cast<EGLNativeWindowType>(kWindowHandle));
|
|
return true;
|
|
#else
|
|
#error "Not supported on this platform"
|
|
#endif
|
|
}
|
|
|
|
bool CreateWindowContext(
|
|
TWindowContext& kWindowContext,
|
|
const SFeatureSpec& kFeatureSpec,
|
|
const SPixelFormatSpec& kPixelFormatSpec,
|
|
const TNativeDisplay& kNativeDisplay)
|
|
{
|
|
#if defined(DXGL_USE_EGL)
|
|
kWindowContext.reset(SDisplayConnection::Create(kPixelFormatSpec, kNativeDisplay));
|
|
if (kWindowContext == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
#elif defined(WIN32)
|
|
kWindowContext = kNativeDisplay;
|
|
if (!SetWindowPixelFormat(kWindowContext, &kPixelFormatSpec))
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void ReleaseWindowContext(TWindowContext& kWindowContext)
|
|
{
|
|
#if defined(DXGL_USE_EGL)
|
|
kWindowContext = nullptr;
|
|
#endif //DXGL_USE_EGL
|
|
}
|
|
|
|
#if DXGL_SUPPORT_QUERY_INTERNAL_FORMAT_SUPPORT
|
|
|
|
bool QueryInternalFormatSupport(GLenum eTarget, GLenum eInternalFormat, GLenum eQueryName, uint32 uFlag, uint32& uMask)
|
|
{
|
|
if (DXGL_GL_EXTENSION_SUPPORTED(ARB_internalformat_query2))
|
|
{
|
|
GLint iSupported;
|
|
glGetInternalformativ(eTarget, eInternalFormat, eQueryName, 1, &iSupported);
|
|
bool bSupported;
|
|
switch (iSupported)
|
|
{
|
|
case GL_NONE:
|
|
bSupported = false;
|
|
break;
|
|
case GL_CAVEAT_SUPPORT:
|
|
DXGL_WARNING("Internal format supported but not optimal");
|
|
bSupported = true;
|
|
break;
|
|
case GL_FULL_SUPPORT:
|
|
case GL_TRUE:
|
|
bSupported = true;
|
|
break;
|
|
default:
|
|
DXGL_ERROR("Invalid parameter returned by internal format query");
|
|
return false;
|
|
}
|
|
uMask = bSupported ? uMask | uFlag : uMask & ~uFlag;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void QueryInternalFormatTexSupport(GLenum eTarget, GLenum eInternalFormat, uint32 uTexFlag, uint32& uMask)
|
|
{
|
|
if (QueryInternalFormatSupport(eTarget, eInternalFormat, GL_INTERNALFORMAT_SUPPORTED, uTexFlag, uMask) && (uMask & uTexFlag) != 0)
|
|
{
|
|
#if !defined(_RELEASE)
|
|
GLint iPreferred;
|
|
glGetInternalformativ(eTarget, eInternalFormat, GL_INTERNALFORMAT_PREFERRED, 1, &iPreferred);
|
|
if (iPreferred != eInternalFormat)
|
|
{
|
|
DXGL_WARNING("Internal format supported but not preferred");
|
|
}
|
|
#endif //!defined(_RELEASE)
|
|
}
|
|
}
|
|
|
|
#endif // DXGL_SUPPORT_QUERY_INTERNAL_FORMAT_SUPPORT
|
|
|
|
uint32 DetectGIFormatSupport(EGIFormat eGIFormat)
|
|
{
|
|
uint32 uSupport(0);
|
|
|
|
const SGIFormatInfo* pFormatInfo(GetGIFormatInfo(eGIFormat));
|
|
if (pFormatInfo == NULL)
|
|
{
|
|
return uSupport;
|
|
}
|
|
|
|
uSupport = pFormatInfo->m_uDefaultSupport;
|
|
|
|
const STextureFormat* pTextureFormat(pFormatInfo->m_pTexture);
|
|
if (pTextureFormat != NULL)
|
|
{
|
|
#if DXGL_SUPPORT_QUERY_INTERNAL_FORMAT_SUPPORT
|
|
QueryInternalFormatTexSupport(GL_TEXTURE_1D, pTextureFormat->m_iInternalFormat, D3D11_FORMAT_SUPPORT_TEXTURE1D, uSupport);
|
|
QueryInternalFormatTexSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, D3D11_FORMAT_SUPPORT_TEXTURE2D, uSupport);
|
|
QueryInternalFormatTexSupport(GL_TEXTURE_3D, pTextureFormat->m_iInternalFormat, D3D11_FORMAT_SUPPORT_TEXTURE3D, uSupport);
|
|
QueryInternalFormatTexSupport(GL_TEXTURE_CUBE_MAP, pTextureFormat->m_iInternalFormat, D3D11_FORMAT_SUPPORT_TEXTURECUBE, uSupport);
|
|
QueryInternalFormatSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, GL_MIPMAP, D3D11_FORMAT_SUPPORT_MIP, uSupport);
|
|
#else
|
|
DXGL_TODO("Use an alternative way to detect texture format support such as proxy textures");
|
|
uSupport |=
|
|
D3D11_FORMAT_SUPPORT_TEXTURE1D
|
|
| D3D11_FORMAT_SUPPORT_TEXTURE2D
|
|
| D3D11_FORMAT_SUPPORT_TEXTURE3D
|
|
| D3D11_FORMAT_SUPPORT_TEXTURECUBE
|
|
| D3D11_FORMAT_SUPPORT_MIP;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
uSupport &= ~(
|
|
D3D11_FORMAT_SUPPORT_TEXTURE1D
|
|
| D3D11_FORMAT_SUPPORT_TEXTURE2D
|
|
| D3D11_FORMAT_SUPPORT_TEXTURE3D
|
|
| D3D11_FORMAT_SUPPORT_TEXTURECUBE
|
|
| D3D11_FORMAT_SUPPORT_MIP);
|
|
}
|
|
|
|
const SUncompressedLayout* pUncompressedLayout(pFormatInfo->m_pUncompressed);
|
|
if (pUncompressedLayout != NULL && pTextureFormat != NULL)
|
|
{
|
|
#if DXGL_SUPPORT_QUERY_INTERNAL_FORMAT_SUPPORT
|
|
if (QueryInternalFormatSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, GL_FRAMEBUFFER_RENDERABLE, D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_DEPTH_STENCIL, uSupport))
|
|
{
|
|
uint32 uColorRenderable(0);
|
|
QueryInternalFormatSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, GL_COLOR_RENDERABLE, D3D11_FORMAT_SUPPORT_RENDER_TARGET, uColorRenderable);
|
|
uint32 uDepthRenderable(0);
|
|
QueryInternalFormatSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, GL_DEPTH_RENDERABLE, D3D11_FORMAT_SUPPORT_DEPTH_STENCIL, uDepthRenderable);
|
|
uint32 uStencilRenderable(0);
|
|
QueryInternalFormatSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, GL_STENCIL_RENDERABLE, D3D11_FORMAT_SUPPORT_DEPTH_STENCIL, uStencilRenderable);
|
|
uSupport |= uColorRenderable | uDepthRenderable | uStencilRenderable;
|
|
|
|
QueryInternalFormatSupport(GL_TEXTURE_2D, pTextureFormat->m_iInternalFormat, GL_FRAMEBUFFER_BLEND, D3D11_FORMAT_SUPPORT_BLENDABLE, uSupport);
|
|
}
|
|
#else
|
|
DXGL_TODO("Use an alternative way to detect format renderability such as per-platform tables in GLFormat.cpp");
|
|
uSupport |=
|
|
D3D11_FORMAT_SUPPORT_RENDER_TARGET
|
|
| D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET
|
|
| D3D11_FORMAT_SUPPORT_BLENDABLE
|
|
| D3D11_FORMAT_SUPPORT_DEPTH_STENCIL;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
uSupport &= ~(
|
|
D3D11_FORMAT_SUPPORT_RENDER_TARGET
|
|
| D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET
|
|
| D3D11_FORMAT_SUPPORT_BLENDABLE
|
|
| D3D11_FORMAT_SUPPORT_DEPTH_STENCIL);
|
|
}
|
|
return uSupport;
|
|
}
|
|
|
|
#if DXGL_SUPPORT_GETTEXIMAGE
|
|
|
|
bool DetectIfCopyImageWorksOnCubeMapFaces()
|
|
{
|
|
GLuint auInput[16 * 3 * 3];
|
|
for (uint32 uPixel = 0; uPixel < DXGL_ARRAY_SIZE(auInput); ++uPixel)
|
|
{
|
|
auInput[uPixel] = (GLuint)uPixel;
|
|
}
|
|
|
|
GLuint auTextures[2];
|
|
glGenTextures(2, auTextures);
|
|
glTextureStorage2DEXT(auTextures[0], GL_TEXTURE_2D, 1, GL_RGBA8, 4 * 3, 4 * 3);
|
|
glTextureStorage2DEXT(auTextures[1], GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 4, 4);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
glTextureSubImage2DEXT(auTextures[0], GL_TEXTURE_2D, 0, 0, 0, 4 * 3, 4 * 3, GL_RGBA, GL_UNSIGNED_BYTE, auInput);
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
|
|
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
|
|
glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
|
|
GLuint auFace[16];
|
|
GLuint auOutput[16 * 6];
|
|
bool bCopied(true);
|
|
for (uint32 uFace = 0; uFace < 6; ++uFace)
|
|
{
|
|
uint32 uX((uFace % 3) * 4), uY((uFace / 3) * 4);
|
|
glCopyImageSubData(auTextures[0], GL_TEXTURE_2D, 0, uX, uY, 0, auTextures[1], GL_TEXTURE_CUBE_MAP, 0, 0, 0, uFace, 4, 4, 1);
|
|
glGetTextureImageEXT(auTextures[1], GL_TEXTURE_CUBE_MAP_POSITIVE_X + uFace, 0, GL_RGBA, GL_UNSIGNED_BYTE, auFace);
|
|
for (uint32 uRow = 0; uRow < 4; ++uRow)
|
|
{
|
|
memcpy(auOutput + (uRow + uY) * 4 * 3 + uX, auFace + uRow * 4, 4 * sizeof(GLuint));
|
|
}
|
|
}
|
|
|
|
glDeleteTextures(2, auTextures);
|
|
|
|
return memcmp(auInput, auOutput, sizeof(auOutput)) == 0;
|
|
}
|
|
|
|
#endif //DXGL_SUPPORT_GETTEXIMAGE
|
|
|
|
#define ELEMENT(_Enum) _Enum,
|
|
|
|
static const GLenum g_aeMaxTextureUnits[] =
|
|
{
|
|
DXGL_SHADER_TYPE_MAP(ELEMENT,
|
|
GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
|
|
GL_MAX_TEXTURE_IMAGE_UNITS,
|
|
GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS,
|
|
GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS,
|
|
GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS,
|
|
GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS)
|
|
GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
|
|
};
|
|
|
|
static const GLenum g_aeMaxUniformBufferUnits[] =
|
|
{
|
|
DXGL_SHADER_TYPE_MAP(ELEMENT,
|
|
GL_MAX_VERTEX_UNIFORM_BLOCKS,
|
|
GL_MAX_FRAGMENT_UNIFORM_BLOCKS,
|
|
GL_MAX_GEOMETRY_UNIFORM_BLOCKS,
|
|
GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS,
|
|
GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS,
|
|
GL_MAX_COMPUTE_UNIFORM_BLOCKS)
|
|
GL_MAX_UNIFORM_BUFFER_BINDINGS
|
|
};
|
|
|
|
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
|
|
|
|
static const GLenum g_aeMaxStorageBufferUnits[] =
|
|
{
|
|
DXGL_SHADER_TYPE_MAP(ELEMENT,
|
|
GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS,
|
|
GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS,
|
|
GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS,
|
|
GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
|
|
GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
|
|
GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS)
|
|
GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS
|
|
};
|
|
|
|
#endif
|
|
|
|
#if DXGL_SUPPORT_SHADER_IMAGES
|
|
|
|
static const GLenum g_aeMaxImageUnits[] =
|
|
{
|
|
DXGL_SHADER_TYPE_MAP(ELEMENT,
|
|
GL_MAX_VERTEX_IMAGE_UNIFORMS,
|
|
GL_MAX_FRAGMENT_IMAGE_UNIFORMS,
|
|
GL_MAX_GEOMETRY_IMAGE_UNIFORMS,
|
|
GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS,
|
|
GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS,
|
|
GL_MAX_COMPUTE_IMAGE_UNIFORMS)
|
|
GL_MAX_IMAGE_UNITS
|
|
};
|
|
|
|
#endif
|
|
|
|
#undef ELEMENT
|
|
|
|
void DetectResourceUnitCapabilities(SResourceUnitCapabilities* pCapabilities, const GLenum* aeMaxUnits)
|
|
{
|
|
memset(pCapabilities->m_aiMaxPerStage, 0, sizeof(pCapabilities->m_aiMaxPerStage));
|
|
for (uint32 uStage = 0; uStage < eST_NUM; ++uStage)
|
|
{
|
|
glGetIntegerv(aeMaxUnits[uStage], pCapabilities->m_aiMaxPerStage + uStage);
|
|
}
|
|
|
|
pCapabilities->m_aiMaxTotal = 0;
|
|
glGetIntegerv(aeMaxUnits[eST_NUM], &pCapabilities->m_aiMaxTotal);
|
|
}
|
|
|
|
void DetectContextFeatures(TFeatures& kFeatures, const SCapabilities& kCapabilities, const SVersion& version, const unsigned int driverVendor)
|
|
{
|
|
uint32 glVersion = version.ToUint();
|
|
#if DXGLES || defined(DXGL_ES_SUBSET)
|
|
bool gles30orHigher = glVersion >= DXGLES_VERSION_30;
|
|
bool gles31orHigher = glVersion >= DXGLES_VERSION_31;
|
|
bool gles32orHigher = glVersion >= DXGLES_VERSION_32;
|
|
|
|
kFeatures.Set(eF_IndexedBoolState, gles31orHigher);
|
|
kFeatures.Set(eF_StencilOnlyFormat, gles31orHigher);
|
|
kFeatures.Set(eF_MultiSampledTextures, gles31orHigher);
|
|
kFeatures.Set(eF_DrawIndirect, gles31orHigher);
|
|
kFeatures.Set(eF_StencilTextures, gles31orHigher);
|
|
kFeatures.Set(eF_AtomicCounters, gles31orHigher);
|
|
kFeatures.Set(eF_DispatchIndirect, gles31orHigher);
|
|
kFeatures.Set(eF_ShaderImages, gles31orHigher);
|
|
kFeatures.Set(eF_TextureViews, gles30orHigher && DXGL_GL_EXTENSION_SUPPORTED(EXT_texture_view) && !DXGL_SUPPORT_NSIGHT_SINCE(4_1) && !DXGL_SUPPORT_VOGL);
|
|
kFeatures.Set(eF_SeparablePrograms, gles31orHigher || DXGL_GL_EXTENSION_SUPPORTED(EXT_separate_shader_objects));
|
|
kFeatures.Set(eF_ComputeShader, gles31orHigher);
|
|
kFeatures.Set(eF_DualSourceBlending, false);
|
|
kFeatures.Set(eF_IndependentBlending, gles32orHigher);
|
|
// glCopyImageSubData causes a crash on Mali GPUs. Disabling it for now.
|
|
kFeatures.Set(eF_CopyImage, (gles32orHigher || DXGL_GL_EXTENSION_SUPPORTED(EXT_copy_image)) && driverVendor != RenderCapabilities::s_gpuVendorIdARM);
|
|
// OpenGLES doesn't support depth clamping but we emulate it by writing the depth in the pixel shader.
|
|
// Unfortunately Qualcomm OpenGL ES 3.0 drivers have a bug and they don't support modifying the depth in the pixel shader.
|
|
kFeatures.Set(eF_DepthClipping, !(glVersion == DXGLES_VERSION_30 && driverVendor == RenderCapabilities::s_gpuVendorIdQualcomm));
|
|
|
|
bool isAnisotropicFilteringEnabled = DXGL_GL_EXTENSION_SUPPORTED(EXT_texture_filter_anisotropic);
|
|
kFeatures.Set(eF_TextureAnisotropicFiltering, isAnisotropicFilteringEnabled);
|
|
|
|
bool textureBorderClamp = false;
|
|
#if defined(DXGL_ES_SUBSET)
|
|
textureBorderClamp = true;
|
|
#endif // defined(DXGL_ES_SUBSET)
|
|
kFeatures.Set(eF_TextureBorderClamp, textureBorderClamp || DXGL_GL_EXTENSION_SUPPORTED(EXT_texture_border_clamp));
|
|
kFeatures.Set(eF_DebugOutput, DXGL_GL_EXTENSION_SUPPORTED(KHR_debug));
|
|
|
|
#else
|
|
// OpenGL
|
|
bool gl32orHigher = glVersion >= DXGL_VERSION_32;
|
|
bool gl41orHigher = glVersion >= DXGL_VERSION_41;
|
|
bool gl42orHigher = glVersion >= DXGL_VERSION_42;
|
|
bool gl43orHigher = glVersion >= DXGL_VERSION_43;
|
|
bool gl44orHigher = glVersion >= DXGL_VERSION_44;
|
|
|
|
kFeatures.Set(eF_DepthClipping, true);
|
|
kFeatures.Set(eF_IndexedBoolState, gl32orHigher);
|
|
kFeatures.Set(eF_StencilOnlyFormat, gl32orHigher);
|
|
kFeatures.Set(eF_TextureBorderClamp, gl32orHigher);
|
|
kFeatures.Set(eF_MultiSampledTextures, gl41orHigher);
|
|
kFeatures.Set(eF_DrawIndirect, gl41orHigher);
|
|
kFeatures.Set(eF_SeparablePrograms, gl41orHigher || DXGL_GL_EXTENSION_SUPPORTED(ARB_separate_shader_objects));
|
|
kFeatures.Set(eF_StencilTextures, gl42orHigher);
|
|
kFeatures.Set(eF_AtomicCounters, gl42orHigher);
|
|
kFeatures.Set(eF_DispatchIndirect, gl43orHigher);
|
|
kFeatures.Set(eF_ShaderImages, gl43orHigher);
|
|
kFeatures.Set(eF_VertexAttribBinding, gl43orHigher || DXGL_GL_EXTENSION_SUPPORTED(ARB_vertex_attrib_binding));
|
|
kFeatures.Set(eF_TextureViews, (gl43orHigher || DXGL_GL_EXTENSION_SUPPORTED(ARB_texture_view)) && !DXGL_SUPPORT_NSIGHT_SINCE(4_1) && !DXGL_SUPPORT_VOGL);
|
|
kFeatures.Set(eF_DebugOutput, gl43orHigher || DXGL_GL_EXTENSION_SUPPORTED(KHR_debug));
|
|
kFeatures.Set(eF_ComputeShader, gl43orHigher || DXGL_GL_EXTENSION_SUPPORTED(ARB_compute_shader));
|
|
kFeatures.Set(eF_BufferStorage, gl44orHigher || DXGL_GL_EXTENSION_SUPPORTED(ARB_buffer_storage));
|
|
kFeatures.Set(eF_IndependentBlending, true);
|
|
kFeatures.Set(eF_CopyImage, gl43orHigher);
|
|
#if DXGL_GLSL_FROM_HLSLCROSSCOMPILER
|
|
// Technically dual source blending is supported since OpenGL 3.3 but you need to declare the fragment shader output with the
|
|
// position and the index (for OpenGL < 4.4):
|
|
// layout(location = 0, index = 1) out vec4 diffuseColor1; <== SR1 for dual source blending
|
|
// Unfortunately the DX shader bytecode doesn't distinguish between a normal COLOR1 output or a COLOR1 for dual blending so the HLSL cross compiler
|
|
// doesn't know that it needs to generate a different declaration.
|
|
kFeatures.Set(eF_DualSourceBlending, gl44orHigher);
|
|
#else
|
|
kFeatures.Set(eF_DualSourceBlending, gl41orHigher);
|
|
#endif // DXGL_GLSL_FROM_HLSLCROSSCOMPILER
|
|
|
|
#endif //#if DXGLES || defined(DXGL_ES_SUBSET)
|
|
|
|
#if DXGL_GLSL_FROM_HLSLCROSSCOMPILER
|
|
DXGL_TODO(
|
|
"At the moment HLSLcc does guarantee exact interface matching between programs. "
|
|
"This can lead to pipelines that fail validation or worse with undefined behavior because they have "
|
|
"in one stage user-defined output variables that are ignored by the following stage."
|
|
"Investigate if this can be fixed.")
|
|
kFeatures.Set(eF_SeparablePrograms, false);
|
|
#endif
|
|
|
|
#ifndef AZ_PLATFORM_ANDROID
|
|
DXGL_TODO("Workaround for the NVIDIA 331.113 x64 linux driver crash - investigate");
|
|
kFeatures.Set(eF_VertexAttribBinding, false);
|
|
#endif // !AZ_PLATFORM_ANDROID
|
|
|
|
DXGL_TODO("Workaround for the multi-threaded GL driver crash - investigate");
|
|
kFeatures.Set(eF_MultiBind, false);
|
|
}
|
|
|
|
bool DetectFeaturesAndCapabilities(TFeatures& kFeatures, SCapabilities& kCapabilities, const SVersion& version, const unsigned int driverVendor)
|
|
{
|
|
glGetIntegerv(GL_MAX_SAMPLES, &kCapabilities.m_iMaxSamples);
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &kCapabilities.m_iMaxVertexAttribs);
|
|
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
|
|
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &kCapabilities.m_iShaderStorageBufferOffsetAlignment);
|
|
#endif
|
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &kCapabilities.m_iUniformBufferOffsetAlignment);
|
|
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &kCapabilities.m_iMaxUniformBlockSize);
|
|
|
|
DetectResourceUnitCapabilities(&kCapabilities.m_akResourceUnits[eRUT_Texture], g_aeMaxTextureUnits);
|
|
DetectResourceUnitCapabilities(&kCapabilities.m_akResourceUnits[eRUT_UniformBuffer], g_aeMaxUniformBufferUnits);
|
|
#if DXGL_SUPPORT_SHADER_STORAGE_BLOCKS
|
|
DetectResourceUnitCapabilities(&kCapabilities.m_akResourceUnits[eRUT_StorageBuffer], g_aeMaxStorageBufferUnits);
|
|
#endif
|
|
DetectContextFeatures(kFeatures, kCapabilities, version, driverVendor);
|
|
#if DXGL_SUPPORT_SHADER_IMAGES
|
|
if (kFeatures.Get(eF_ShaderImages))
|
|
{
|
|
DetectResourceUnitCapabilities(&kCapabilities.m_akResourceUnits[eRUT_Image], g_aeMaxImageUnits);
|
|
}
|
|
#endif
|
|
|
|
#if DXGL_SUPPORT_VERTEX_ATTRIB_BINDING
|
|
if (kFeatures.Get(eF_VertexAttribBinding))
|
|
{
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &kCapabilities.m_iMaxVertexAttribBindings);
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, &kCapabilities.m_iMaxVertexAttribRelativeOffset);
|
|
|
|
if (kCapabilities.m_iMaxVertexAttribBindings > MAX_VERTEX_ATTRIB_BINDINGS)
|
|
{
|
|
kCapabilities.m_iMaxVertexAttribBindings = MAX_VERTEX_ATTRIB_BINDINGS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (uint32 uGIFormat = 0; uGIFormat < eGIF_NUM; ++uGIFormat)
|
|
{
|
|
kCapabilities.m_auFormatSupport[uGIFormat] = DetectGIFormatSupport((EGIFormat)uGIFormat);
|
|
}
|
|
|
|
// Assume it works
|
|
kCapabilities.m_bCopyImageWorksOnCubeMapFaces = true;
|
|
#if DXGL_SUPPORT_GETTEXIMAGE
|
|
if(kFeatures.Get(eF_CopyImage))
|
|
{
|
|
kCapabilities.m_bCopyImageWorksOnCubeMapFaces = DetectIfCopyImageWorksOnCubeMapFaces();
|
|
}
|
|
#endif // DXGL_SUPPORT_GETTEXIMAGE
|
|
|
|
|
|
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &kCapabilities.m_maxRenderTargets);
|
|
kCapabilities.m_plsSizeInBytes = 0;
|
|
#if defined(GL_EXT_shader_pixel_local_storage)
|
|
if (DXGL_GL_EXTENSION_SUPPORTED(EXT_shader_pixel_local_storage))
|
|
{
|
|
glGetIntegerv(GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT, &kCapabilities.m_plsSizeInBytes);
|
|
}
|
|
#endif // GL_EXT_shader_pixel_local_storage
|
|
|
|
#if defined(GL_EXT_shader_framebuffer_fetch)
|
|
if (DXGL_GL_EXTENSION_SUPPORTED(EXT_shader_framebuffer_fetch))
|
|
{
|
|
kCapabilities.m_frameBufferFetchSupport.set(RenderCapabilities::FBF_ALL_COLORS);
|
|
kCapabilities.m_frameBufferFetchSupport.set(RenderCapabilities::FBF_COLOR0);
|
|
}
|
|
#endif // GL_EXT_shader_framebuffer_fetch
|
|
|
|
#if defined(GL_ARM_shader_framebuffer_fetch)
|
|
if (DXGL_GL_EXTENSION_SUPPORTED(ARM_shader_framebuffer_fetch))
|
|
{
|
|
// Check that we can fetch COLOR0 when using multiple render targets.
|
|
GLboolean mrtSupport = GL_FALSE;
|
|
glGetBooleanv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM, &mrtSupport);
|
|
if (mrtSupport)
|
|
{
|
|
kCapabilities.m_frameBufferFetchSupport.set(RenderCapabilities::FBF_COLOR0);
|
|
}
|
|
}
|
|
#endif // GL_ARM_shader_framebuffer_fetch
|
|
|
|
#if defined(GL_ARM_shader_framebuffer_fetch_depth_stencil)
|
|
if (DXGL_GL_EXTENSION_SUPPORTED(ARM_shader_framebuffer_fetch_depth_stencil))
|
|
{
|
|
kCapabilities.m_frameBufferFetchSupport.set(RenderCapabilities::FBF_DEPTH);
|
|
kCapabilities.m_frameBufferFetchSupport.set(RenderCapabilities::FBF_STENCIL);
|
|
}
|
|
#endif // GL_ARM_shader_framebuffer_fetch_depth_stencil
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t DetectVideoMemory()
|
|
{
|
|
#if DXGL_EXTENSION_LOADER
|
|
#if !DXGLES
|
|
if (DXGL_GL_EXTENSION_SUPPORTED(NVX_gpu_memory_info))
|
|
{
|
|
GLint iVMemKB = 0;
|
|
glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &iVMemKB);
|
|
return iVMemKB * 1024;
|
|
}
|
|
else if (DXGL_GL_EXTENSION_SUPPORTED(ATI_meminfo))
|
|
{
|
|
GLint aiTexFreeMemoryInfo[4] = {0, 0, 0, 0};
|
|
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, aiTexFreeMemoryInfo);
|
|
return aiTexFreeMemoryInfo[0] * 1024;
|
|
}
|
|
#else
|
|
DXGL_TODO("Not yet implemented for GLES");
|
|
#endif
|
|
return 0;
|
|
#elif defined(MAC)
|
|
return static_cast<size_t>(GetVRAMForDisplay(0));
|
|
#elif defined(IOS)
|
|
DXGL_TODO("Not yet implemented for iOS")
|
|
return 0;
|
|
#elif defined(DXGL_USE_EGL)
|
|
DXGL_TODO("Not yet implemented for EGL");
|
|
return 0;
|
|
#else
|
|
#error "Not implemented on this platform"
|
|
#endif
|
|
}
|
|
|
|
unsigned int DetectDriverVendor(const char* szVendorName)
|
|
{
|
|
struct
|
|
{
|
|
uint16 m_uPCIID;
|
|
const char* m_szName;
|
|
} akKnownVendors[] =
|
|
{
|
|
{ RenderCapabilities::s_gpuVendorIdNVIDIA, "NVIDIA Corporation" },
|
|
{ RenderCapabilities::s_gpuVendorIdNVIDIA, "Nouveau" },
|
|
{ RenderCapabilities::s_gpuVendorIdAMD, "ATI Technologies Inc." },
|
|
{ RenderCapabilities::s_gpuVendorIdAMD, "Advanced Micro Devices, Inc." },
|
|
{ RenderCapabilities::s_gpuVendorIdIntel, "Intel" },
|
|
{ RenderCapabilities::s_gpuVendorIdIntel, "Intel Inc." },
|
|
{ RenderCapabilities::s_gpuVendorIdIntel, "Intel Corporation" },
|
|
{ RenderCapabilities::s_gpuVendorIdIntel, "Intel Open Source Technology Center" },
|
|
{ RenderCapabilities::s_gpuVendorIdQualcomm, "Qualcomm" },
|
|
{ RenderCapabilities::s_gpuVendorIdARM, "ARM" },
|
|
// Rally US2888 - VendorID detection for Imagination, Samsung, etc.
|
|
};
|
|
|
|
for (uint32 uVendor = 0; uVendor < DXGL_ARRAY_SIZE(akKnownVendors); ++uVendor)
|
|
{
|
|
if (azstricmp(szVendorName, akKnownVendors[uVendor].m_szName) == 0)
|
|
{
|
|
return akKnownVendors[uVendor].m_uPCIID;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if DXGL_EXTENSION_LOADER
|
|
bool LoadEarlyGLEntryPoints()
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_6
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(DXGL_USE_LOADER_GLAD)
|
|
# if defined(DXGL_USE_EGL)
|
|
if (gladLoaderLoadEGL(NULL) == 0)
|
|
{
|
|
DXGL_ERROR("Failed to retrieve EGL entry points");
|
|
return false;
|
|
}
|
|
# endif
|
|
#else
|
|
#error "Not implemented on this platform"
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool LoadGLEntryPoints(SDummyContext& kDummyContext)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_7
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(DXGL_USE_LOADER_GLAD)
|
|
# if defined(DXGL_USE_EGL)
|
|
if (gladLoaderLoadEGL(NULL) == 0)
|
|
{
|
|
DXGL_ERROR("Failed to retrieve EGL entry points");
|
|
return false;
|
|
}
|
|
# elif defined(DXGL_USE_GLX)
|
|
if (gladLoaderLoadGLX(NULL, 0) == 0)
|
|
{
|
|
DXGL_ERROR("Failed to retrieve GLX entry points");
|
|
return false;
|
|
}
|
|
# endif
|
|
|
|
#if defined(OPENGL_ES)
|
|
int ret = gladLoaderLoadGLES2();
|
|
#else
|
|
int ret = gladLoaderLoadGL();
|
|
#endif //defined(OPENGL_ES)
|
|
|
|
if (ret == 0)
|
|
{
|
|
DXGL_ERROR("Failed to retrieve GL entry points");
|
|
return false;
|
|
}
|
|
|
|
# if defined(DXGL_USE_WGL)
|
|
if (gladLoaderLoadWGL(kDummyContext.m_kDummyWindow.m_kNativeDisplay) == 0)
|
|
{
|
|
DXGL_ERROR("Failed to retrieve WGL entry points");
|
|
return false;
|
|
}
|
|
# endif
|
|
|
|
#elif defined(DXGL_USE_LOADER_GLEW)
|
|
GLenum err = glewInit();
|
|
if (GLEW_OK != err)
|
|
{
|
|
DXGL_ERROR("Failed to init GLEW. Error %s", glewGetErrorString(err));
|
|
return false;
|
|
}
|
|
# if defined(DXGL_USE_WGL)
|
|
err = wglewInit();
|
|
if (GLEW_OK != err)
|
|
{
|
|
DXGL_ERROR("Failed to init WGL GLEW. Error %s", glewGetErrorString(err));
|
|
return false;
|
|
}
|
|
# elif defined(DXGL_USE_GLX)
|
|
err = glxewInit();
|
|
if (GLEW_OK != err)
|
|
{
|
|
DXGL_ERROR("Failed to init WGL GLEW. Error %s", glewGetErrorString(err));
|
|
return false;
|
|
}
|
|
# endif
|
|
#else
|
|
#error "Not implemented on this platform"
|
|
#endif
|
|
return true;
|
|
}
|
|
#endif //DXGL_EXTENSION_LOADER
|
|
bool GetGLVersion(SAdapterPtr& pAdapter)
|
|
{
|
|
if (!pAdapter)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Lumberyard::OpenGL::ClearErrors();
|
|
GLint major = 0, minor = 0;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
AZ_Assert(major >=0, "Invalid OpenGL major version %d", major);
|
|
AZ_Assert(minor >= 0, "Invalid OpenGL minor version %d", minor);
|
|
|
|
pAdapter->m_sVersion.m_uMajorVersion = static_cast<uint32>(major);
|
|
pAdapter->m_sVersion.m_uMinorVersion = static_cast<uint32>(minor);
|
|
return Lumberyard::OpenGL::CheckError() == GL_NO_ERROR;
|
|
}
|
|
|
|
bool ParseExtensions(SAdapterPtr& pAdapter)
|
|
{
|
|
if (!pAdapter)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int num = 0, index;
|
|
bool result = true;
|
|
Lumberyard::OpenGL::ClearErrors();
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &num);
|
|
for (index = 0; index < num; ++index)
|
|
{
|
|
const char* extension = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
|
|
if (!extension)
|
|
{
|
|
result = false;
|
|
AZ_Warning("Renderer", false, "Failed to get extension %d for adapter %s %s", index, pAdapter->m_strVendor.c_str(), pAdapter->m_strRenderer.c_str());
|
|
continue;
|
|
}
|
|
|
|
pAdapter->AddExtension(extension);
|
|
}
|
|
|
|
return result && Lumberyard::OpenGL::CheckError() == GL_NO_ERROR;
|
|
}
|
|
|
|
bool DetectAdapters(std::vector<SAdapterPtr>& kAdapters)
|
|
{
|
|
// Linux needs access to EGL much earlier than other EGL platforms do, so the EGL entry points are loaded in CreateWindow
|
|
#if DXGL_EXTENSION_LOADER && !defined(AZ_PLATFORM_LINUX)
|
|
if (!LoadEarlyGLEntryPoints())
|
|
{
|
|
return false;
|
|
}
|
|
#endif // DXGL_EXTENSION_LOADER
|
|
|
|
SDummyContext kDummyContext;
|
|
if (!kDummyContext.Initialize())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if DXGL_EXTENSION_LOADER
|
|
if (!LoadGLEntryPoints(kDummyContext))
|
|
{
|
|
return false;
|
|
}
|
|
#endif // DXGL_EXTENSION_LOADER
|
|
|
|
SAdapterPtr spAdapter(new SAdapter);
|
|
spAdapter->m_strRenderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
|
spAdapter->m_strVendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
|
spAdapter->m_strVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
|
spAdapter->m_uVRAMBytes = DetectVideoMemory();
|
|
spAdapter->m_eDriverVendor = DetectDriverVendor(spAdapter->m_strVendor.c_str());
|
|
spAdapter->m_sVersion.m_uMajorVersion = 0;
|
|
spAdapter->m_sVersion.m_uMinorVersion = 0;
|
|
bool result = GetGLVersion(spAdapter);
|
|
AZ_Warning("Renderer", result, "Failed to get the OpenGL version for adapter %s %s", spAdapter->m_strVendor.c_str(), spAdapter->m_strRenderer.c_str());
|
|
result = ParseExtensions(spAdapter);
|
|
AZ_Warning("Renderer", result, "Failed to parse OpenGL Extensions for adapter %s %s", spAdapter->m_strVendor.c_str(), spAdapter->m_strRenderer.c_str());
|
|
|
|
if (gEnv->pRenderer)
|
|
{
|
|
gEnv->pRenderer->SetApiVersion(spAdapter->m_strVersion.c_str());
|
|
}
|
|
|
|
if (!DetectFeaturesAndCapabilities(spAdapter->m_kFeatures, spAdapter->m_kCapabilities, spAdapter->m_sVersion, spAdapter->m_eDriverVendor))
|
|
{
|
|
return false;
|
|
}
|
|
kAdapters.push_back(spAdapter);
|
|
return true;
|
|
}
|
|
|
|
bool CheckAdapterCapabilities(const SAdapter& kAdapter, AZStd::string* pErrorMsg /*=nullptr*/)
|
|
{
|
|
// Check the openGL(ES) version
|
|
uint32 version = kAdapter.m_sVersion.ToUint();
|
|
uint32 requiredVersion;
|
|
#if DXGLES
|
|
requiredVersion = DXGLES_REQUIRED_VERSION;
|
|
#else
|
|
requiredVersion = DXGL_REQUIRED_VERSION;
|
|
#endif
|
|
|
|
if (version < requiredVersion)
|
|
{
|
|
if (pErrorMsg)
|
|
{
|
|
*pErrorMsg = AZStd::string::format("Device %s %s doesn't support the minimum version needed of OpenGL (ES). Required %u, found %u.", kAdapter.m_strVendor.c_str(),
|
|
kAdapter.m_strRenderer.c_str(),
|
|
requiredVersion, version);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int maxBufferUniform = kAdapter.m_kCapabilities.m_akResourceUnits[eRUT_UniformBuffer].m_aiMaxTotal;
|
|
if (maxBufferUniform < MIN_UNIFORM_BUFFERS_REQUIRED)
|
|
{
|
|
if (pErrorMsg)
|
|
{
|
|
*pErrorMsg = AZStd::string::format("Device %s %s doesn't support enough uniform buffers. Required %d, found %d", kAdapter.m_strVendor.c_str(), kAdapter.m_strRenderer.c_str(),
|
|
MIN_UNIFORM_BUFFERS_REQUIRED, maxBufferUniform);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DetectOutputs(const SAdapter& kAdapter, std::vector<SOutputPtr>& kOutputs)
|
|
{
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_8
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(WIN32)
|
|
uint32 uDisplay(0);
|
|
DISPLAY_DEVICEA kDisplayDevice;
|
|
ZeroMemory(&kDisplayDevice, sizeof(kDisplayDevice));
|
|
kDisplayDevice.cb = sizeof(kDisplayDevice);
|
|
while (EnumDisplayDevicesA(NULL, uDisplay, &kDisplayDevice, 0))
|
|
{
|
|
SOutputPtr spOutput(new SOutput);
|
|
spOutput->m_strDeviceID = kDisplayDevice.DeviceID;
|
|
spOutput->m_strDeviceName = kDisplayDevice.DeviceName;
|
|
|
|
DEVMODEA kDevMode;
|
|
ZeroMemory(&kDevMode, sizeof(kDevMode));
|
|
kDevMode.dmSize = sizeof(kDevMode);
|
|
|
|
SDisplayMode kDisplayMode;
|
|
uint32 uModeID(0);
|
|
while (EnumDisplaySettingsA(kDisplayDevice.DeviceName, uModeID, &kDevMode))
|
|
{
|
|
DevModeToDisplayMode(&kDisplayMode, kDevMode);
|
|
++uModeID;
|
|
|
|
spOutput->m_kModes.push_back(kDisplayMode);
|
|
}
|
|
|
|
if (!spOutput->m_kModes.empty())
|
|
{
|
|
if (!EnumDisplaySettingsA(kDisplayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &kDevMode))
|
|
{
|
|
DXGL_ERROR("Could not retrieve the desktop display mode mode for display %d", uDisplay);
|
|
return false;
|
|
}
|
|
DevModeToDisplayMode(&spOutput->m_kDesktopMode, kDevMode);
|
|
|
|
kOutputs.push_back(spOutput);
|
|
}
|
|
++uDisplay;
|
|
}
|
|
return true;
|
|
#elif defined(ANDROID)
|
|
ANativeWindow* nativeWindow = AZ::Android::Utils::GetWindow();
|
|
if (!nativeWindow)
|
|
{
|
|
DXGL_ERROR("Failed to get native window");
|
|
return false;
|
|
}
|
|
|
|
int widthPixels, heightPixels;
|
|
if (!AZ::Android::Utils::GetWindowSize(widthPixels, heightPixels))
|
|
{
|
|
DXGL_ERROR("Failed to get window size");
|
|
return false;
|
|
}
|
|
|
|
gcpRendD3D->GetClampedWindowSize(widthPixels, heightPixels);
|
|
|
|
SDisplayMode mode;
|
|
mode.m_uWidth = static_cast<uint32>(widthPixels);
|
|
mode.m_uHeight = static_cast<uint32>(heightPixels);
|
|
mode.m_uFrequency = 0;
|
|
mode.m_nativeFormat = ANativeWindow_getFormat(nativeWindow);
|
|
|
|
SOutputPtr output(new SOutput());
|
|
output->m_strDeviceID = "0";
|
|
output->m_strDeviceName = "Main Output";
|
|
output->m_kModes.push_back(mode);
|
|
output->m_kDesktopMode = mode;
|
|
kOutputs.push_back(output);
|
|
return true;
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
// TODO Linux - Query window dims from adapter
|
|
int widthPixels = 1280;
|
|
int heightPixels = 720;
|
|
gcpRendD3D->GetClampedWindowSize(widthPixels, heightPixels);
|
|
|
|
SDisplayMode mode;
|
|
mode.m_uWidth = static_cast<uint32>(widthPixels);
|
|
mode.m_uHeight = static_cast<uint32>(heightPixels);
|
|
mode.m_uFrequency = 0;
|
|
|
|
SOutputPtr output(new SOutput());
|
|
output->m_strDeviceID = "0";
|
|
output->m_strDeviceName = "Main Output";
|
|
output->m_kModes.push_back(mode);
|
|
output->m_kDesktopMode = mode;
|
|
kOutputs.push_back(output);
|
|
return true;
|
|
#else
|
|
DXGL_NOT_IMPLEMENTED;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool CheckFormatMultisampleSupport(SAdapter* pAdapter, EGIFormat eFormat, uint32 uNumSamples)
|
|
{
|
|
return
|
|
uNumSamples <= pAdapter->m_kCapabilities.m_iMaxSamples;
|
|
}
|
|
|
|
void GetDXGIModeDesc(DXGI_MODE_DESC* pDXGIModeDesc, const SDisplayMode& kDisplayMode)
|
|
{
|
|
pDXGIModeDesc->Width = kDisplayMode.m_uWidth;
|
|
pDXGIModeDesc->Height = kDisplayMode.m_uHeight;
|
|
pDXGIModeDesc->RefreshRate.Numerator = kDisplayMode.m_uFrequency;
|
|
pDXGIModeDesc->RefreshRate.Denominator = 1;
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_9
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(WIN32)
|
|
DXGL_TODO("Check if there is a better way of mapping GL display modes to formats")
|
|
switch (kDisplayMode.m_uBitsPerPixel)
|
|
{
|
|
case 32:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
break;
|
|
case 64:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_R16G16B16A16_UNORM;
|
|
break;
|
|
default:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_UNKNOWN;
|
|
break;
|
|
}
|
|
#elif defined(ANDROID)
|
|
switch (kDisplayMode.m_nativeFormat)
|
|
{
|
|
case WINDOW_FORMAT_RGBA_8888:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
break;
|
|
case WINDOW_FORMAT_RGBX_8888:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_B8G8R8X8_UNORM;
|
|
break;
|
|
case WINDOW_FORMAT_RGB_565:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_B5G6R5_UNORM;
|
|
break;
|
|
default:
|
|
pDXGIModeDesc->Format = DXGI_FORMAT_UNKNOWN;
|
|
break;
|
|
}
|
|
#elif defined(AZ_PLATFORM_LINUX)
|
|
// Do nothing?
|
|
#else
|
|
DXGL_NOT_IMPLEMENTED;
|
|
#endif
|
|
|
|
pDXGIModeDesc->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
pDXGIModeDesc->Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
}
|
|
|
|
bool GetDisplayMode(SDisplayMode* pDisplayMode, const DXGI_MODE_DESC& kDXGIModeDesc)
|
|
{
|
|
pDisplayMode->m_uWidth = kDXGIModeDesc.Width;
|
|
pDisplayMode->m_uHeight = kDXGIModeDesc.Height;
|
|
if (kDXGIModeDesc.RefreshRate.Denominator != 0)
|
|
{
|
|
pDisplayMode->m_uFrequency = kDXGIModeDesc.RefreshRate.Numerator / kDXGIModeDesc.RefreshRate.Denominator;
|
|
}
|
|
else
|
|
{
|
|
pDisplayMode->m_uFrequency = 0;
|
|
}
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION GLDEVICE_CPP_SECTION_10
|
|
#include AZ_RESTRICTED_FILE(GLDevice_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#elif defined(WIN32)
|
|
switch (kDXGIModeDesc.Format)
|
|
{
|
|
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
|
pDisplayMode->m_uBitsPerPixel = 32;
|
|
break;
|
|
case DXGI_FORMAT_R16G16B16A16_UNORM:
|
|
pDisplayMode->m_uBitsPerPixel = 64;
|
|
break;
|
|
default:
|
|
{
|
|
EGIFormat eGIFormat(GetGIFormat(kDXGIModeDesc.Format));
|
|
const SGIFormatInfo* pFormatInfo;
|
|
if (eGIFormat == eGIF_NUM ||
|
|
(pFormatInfo = GetGIFormatInfo(eGIFormat)) == NULL ||
|
|
pFormatInfo->m_pUncompressed == NULL)
|
|
{
|
|
DXGL_ERROR("Invalid DXGI format for display mode");
|
|
return false;
|
|
}
|
|
pDisplayMode->m_uBitsPerPixel = pFormatInfo->m_pUncompressed->GetPixelBits();
|
|
}
|
|
break;
|
|
}
|
|
#elif defined(ANDROID)
|
|
switch (kDXGIModeDesc.Format)
|
|
{
|
|
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
|
pDisplayMode->m_nativeFormat = WINDOW_FORMAT_RGBA_8888;
|
|
break;
|
|
case DXGI_FORMAT_B8G8R8X8_UNORM:
|
|
pDisplayMode->m_nativeFormat = WINDOW_FORMAT_RGBX_8888;
|
|
break;
|
|
case DXGI_FORMAT_B5G6R5_UNORM:
|
|
pDisplayMode->m_nativeFormat = WINDOW_FORMAT_RGB_565;
|
|
break;
|
|
default:
|
|
AZ_Assert(false, "Invalid DXGI_MODE_DESC format %d", kDXGIModeDesc.Format);
|
|
return false;
|
|
}
|
|
#endif
|
|
DXGL_TODO("Consider scanline order and scaling if possible");
|
|
|
|
return true;
|
|
}
|
|
|
|
#if DXGL_DEBUG_OUTPUT_VERBOSITY
|
|
|
|
void DXGL_DEBUG_CALLBACK_CONVENTION DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* message, void* userParam)
|
|
{
|
|
// this filters out the debug messages earlier saving the performance which might be broken by excessive string creation.
|
|
if ((type == GL_DEBUG_SEVERITY_LOW) || (type == GL_DEBUG_SEVERITY_NOTIFICATION))
|
|
{
|
|
return;
|
|
}
|
|
|
|
::string errorMessage, sourceStr, typeStr, severityStr;
|
|
ELogSeverity eLogSeverity = eLS_Warning;
|
|
|
|
switch (source)
|
|
{
|
|
case GL_DEBUG_SOURCE_API:
|
|
sourceStr = "OpenGL";
|
|
break;
|
|
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
|
sourceStr = "Windows";
|
|
break;
|
|
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
|
sourceStr = "Shader Compiler";
|
|
break;
|
|
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
|
sourceStr = "Third Party";
|
|
break;
|
|
case GL_DEBUG_SOURCE_APPLICATION:
|
|
sourceStr = "Application";
|
|
break;
|
|
case GL_DEBUG_SOURCE_OTHER:
|
|
sourceStr = "Other";
|
|
break;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case GL_DEBUG_TYPE_ERROR:
|
|
typeStr = "Error";
|
|
break;
|
|
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
|
typeStr = "Deprecated behavior";
|
|
break;
|
|
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
|
typeStr = "Undefined behavior";
|
|
break;
|
|
case GL_DEBUG_TYPE_PORTABILITY:
|
|
typeStr = "Portability";
|
|
break;
|
|
case GL_DEBUG_TYPE_PERFORMANCE:
|
|
typeStr = "Performance";
|
|
break;
|
|
case GL_DEBUG_TYPE_OTHER:
|
|
typeStr = "Other";
|
|
break;
|
|
}
|
|
|
|
switch (severity)
|
|
{
|
|
case GL_DEBUG_SEVERITY_HIGH:
|
|
severityStr = "High";
|
|
eLogSeverity = eLS_Error;
|
|
break;
|
|
case GL_DEBUG_SEVERITY_MEDIUM:
|
|
severityStr = "Medium";
|
|
eLogSeverity = eLS_Warning;
|
|
break;
|
|
case GL_DEBUG_SEVERITY_LOW:
|
|
severityStr = "Low";
|
|
eLogSeverity = eLS_Info;
|
|
break;
|
|
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
|
severityStr = "Info";
|
|
eLogSeverity = eLS_Info;
|
|
break;
|
|
}
|
|
|
|
//Anyone needing more information on OpenGL rendering in non-debug builds should enable this section of code for additional information
|
|
//It's DEBUG only to help obtain a cleaner log on some Android devices which would otherwise be inundated with messages from this section of code
|
|
#if defined(DEBUG)
|
|
if (eLogSeverity != eLS_Info)
|
|
{
|
|
errorMessage.Format("OpenGLError:\nSource: %s\nType: %s\nId: %i\nSeverity: %s\nMessage: %s\n", sourceStr.c_str(), typeStr.c_str(), id, severityStr.c_str(), message);
|
|
NCryOpenGL::LogMessage(eLogSeverity, errorMessage.c_str());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif //DXGL_DEBUG_OUTPUT_VERBOSITY
|
|
|
|
#if DXGL_TRACE_CALLS
|
|
|
|
void CallTracePrintf(const char* szFormat, ...)
|
|
{
|
|
CDevice* pDevice(CDevice::GetCurrentDevice());
|
|
if (pDevice == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CContext* pCurrentContext(pDevice->GetCurrentContext());
|
|
if (pCurrentContext == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
char acBuffer[512];
|
|
va_list kArgs;
|
|
va_start(kArgs, szFormat);
|
|
vsprintf_s(acBuffer, szFormat, kArgs);
|
|
va_end(kArgs);
|
|
|
|
pCurrentContext->CallTraceWrite(acBuffer);
|
|
}
|
|
|
|
void CallTraceFlush()
|
|
{
|
|
CDevice* pDevice(CDevice::GetCurrentDevice());
|
|
if (pDevice == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CContext* pCurrentContext(pDevice->GetCurrentContext());
|
|
if (pCurrentContext == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pCurrentContext->CallTraceFlush();
|
|
}
|
|
|
|
#endif
|
|
|
|
#if DXGL_CHECK_ERRORS
|
|
|
|
void CheckErrors()
|
|
{
|
|
enum
|
|
{
|
|
MAX_ERROR_QUERIES = 4
|
|
};
|
|
uint32 uNumQueries(0);
|
|
GLenum eErrorCode;
|
|
while ((eErrorCode = DXGL_UNWRAPPED_FUNCTION(glGetError)()) != GL_NO_ERROR)
|
|
{
|
|
const char* szName;
|
|
const char* szMessage;
|
|
switch (eErrorCode)
|
|
{
|
|
#if defined(WIN32)
|
|
case GL_CONTEXT_LOST:
|
|
szName = "GL_CONTEXT_LOST";
|
|
szMessage = "Context has been lost and reset by the driver";
|
|
break;
|
|
#endif
|
|
case GL_INVALID_ENUM:
|
|
szName = "GL_INVALID_ENUM";
|
|
szMessage = "Enum argument out of range";
|
|
break;
|
|
case GL_INVALID_VALUE:
|
|
szName = "GL_INVALID_VALUE";
|
|
szMessage = "Numeric argument out of range";
|
|
break;
|
|
case GL_INVALID_OPERATION:
|
|
szName = "GL_INVALID_OPERATION";
|
|
szMessage = "Operation illegal in current state";
|
|
break;
|
|
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
|
szName = "GL_INVALID_FRAMEBUFFER_OPERATION";
|
|
szMessage = "Framebuffer object is not complete";
|
|
break;
|
|
case GL_OUT_OF_MEMORY:
|
|
szName = "GL_OUT_OF_MEMORY";
|
|
szMessage = "Not enough memory left to execute command";
|
|
break;
|
|
case GL_STACK_OVERFLOW:
|
|
szName = "GL_STACK_OVERFLOW";
|
|
szMessage = "Command would cause a stack overflow";
|
|
break;
|
|
case GL_STACK_UNDERFLOW:
|
|
szName = "GL_STACK_UNDERFLOW";
|
|
szMessage = "Command would cause a stack underflow";
|
|
break;
|
|
default:
|
|
szName = "?";
|
|
szMessage = "Unknown GL error";
|
|
break;
|
|
}
|
|
DXGL_ERROR("GL error: %s (0x%04X) - %s", szName, eErrorCode, szMessage);
|
|
if (++uNumQueries > MAX_ERROR_QUERIES)
|
|
{
|
|
DXGL_ERROR("GL error limit reached - probably no context set");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //DXGL_CHECK_ERRORS
|
|
} // namespace NCryOpenGL
|
|
|
|
namespace Lumberyard
|
|
{
|
|
namespace OpenGL
|
|
{
|
|
#if defined(LY_ENABLE_OPENGL_ERROR_CHECKING)
|
|
unsigned int CheckError()
|
|
{
|
|
GLenum errorCode = glGetError();
|
|
while (errorCode != GL_NO_ERROR)
|
|
{
|
|
::string errorMessage = string().Format("OpenGL Error: [0x%08x]\n!", errorCode);
|
|
NCryOpenGL::LogMessage(NCryOpenGL::ELogSeverity::eLS_Warning, errorMessage.c_str());
|
|
errorCode = glGetError();
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
void ClearErrors()
|
|
{
|
|
while (glGetError() != GL_NO_ERROR);
|
|
}
|
|
#endif
|
|
} // namespace OpenGL
|
|
} // namespace Lumberyard
|