summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineOpenGL/src/GLContextWindows.cpp
blob: 305cbe54be3d8a8be590e038b04e9728974d6e4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence), 
 *  contract, or otherwise, unless required by applicable law (such as deliberate 
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental, 
 *  or consequential damages of any character arising as a result of this License or 
 *  out of the use or inability to use the software (including but not limited to damages 
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and 
 *  all other commercial damages or losses), even if such Contributor has been advised 
 *  of the possibility of such damages.
 */

#include "pch.h"

#include "GLContextWindows.hpp"
#include "GraphicsTypes.h"
#include "GLTypeConversions.hpp"
#include "GraphicsAccessories.hpp"

namespace Diligent
{

GLContext::GLContext(const EngineGLCreateInfo& InitAttribs, DeviceCaps& deviceCaps, const SwapChainDesc* pSCDesc) :
    m_Context{0},
    m_WindowHandleToDeviceContext{0}
{
    Int32 MajorVersion = 0, MinorVersion = 0;
    if (InitAttribs.Window.hWnd != nullptr)
    {
        HWND hWnd = reinterpret_cast<HWND>(InitAttribs.Window.hWnd);

        // See http://www.opengl.org/wiki/Tutorial:_OpenGL_3.1_The_First_Triangle_(C%2B%2B/Win)
        //     http://www.opengl.org/wiki/Creating_an_OpenGL_Context_(WGL)
        PIXELFORMATDESCRIPTOR pfd;
        memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
        pfd.nSize      = sizeof(PIXELFORMATDESCRIPTOR);
        pfd.nVersion   = 1;
        pfd.dwFlags    = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
        pfd.iPixelType = PFD_TYPE_RGBA;
        if (pSCDesc != nullptr)
        {
            auto ColorFmt = pSCDesc->ColorBufferFormat;
            if (ColorFmt == TEX_FORMAT_RGBA8_UNORM || ColorFmt == TEX_FORMAT_RGBA8_UNORM_SRGB ||
                ColorFmt == TEX_FORMAT_BGRA8_UNORM || ColorFmt == TEX_FORMAT_BGRA8_UNORM_SRGB)
            {
                pfd.cColorBits = 32;
            }
            else
            {
                LOG_WARNING_MESSAGE("Unsupported color buffer format ", GetTextureFormatAttribs(ColorFmt).Name,
                                    ". OpenGL only supports 32-bit UNORM color buffer formats.");
                pfd.cColorBits = 32;
            }

            auto DepthFmt = pSCDesc->DepthBufferFormat;
            switch (DepthFmt)
            {
                case TEX_FORMAT_UNKNOWN:
                    pfd.cDepthBits   = 0;
                    pfd.cStencilBits = 0;
                    break;

                case TEX_FORMAT_D32_FLOAT_S8X24_UINT:
                    pfd.cDepthBits   = 32;
                    pfd.cStencilBits = 8;
                    break;

                case TEX_FORMAT_D32_FLOAT:
                    pfd.cDepthBits = 32;
                    break;

                case TEX_FORMAT_D24_UNORM_S8_UINT:
                    pfd.cDepthBits   = 24;
                    pfd.cStencilBits = 8;
                    break;

                case TEX_FORMAT_D16_UNORM:
                    pfd.cDepthBits = 16;
                    break;

                default:
                    LOG_ERROR_MESSAGE("Unsupported depth buffer format ", GetTextureFormatAttribs(DepthFmt).Name);
                    pfd.cDepthBits = 32;
            }
        }
        else
        {
            pfd.cColorBits = 32;
            pfd.cDepthBits = 32;
        }
        pfd.iLayerType = PFD_MAIN_PLANE;

        m_WindowHandleToDeviceContext = GetDC(hWnd);
        int nPixelFormat              = ChoosePixelFormat(m_WindowHandleToDeviceContext, &pfd);

        if (nPixelFormat == 0)
            LOG_ERROR_AND_THROW("Invalid Pixel Format");

        BOOL bResult = SetPixelFormat(m_WindowHandleToDeviceContext, nPixelFormat, &pfd);
        if (!bResult)
            LOG_ERROR_AND_THROW("Failed to set Pixel Format");

        // Create standard OpenGL (2.1) rendering context which will be used only temporarily,
        HGLRC tempContext = wglCreateContext(m_WindowHandleToDeviceContext);
        // and make it current
        wglMakeCurrent(m_WindowHandleToDeviceContext, tempContext);

        // Initialize GLEW
        GLenum err = glewInit();
        if (GLEW_OK != err)
            LOG_ERROR_AND_THROW("Failed to initialize GLEW");

        if (wglewIsSupported("WGL_ARB_create_context") == 1)
        {
            std::pair<int, int> gl_versions[] = {{4, 4}, {4, 3}, {4, 2}};
            for (size_t i = 0; i < _countof(gl_versions) && m_Context == NULL; ++i)
            {
                const auto& version = gl_versions[i];
                MajorVersion        = version.first;
                MinorVersion        = version.second;
                // Setup attributes for a new OpenGL rendering context
                int attribs[] =
                    {
                        WGL_CONTEXT_MAJOR_VERSION_ARB, MajorVersion,
                        WGL_CONTEXT_MINOR_VERSION_ARB, MinorVersion,
                        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                        GL_CONTEXT_PROFILE_MASK, GL_CONTEXT_CORE_PROFILE_BIT,
                        0, 0 //
                    };

                if (InitAttribs.CreateDebugContext)
                {
                    attribs[5] |= WGL_CONTEXT_DEBUG_BIT_ARB;
                }

                // Create new rendering context
                // In order to create new OpenGL rendering context we have to call function wglCreateContextAttribsARB(),
                // which is an OpenGL function and requires OpenGL to be active when it is called.
                // The only way is to create an old context, activate it, and while it is active create a new one.
                // Very inconsistent, but we have to live with it!
                m_Context = wglCreateContextAttribsARB(m_WindowHandleToDeviceContext, 0, attribs);
            }

            if (m_Context == NULL)
            {
                LOG_ERROR_AND_THROW("Failed to initialize OpenGL context.");
            }

            // Delete tempContext
            wglMakeCurrent(NULL, NULL);
            wglDeleteContext(tempContext);
            // Make new context current
            wglMakeCurrent(m_WindowHandleToDeviceContext, m_Context);
            wglSwapIntervalEXT(0);
        }
        else
        { //It's not possible to make a GL 4.x context. Use the old style context (GL 2.1 and before)
            m_Context = tempContext;
        }
    }
    else
    {
        auto CurrentCtx = wglGetCurrentContext();
        if (CurrentCtx == 0)
        {
            LOG_ERROR_AND_THROW("No current GL context found! Provide non-null handle to a native Window to create a GL context");
        }

        // Initialize GLEW
        GLenum err = glewInit();
        if (GLEW_OK != err)
            LOG_ERROR_AND_THROW("Failed to initialize GLEW");
    }

    //Checking GL version
    const GLubyte* GLVersionString = glGetString(GL_VERSION);

    //Or better yet, use the GL3 way to get the version number
    glGetIntegerv(GL_MAJOR_VERSION, &MajorVersion);
    glGetIntegerv(GL_MINOR_VERSION, &MinorVersion);
    LOG_INFO_MESSAGE(InitAttribs.Window.hWnd != nullptr ? "Initialized OpenGL " : "Attached to OpenGL ", MajorVersion, '.', MinorVersion, " context (", GLVersionString, ')');

    // Under the standard filtering rules for cubemaps, filtering does not work across faces of the cubemap.
    // This results in a seam across the faces of a cubemap. This was a hardware limitation in the past, but
    // modern hardware is capable of interpolating across a cube face boundary.
    // GL_TEXTURE_CUBE_MAP_SEAMLESS is not defined in OpenGLES
    glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
    if (glGetError() != GL_NO_ERROR)
        LOG_ERROR_MESSAGE("Failed to enable seamless cubemap filtering");

    // When GL_FRAMEBUFFER_SRGB is enabled, and if the destination image is in the sRGB colorspace
    // then OpenGL will assume the shader's output is in the linear RGB colorspace. It will therefore
    // convert the output from linear RGB to sRGB.
    // Any writes to images that are not in the sRGB format should not be affected.
    // Thus this setting should be just set once and left that way
    glEnable(GL_FRAMEBUFFER_SRGB);
    if (glGetError() != GL_NO_ERROR)
        LOG_ERROR_MESSAGE("Failed to enable SRGB framebuffers");

    deviceCaps.DevType      = RENDER_DEVICE_TYPE_GL;
    deviceCaps.MajorVersion = MajorVersion;
    deviceCaps.MinorVersion = MinorVersion;
}

GLContext::~GLContext()
{
    // Do not destroy context if it was created by the app.
    if (m_Context)
    {
        wglMakeCurrent(m_WindowHandleToDeviceContext, 0);
        wglDeleteContext(m_Context);
    }
}

void GLContext::SwapBuffers(int SwapInterval)
{
    if (m_WindowHandleToDeviceContext)
    {
#if WGL_EXT_swap_control
        if (wglSwapIntervalEXT != nullptr)
        {
            wglSwapIntervalEXT(SwapInterval);
        }
#endif

        ::SwapBuffers(m_WindowHandleToDeviceContext);
    }
    else
    {
        LOG_ERROR("Swap buffer failed because window handle to device context is not initialized");
    }
}

GLContext::NativeGLContextType GLContext::GetCurrentNativeGLContext()
{
    return wglGetCurrentContext();
}

} // namespace Diligent