Gamp v0.0.8
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
gamp_sdl2_gl.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11
12#include <gamp/Gamp.hpp>
13#include <gamp/GampTypes.hpp>
20#include <gamp/wt/Window.hpp>
21#include <memory>
22
23#include <SDL2/SDL.h>
24#include <SDL2/SDL_video.h>
25
26using namespace jau::int_literals;
27using namespace jau::fractions_i64_literals;
28using namespace jau::enums;
29
30using namespace gamp;
31using namespace gamp::wt;
32using namespace gamp::render;
33using namespace gamp::render::gl;
34
35static bool GetGLAttribute(SDL_GLattr attr, int& value) noexcept {
36 return 0 == SDL_GL_GetAttribute(attr, &value);
37}
38static bool SetGLAttribute(SDL_GLattr attr, int value) noexcept {
39 return 0 == SDL_GL_SetAttribute(attr, value);
40}
41
42bool Surface::setSwapIntervalImpl(int v) noexcept {
43 if( 0 == SDL_GL_SetSwapInterval( v ) ) {
44 m_swapInterval = v;
45 return true;
46 }
47 if( -1 == v && 0 == SDL_GL_SetSwapInterval( 1 ) ) {
48 m_swapInterval = 1;
49 return true;
50 }
51 m_swapInterval = 0;
52 return false;
53}
54
55CapabilitiesPtr Surface::retrieveCaps(const wt::SurfaceSRef& surface) noexcept {
56 if (!surface->isValid()) {
57 return nullptr;
58 }
59 // SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(surface->surfaceHandle()); // NOLINT
60 GLCapabilitiesPtr caps = std::make_unique<GLCapabilities>();
61 bool ok;
62 int v;
63
64 ok = GetGLAttribute(SDL_GL_RED_SIZE, caps->redBits());
65 ok = ok && GetGLAttribute(SDL_GL_GREEN_SIZE, caps->greenBits());
66 ok = ok && GetGLAttribute(SDL_GL_BLUE_SIZE, caps->blueBits());
67 ok = ok && GetGLAttribute(SDL_GL_ALPHA_SIZE, caps->alphaBits());
68 if (!ok) {
69 return nullptr;
70 }
71
72 ok = GetGLAttribute(SDL_GL_DOUBLEBUFFER, v);
73 if (ok) { caps->setDoubleBuffered(v); }
74
75 GetGLAttribute(SDL_GL_DEPTH_SIZE, caps->depthBits());
76 GetGLAttribute(SDL_GL_STENCIL_SIZE, caps->stencilBits());
77 GetGLAttribute(SDL_GL_ACCUM_RED_SIZE, caps->accumRedBits());
78 GetGLAttribute(SDL_GL_ACCUM_GREEN_SIZE, caps->accumGreenBits());
79 GetGLAttribute(SDL_GL_ACCUM_BLUE_SIZE, caps->accumBlueBits());
80 GetGLAttribute(SDL_GL_ACCUM_ALPHA_SIZE, caps->accumAlphaBits());
81 ok = GetGLAttribute(SDL_GL_STEREO, v);
82 if (ok) { caps->setStereo(v); }
83 ok = GetGLAttribute(SDL_GL_ACCELERATED_VISUAL, v);
84 if (ok) { caps->setHardwareAccelerated(v>0); }
85
86 ok = GetGLAttribute(SDL_GL_MULTISAMPLEBUFFERS, v);
87 if (ok && v > 0) {
88 ok = GetGLAttribute(SDL_GL_MULTISAMPLESAMPLES, v);
89 if (ok) { caps->setMultiSamplesCount(v); }
90 }
91
92 return caps;
93}
94
95void Surface::setCaps(handle_t surface_handle, const Capabilities& requested) noexcept {
96 (void)surface_handle; // SDL2 uses a process static capabilities system (well)
97
98 SetGLAttribute(SDL_GL_RED_SIZE, requested.redBits());
99 SetGLAttribute(SDL_GL_GREEN_SIZE, requested.greenBits());
100 SetGLAttribute(SDL_GL_BLUE_SIZE, requested.blueBits());
101 SetGLAttribute(SDL_GL_ALPHA_SIZE, requested.alphaBits());
102
103 if( requested.signature() == GLCapabilities::GLSignature() ) {
104 const GLCapabilities &glcaps = static_cast<const GLCapabilities&>(requested);
105 SetGLAttribute(SDL_GL_DOUBLEBUFFER, glcaps.doubleBuffered() ? 1 : 0);
106
107 SetGLAttribute(SDL_GL_DEPTH_SIZE, glcaps.depthBits());
108 SetGLAttribute(SDL_GL_STENCIL_SIZE, glcaps.stencilBits());
109 SetGLAttribute(SDL_GL_ACCUM_RED_SIZE, glcaps.accumRedBits());
110 SetGLAttribute(SDL_GL_ACCUM_GREEN_SIZE, glcaps.accumGreenBits());
111 SetGLAttribute(SDL_GL_ACCUM_BLUE_SIZE, glcaps.accumBlueBits());
112 SetGLAttribute(SDL_GL_ACCUM_ALPHA_SIZE, glcaps.accumAlphaBits());
113 SetGLAttribute(SDL_GL_STEREO, glcaps.stereo() ? 1 : 0);
114 SetGLAttribute(SDL_GL_ACCELERATED_VISUAL, glcaps.hardwareAccelerated() ? 1 : 0);
115 if (glcaps.hasMultiSamples()) {
116 SetGLAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
117 SetGLAttribute(SDL_GL_MULTISAMPLESAMPLES, glcaps.multiSamplesCount());
118 } else {
119 SetGLAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
120 SetGLAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
121 }
122 }
123}
124
126 const gamp::render::RenderProfile& profile,
127 const gamp::render::RenderContextFlags& contextFlags,
128 gamp::render::RenderContext* shareWith) noexcept {
129 // const wt::SurfaceSRef& surface, render::gl::GLProfileMask profile, render::gl::GLContextFlags contextFlags, render::gl::GL* shareWith
130 if( !surface->isValid() ) {
131 printf("SDL: Error creating GL context: Invalid surface: %s\n", surface->toString().c_str());
132 return nullptr;
133 }
134 const bool verbose = is_set(contextFlags, gamp::render::RenderContextFlags::verbose);
135 if( verbose ) {
136 printf("Surface::createContext: shareWith %p, profile %s, ctx %s, surface %s\n",
137 (void*)shareWith, profile.toString().c_str(), to_string(contextFlags).c_str(), surface->toString().c_str());
138 }
139 SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(surface->surfaceHandle()); // NOLINT
140 // Create OpenGL context on SDL window
141 surface->setSwapIntervalImpl( surface->swapInterval() );
142 {
143 int ctxFlags = 0;
145 ctxFlags |= SDL_GL_CONTEXT_DEBUG_FLAG;
146 }
148 ctxFlags |= SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG;
149 }
150 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, ctxFlags);
151 }
152 if( shareWith ) {
153 // FIXME: shareWith should be current!
154 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
155 }
156 if( verbose ) {
157 printf("Surface::createContext.1: surface %s\n", surface->toString().c_str());
158 }
159
160 GLProfile glp_in;
161 bool use_glp_core = false;
162 if( profile.signature() == GLProfile::GLSignature() ) {
163 glp_in = GLProfile::downcast(profile);
164 if( !glp_in.isGLES() ) {
165 use_glp_core = true;
166 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
167 if( glp_in.version().major() >= 4 ) {
168 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
169 } else if( glp_in.version().major() >= 3 ) {
170 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
171 }
172 }
173 }
174 if( !use_glp_core ) {
175 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
176 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
177 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
178 }
179 SDL_GLContext sdl_glc = SDL_GL_CreateContext(sdl_win);
180 if (nullptr == sdl_glc) {
181 if( use_glp_core ) {
182 printf("SDL: Error creating %s context: %s\n", glp_in.toString().c_str(), SDL_GetError());
183 return nullptr;
184 }
185 printf("SDL: Error creating GL ES 3 context: %s\n", SDL_GetError());
186 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
187 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
188 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
189 sdl_glc = SDL_GL_CreateContext(sdl_win);
190 if (nullptr == sdl_glc) {
191 printf("SDL: Error creating GL ES 2 context: %s\n", SDL_GetError());
192 return nullptr;
193 }
194 }
195 if (0 != SDL_GL_MakeCurrent(sdl_win, sdl_glc)) {
196 printf("SDL: Error making GL context current: %s\n", SDL_GetError());
197 SDL_GL_DeleteContext(sdl_glc);
198 return nullptr;
199 }
200 const char* gl_version_cstr = reinterpret_cast<const char*>( glGetString(GL_VERSION) );
201 if (nullptr == gl_version_cstr) {
202 printf("SDL: Error retrieving GL version: %s\n", SDL_GetError());
203 SDL_GL_DeleteContext(sdl_glc);
204 return nullptr;
205 }
206 bool ok;
207 int major, minor, nContextFlags, nProfileMask;
208
209 ok = GetGLAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
210 ok = ok && GetGLAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
211 ok = ok && GetGLAttribute(SDL_GL_CONTEXT_FLAGS, nContextFlags);
212 ok = ok && GetGLAttribute(SDL_GL_CONTEXT_PROFILE_MASK, nProfileMask);
213 if( !ok ) {
214 printf("SDL: Error retrieving GL context version information: %s\n", SDL_GetError());
215 SDL_GL_DeleteContext(sdl_glc);
216 return nullptr;
217 }
219 if( 0 != ( nProfileMask & SDL_GL_CONTEXT_PROFILE_CORE ) ) {
220 profileMask |= render::gl::GLProfileMask::core;
221 } else if( 0 != ( nProfileMask & SDL_GL_CONTEXT_PROFILE_COMPATIBILITY ) ) {
223 } else if( 0 != ( nProfileMask & SDL_GL_CONTEXT_PROFILE_ES ) ) {
224 profileMask |= render::gl::GLProfileMask::es;
225 }
227 if( 0 != ( nContextFlags & SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG ) ) {
229 }
230 if( 0 != ( nContextFlags & SDL_GL_CONTEXT_DEBUG_FLAG ) ) {
232 }
233 if( 0 != ( nContextFlags & SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG ) ) {
235 }
236 GLVersionNumber glv = GLVersionNumber::create(gl_version_cstr);
237 render::gl::GLProfile glp_out(glv, profileMask);
238 if( verbose ) {
239 render::gl::GLProfile glp_out0(jau::util::VersionNumber(major, minor, 0), profileMask);
240 printf("Surface::createContext.2: GLProfile (query) %s\n", glp_out0.toString().c_str());
241 printf("Surface::createContext.2: GLProfile (version) %s\n", glp_out.toString().c_str());
242 }
243 return render::gl::GLContext::create(surface, (handle_t)sdl_glc, std::move(glp_out), ctxFlags, gl_version_cstr); // current!
244}
245
246
247bool Window::surfaceSwap() noexcept {
248 if( isValid() ) {
249 SDL_GL_SwapWindow(reinterpret_cast<SDL_Window*>(windowHandle())); // NOLINT
250 return true;
251 }
252 return false;
253}
254
255//
256//
257//
258
259thread_local render::gl::GLContext* render::gl::GLContext::m_current = nullptr;
260
262 SDL_GLContext sdl_glc = reinterpret_cast<SDL_GLContext>(context()); // NOLINT
263 if( sdl_glc ) {
264 if( isCurrent() ) {
266 }
267 SDL_GL_DeleteContext(sdl_glc);
268 }
270}
271
272bool gamp::render::gl::GLContext::makeCurrentImpl(const gamp::wt::SurfaceSRef& surface, gamp::handle_t context) noexcept {
273 if( !surface || !surface->isValid() || !context) {
274 printf("SDL: Error GLContext::makeCurrent: Invalid surface/context: surface %s, context %p\n",
275 surface ? surface->toString().c_str() : "nil", (void*)context); // NOLINT
276 return false;
277 }
278 SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(surface->surfaceHandle()); // NOLINT
279 SDL_GLContext sdl_glc = reinterpret_cast<SDL_GLContext>(context); // NOLINT
280 if (0 != SDL_GL_MakeCurrent(sdl_win, sdl_glc)) {
281 return false;
282 }
283 return true;
284}
285
286void gamp::render::gl::GLContext::releaseContextImpl(const gamp::wt::SurfaceSRef& surface) noexcept {
287 if( !surface || !surface->isValid() ) {
288 printf("SDL: Error GLContext::release: Invalid surface: surface %s\n",
289 surface ? surface->toString().c_str() : "nil");
290 return;
291 }
292 SDL_Window* sdl_win = reinterpret_cast<SDL_Window*>(surface->surfaceHandle()); // NOLINT
293 SDL_GL_MakeCurrent(sdl_win, nullptr);
294}
constexpr gamp::handle_t context() const noexcept
Specifies the render profile.
constexpr const jau::util::VersionNumber & version() const noexcept
Specifies a set of OpenGL capabilities.
constexpr int stencilBits() const noexcept
Returns the number of stencil buffer bits.
constexpr int accumGreenBits() const noexcept
Returns the number of bits for the accumulation buffer's green component.
static const jau::type_info & GLSignature() noexcept
constexpr int accumBlueBits() const noexcept
Returns the number of bits for the accumulation buffer's blue component.
constexpr GLCapabilities & setMultiSamplesCount(int v) noexcept
Defaults to 0, i.e.
constexpr int depthBits() const noexcept
Returns the number of depth buffer bits.
constexpr int multiSamplesCount() const noexcept
Returns the number of sample buffers to be allocated if sample buffers are enabled,...
constexpr int accumAlphaBits() const noexcept
Returns the number of bits for the accumulation buffer's alpha component.
constexpr bool stereo() const noexcept
Returns whether stereo is requested, available or chosen.
constexpr GLCapabilities & setDoubleBuffered(bool enable) noexcept
Enables or disables double buffering.
constexpr GLCapabilities & setHardwareAccelerated(bool enable) noexcept
Enables or disables hardware acceleration.
constexpr int accumRedBits() const noexcept
Returns the number of bits for the accumulation buffer's red component.
constexpr bool doubleBuffered() const noexcept
Returns whether double-buffering is requested, available or chosen.
constexpr GLCapabilities & setStereo(bool enable) noexcept
Enables or disables stereo viewing.
constexpr bool hasMultiSamples() const noexcept
Sets the desired extension for full-scene antialiasing (FSAA), default is DEFAULT_SAMPLE_EXTENSION.
constexpr bool hardwareAccelerated() const noexcept
Returns whether hardware acceleration is requested, available or chosen.
OpenGL Rendering Context.
static RenderContextPtr create(gamp::handle_t context, GLProfile &&profile, RenderContextFlags contextFlags, const char *gl_version_cstr) noexcept
Create a new instance of a non-current context.
bool isCurrent() const noexcept
void dispose() noexcept override
void disposedNotify() override
void releaseContext() noexcept override
Release this context (used for OpenGL, but a NOP on Vulkan)
Specifies the OpenGL profile.
Definition GLContext.hpp:42
std::string toString() const override
constexpr bool isGLES() const noexcept
Indicates whether this profile is capable of GLES.
static const GLProfile & downcast(const RenderProfile &rp)
Downcast dereferenced given const RenderProfile& to const GLProfile&, throws exception if signature d...
static const jau::type_info & GLSignature() noexcept
A class for storing and comparing OpenGL version numbers.
static GLVersionNumber create(const std::string &versionString) noexcept
Specifies a set of capabilities that a window's rendering context must support, such as color depth p...
constexpr int redBits() const noexcept
Returns the number of bits for the color buffer's red component.
constexpr int greenBits() const noexcept
Returns the number of bits for the color buffer's green component.
constexpr int blueBits() const noexcept
Returns the number of bits for the color buffer's blue component.
constexpr int alphaBits() const noexcept
Returns the number of bits for the color buffer's alpha component.
static void setCaps(handle_t surface_handle, const Capabilities &requested) noexcept
bool createContext(const gamp::render::RenderProfile &profile, const gamp::render::RenderContextFlags &contextFlags)
Definition Surface.hpp:117
constexpr handle_t windowHandle() const noexcept
Returns the handle to the surface for this NativeSurface.
Definition Window.hpp:264
bool isValid() const noexcept override
Definition Window.hpp:265
bool surfaceSwap() noexcept override
Provide a mechanism to utilize custom (pre-) swap surface code.
Simple version number class containing a version number either being defined explicit or derived from...
constexpr int major() const noexcept
static bool SetGLAttribute(SDL_GLattr attr, int value) noexcept
static bool GetGLAttribute(SDL_GLattr attr, int &value) noexcept
constexpr bool value(const Bool rhs) noexcept
constexpr bool is_set(const E mask, const E bits) noexcept
GLProfileMask
OpenGL profile-mask bits.
std::unique_ptr< GLCapabilities > GLCapabilitiesPtr
@ compat
Desktop compatibility profile.
@ core
Desktop core profile.
std::unique_ptr< RenderContext > RenderContextPtr
RenderContextFlags
OpenGL context flags.
@ verbose
Verbose operations (debugging).
std::shared_ptr< Surface > SurfaceSRef
std::unique_ptr< Capabilities > CapabilitiesPtr
uintptr_t handle_t
A native handle type, big enough to store a pointer.
Definition GampTypes.hpp:52
std::string_view to_string(const math_error_t v) noexcept
Returns std::string_view representation of math_error_t.
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
Definition PTS.hpp:24
Author: Sven Gothel sgothel@jausoft.com Copyright Gothel Software e.K.
Definition enum_util.hpp:65
int printf(const char *format,...)
Operating Systems predefined macros.