1 | #ifndef ENGINE_CLIENT_BACKEND_SDL_H |
2 | #define ENGINE_CLIENT_BACKEND_SDL_H |
3 | |
4 | #include <SDL_video.h> |
5 | |
6 | #include <base/detect.h> |
7 | |
8 | #include <engine/graphics.h> |
9 | |
10 | #include <engine/client/graphics_threaded.h> |
11 | |
12 | #include <engine/client/backend/backend_base.h> |
13 | |
14 | #include <atomic> |
15 | #include <condition_variable> |
16 | #include <cstddef> |
17 | #include <cstdint> |
18 | #include <mutex> |
19 | #include <vector> |
20 | |
21 | #if defined(CONF_PLATFORM_MACOS) |
22 | #include <objc/objc-runtime.h> |
23 | |
24 | class CAutoreleasePool |
25 | { |
26 | private: |
27 | id m_Pool; |
28 | |
29 | public: |
30 | CAutoreleasePool() |
31 | { |
32 | Class NSAutoreleasePoolClass = (Class)objc_getClass("NSAutoreleasePool" ); |
33 | m_Pool = class_createInstance(NSAutoreleasePoolClass, 0); |
34 | SEL selector = sel_registerName("init" ); |
35 | ((id(*)(id, SEL))objc_msgSend)(m_Pool, selector); |
36 | } |
37 | |
38 | ~CAutoreleasePool() |
39 | { |
40 | SEL selector = sel_registerName("drain" ); |
41 | ((id(*)(id, SEL))objc_msgSend)(m_Pool, selector); |
42 | } |
43 | }; |
44 | #endif |
45 | |
46 | // basic threaded backend, abstract, missing init and shutdown functions |
47 | class CGraphicsBackend_Threaded : public IGraphicsBackend |
48 | { |
49 | private: |
50 | TTranslateFunc m_TranslateFunc; |
51 | SGfxWarningContainer m_Warning; |
52 | |
53 | public: |
54 | // constructed on the main thread, the rest of the functions is run on the render thread |
55 | class ICommandProcessor |
56 | { |
57 | public: |
58 | virtual ~ICommandProcessor() = default; |
59 | virtual void RunBuffer(CCommandBuffer *pBuffer) = 0; |
60 | |
61 | virtual const SGfxErrorContainer &GetError() const = 0; |
62 | virtual void ErroneousCleanup() = 0; |
63 | |
64 | virtual const SGfxWarningContainer &GetWarning() const = 0; |
65 | }; |
66 | |
67 | CGraphicsBackend_Threaded(TTranslateFunc &&TranslateFunc); |
68 | |
69 | void RunBuffer(CCommandBuffer *pBuffer) override; |
70 | void RunBufferSingleThreadedUnsafe(CCommandBuffer *pBuffer) override; |
71 | bool IsIdle() const override; |
72 | void WaitForIdle() override; |
73 | |
74 | void ProcessError(const SGfxErrorContainer &Error); |
75 | |
76 | protected: |
77 | void StartProcessor(ICommandProcessor *pProcessor); |
78 | void StopProcessor(); |
79 | |
80 | bool HasWarning() |
81 | { |
82 | return m_Warning.m_WarningType != GFX_WARNING_TYPE_NONE; |
83 | } |
84 | |
85 | private: |
86 | ICommandProcessor *m_pProcessor; |
87 | std::mutex m_BufferSwapMutex; |
88 | std::condition_variable m_BufferSwapCond; |
89 | CCommandBuffer *m_pBuffer; |
90 | std::atomic_bool m_Shutdown; |
91 | bool m_Started = false; |
92 | std::atomic_bool m_BufferInProcess; |
93 | void *m_pThread; |
94 | |
95 | static void ThreadFunc(void *pUser); |
96 | |
97 | public: |
98 | bool GetWarning(std::vector<std::string> &WarningStrings) override; |
99 | }; |
100 | |
101 | // takes care of implementation independent operations |
102 | class CCommandProcessorFragment_General |
103 | { |
104 | void Cmd_Nop(); |
105 | void Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand); |
106 | |
107 | public: |
108 | bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand); |
109 | }; |
110 | |
111 | struct SBackendCapabilites |
112 | { |
113 | bool m_TileBuffering; |
114 | bool m_QuadBuffering; |
115 | bool m_TextBuffering; |
116 | bool m_QuadContainerBuffering; |
117 | |
118 | bool m_MipMapping; |
119 | bool m_NPOTTextures; |
120 | bool m_3DTextures; |
121 | bool m_2DArrayTextures; |
122 | bool m_2DArrayTexturesAsExtension; |
123 | bool m_ShaderSupport; |
124 | |
125 | // use quads as much as possible, even if the user config says otherwise |
126 | bool m_TrianglesAsQuads; |
127 | |
128 | int m_ContextMajor; |
129 | int m_ContextMinor; |
130 | int m_ContextPatch; |
131 | }; |
132 | |
133 | // takes care of sdl related commands |
134 | class CCommandProcessorFragment_SDL |
135 | { |
136 | // SDL stuff |
137 | SDL_Window *m_pWindow = nullptr; |
138 | SDL_GLContext m_GLContext = nullptr; |
139 | |
140 | public: |
141 | enum |
142 | { |
143 | CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_SDL, |
144 | CMD_SHUTDOWN, |
145 | }; |
146 | |
147 | struct SCommand_Init : public CCommandBuffer::SCommand |
148 | { |
149 | SCommand_Init() : |
150 | SCommand(CMD_INIT) {} |
151 | SDL_Window *m_pWindow; |
152 | SDL_GLContext m_GLContext; |
153 | }; |
154 | |
155 | struct SCommand_Shutdown : public CCommandBuffer::SCommand |
156 | { |
157 | SCommand_Shutdown() : |
158 | SCommand(CMD_SHUTDOWN) {} |
159 | }; |
160 | |
161 | private: |
162 | void Cmd_Init(const SCommand_Init *pCommand); |
163 | void Cmd_Shutdown(const SCommand_Shutdown *pCommand); |
164 | void Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand); |
165 | void Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand); |
166 | void Cmd_WindowCreateNtf(const CCommandBuffer::SCommand_WindowCreateNtf *pCommand); |
167 | void Cmd_WindowDestroyNtf(const CCommandBuffer::SCommand_WindowDestroyNtf *pCommand); |
168 | |
169 | public: |
170 | CCommandProcessorFragment_SDL(); |
171 | |
172 | bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand); |
173 | }; |
174 | |
175 | // command processor implementation, uses the fragments to combine into one processor |
176 | class CCommandProcessor_SDL_GL : public CGraphicsBackend_Threaded::ICommandProcessor |
177 | { |
178 | CCommandProcessorFragment_GLBase *m_pGLBackend; |
179 | CCommandProcessorFragment_SDL m_SDL; |
180 | CCommandProcessorFragment_General m_General; |
181 | |
182 | EBackendType m_BackendType; |
183 | |
184 | SGfxErrorContainer m_Error; |
185 | SGfxWarningContainer m_Warning; |
186 | |
187 | public: |
188 | CCommandProcessor_SDL_GL(EBackendType BackendType, int GLMajor, int GLMinor, int GLPatch); |
189 | virtual ~CCommandProcessor_SDL_GL(); |
190 | void RunBuffer(CCommandBuffer *pBuffer) override; |
191 | |
192 | const SGfxErrorContainer &GetError() const override; |
193 | void ErroneousCleanup() override; |
194 | |
195 | const SGfxWarningContainer &GetWarning() const override; |
196 | |
197 | void HandleError(); |
198 | void HandleWarning(); |
199 | }; |
200 | |
201 | static constexpr size_t gs_GpuInfoStringSize = 256; |
202 | |
203 | // graphics backend implemented with SDL and the graphics library @see EBackendType |
204 | class CGraphicsBackend_SDL_GL : public CGraphicsBackend_Threaded |
205 | { |
206 | SDL_Window *m_pWindow = nullptr; |
207 | SDL_GLContext m_GLContext = nullptr; |
208 | ICommandProcessor *m_pProcessor = nullptr; |
209 | std::atomic<uint64_t> m_TextureMemoryUsage{0}; |
210 | std::atomic<uint64_t> m_BufferMemoryUsage{0}; |
211 | std::atomic<uint64_t> m_StreamMemoryUsage{0}; |
212 | std::atomic<uint64_t> m_StagingMemoryUsage{0}; |
213 | |
214 | TTwGraphicsGpuList m_GpuList; |
215 | |
216 | TGLBackendReadPresentedImageData m_ReadPresentedImageDataFunc; |
217 | |
218 | int m_NumScreens; |
219 | |
220 | SBackendCapabilites m_Capabilites; |
221 | |
222 | char m_aVendorString[gs_GpuInfoStringSize] = {}; |
223 | char m_aVersionString[gs_GpuInfoStringSize] = {}; |
224 | char m_aRendererString[gs_GpuInfoStringSize] = {}; |
225 | |
226 | EBackendType m_BackendType = BACKEND_TYPE_AUTO; |
227 | |
228 | char m_aErrorString[256]; |
229 | |
230 | static EBackendType DetectBackend(); |
231 | static void ClampDriverVersion(EBackendType BackendType); |
232 | |
233 | public: |
234 | CGraphicsBackend_SDL_GL(TTranslateFunc &&TranslateFunc); |
235 | int Init(const char *pName, int *pScreen, int *pWidth, int *pHeight, int *pRefreshRate, int *pFsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage) override; |
236 | int Shutdown() override; |
237 | |
238 | uint64_t TextureMemoryUsage() const override; |
239 | uint64_t BufferMemoryUsage() const override; |
240 | uint64_t StreamedMemoryUsage() const override; |
241 | uint64_t StagingMemoryUsage() const override; |
242 | |
243 | const TTwGraphicsGpuList &GetGpus() const override; |
244 | |
245 | int GetNumScreens() const override { return m_NumScreens; } |
246 | const char *GetScreenName(int Screen) const override; |
247 | |
248 | void GetVideoModes(CVideoMode *pModes, int MaxModes, int *pNumModes, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int ScreenId) override; |
249 | void GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int ScreenId) override; |
250 | |
251 | void Minimize() override; |
252 | void Maximize() override; |
253 | void SetWindowParams(int FullscreenMode, bool IsBorderless) override; |
254 | bool SetWindowScreen(int Index) override; |
255 | bool UpdateDisplayMode(int Index) override; |
256 | int GetWindowScreen() override; |
257 | int WindowActive() override; |
258 | int WindowOpen() override; |
259 | void SetWindowGrab(bool Grab) override; |
260 | bool ResizeWindow(int w, int h, int RefreshRate) override; |
261 | void GetViewportSize(int &w, int &h) override; |
262 | void NotifyWindow() override; |
263 | |
264 | void WindowDestroyNtf(uint32_t WindowId) override; |
265 | void WindowCreateNtf(uint32_t WindowId) override; |
266 | |
267 | bool GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType) override; |
268 | bool IsConfigModernAPI() override { return IsModernAPI(BackendType: m_BackendType); } |
269 | bool UseTrianglesAsQuad() override { return m_Capabilites.m_TrianglesAsQuads; } |
270 | bool HasTileBuffering() override { return m_Capabilites.m_TileBuffering; } |
271 | bool HasQuadBuffering() override { return m_Capabilites.m_QuadBuffering; } |
272 | bool HasTextBuffering() override { return m_Capabilites.m_TextBuffering; } |
273 | bool HasQuadContainerBuffering() override { return m_Capabilites.m_QuadContainerBuffering; } |
274 | bool Uses2DTextureArrays() override { return m_Capabilites.m_2DArrayTextures; } |
275 | bool HasTextureArraysSupport() override { return m_Capabilites.m_2DArrayTextures || m_Capabilites.m_3DTextures; } |
276 | |
277 | const char *GetErrorString() override |
278 | { |
279 | if(m_aErrorString[0] != '\0') |
280 | return m_aErrorString; |
281 | |
282 | return NULL; |
283 | } |
284 | |
285 | const char *GetVendorString() override |
286 | { |
287 | return m_aVendorString; |
288 | } |
289 | |
290 | const char *GetVersionString() override |
291 | { |
292 | return m_aVersionString; |
293 | } |
294 | |
295 | const char *GetRendererString() override |
296 | { |
297 | return m_aRendererString; |
298 | } |
299 | |
300 | TGLBackendReadPresentedImageData &GetReadPresentedImageDataFuncUnsafe() override; |
301 | |
302 | bool ShowMessageBox(unsigned Type, const char *pTitle, const char *pMsg) override; |
303 | |
304 | static bool IsModernAPI(EBackendType BackendType); |
305 | }; |
306 | |
307 | #endif // ENGINE_CLIENT_BACKEND_SDL_H |
308 | |