#ifndef ENGINE_CLIENT_BACKEND_SDL_H #define ENGINE_CLIENT_BACKEND_SDL_H #include "SDL.h" #include #include "graphics_defines.h" #ifdef CONF_BACKEND_OPENGL_ES3 #define BACKEND_GL_MODERN_API 1 #endif #include "blocklist_driver.h" #include "graphics_threaded.h" #include #include #if defined(CONF_PLATFORM_MACOS) #include class CAutoreleasePool { private: id m_Pool; public: CAutoreleasePool() { Class NSAutoreleasePoolClass = (Class)objc_getClass("NSAutoreleasePool"); m_Pool = class_createInstance(NSAutoreleasePoolClass, 0); SEL selector = sel_registerName("init"); ((id(*)(id, SEL))objc_msgSend)(m_Pool, selector); } ~CAutoreleasePool() { SEL selector = sel_registerName("drain"); ((id(*)(id, SEL))objc_msgSend)(m_Pool, selector); } }; #endif // basic threaded backend, abstract, missing init and shutdown functions class CGraphicsBackend_Threaded : public IGraphicsBackend { public: // constructed on the main thread, the rest of the functions is run on the render thread class ICommandProcessor { public: virtual ~ICommandProcessor() {} virtual void RunBuffer(CCommandBuffer *pBuffer) = 0; }; CGraphicsBackend_Threaded(); virtual void RunBuffer(CCommandBuffer *pBuffer); virtual bool IsIdle() const; virtual void WaitForIdle(); protected: void StartProcessor(ICommandProcessor *pProcessor); void StopProcessor(); private: ICommandProcessor *m_pProcessor; CCommandBuffer *volatile m_pBuffer; volatile bool m_Shutdown; CSemaphore m_Activity; CSemaphore m_BufferDone; void *m_pThread; static void ThreadFunc(void *pUser); }; // takes care of implementation independent operations class CCommandProcessorFragment_General { void Cmd_Nop(); void Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand); public: bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand); }; enum EBackendType { BACKEND_TYPE_OPENGL = 0, BACKEND_TYPE_OPENGL_ES, }; struct SBackendCapabilites { bool m_TileBuffering; bool m_QuadBuffering; bool m_TextBuffering; bool m_QuadContainerBuffering; bool m_MipMapping; bool m_NPOTTextures; bool m_3DTextures; bool m_2DArrayTextures; bool m_2DArrayTexturesAsExtension; bool m_ShaderSupport; int m_ContextMajor; int m_ContextMinor; int m_ContextPatch; }; class CGLSLProgram; class CGLSLTWProgram; class CGLSLPrimitiveProgram; class CGLSLQuadProgram; class CGLSLTileProgram; class CGLSLTextProgram; class CGLSLPrimitiveExProgram; class CGLSLSpriteMultipleProgram; // takes care of opengl related rendering class CCommandProcessorFragment_OpenGL { protected: struct CTexture { CTexture() : m_Tex(0), m_Tex2DArray(0), m_Sampler(0), m_Sampler2DArray(0), m_LastWrapMode(CCommandBuffer::WRAP_REPEAT), m_MemSize(0), m_Width(0), m_Height(0), m_RescaleCount(0), m_ResizeWidth(0), m_ResizeHeight(0) { } TWGLuint m_Tex; TWGLuint m_Tex2DArray; //or 3D texture as fallback TWGLuint m_Sampler; TWGLuint m_Sampler2DArray; //or 3D texture as fallback int m_LastWrapMode; int m_MemSize; int m_Width; int m_Height; int m_RescaleCount; float m_ResizeWidth; float m_ResizeHeight; }; std::vector m_Textures; std::atomic *m_pTextureMemoryUsage; TWGLint m_MaxTexSize; bool m_Has2DArrayTextures; bool m_Has2DArrayTexturesAsExtension; TWGLenum m_2DArrayTarget; bool m_Has3DTextures; bool m_HasMipMaps; bool m_HasNPOTTextures; bool m_HasShaders; int m_LastBlendMode; //avoid all possible opengl state changes bool m_LastClipEnable; int m_OpenGLTextureLodBIAS; bool m_IsOpenGLES; public: enum { CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_OPENGL, CMD_SHUTDOWN = CMD_INIT + 1, }; struct SCommand_Init : public CCommandBuffer::SCommand { SCommand_Init() : SCommand(CMD_INIT) {} class IStorage *m_pStorage; std::atomic *m_pTextureMemoryUsage; SBackendCapabilites *m_pCapabilities; int *m_pInitError; const char **m_pErrStringPtr; char *m_pVendorString; char *m_pVersionString; char *m_pRendererString; int m_RequestedMajor; int m_RequestedMinor; int m_RequestedPatch; EBackendType m_RequestedBackend; int m_GlewMajor; int m_GlewMinor; int m_GlewPatch; }; struct SCommand_Shutdown : public CCommandBuffer::SCommand { SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {} }; protected: bool IsTexturedState(const CCommandBuffer::SState &State); static bool Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight); void InitOpenGL(const SCommand_Init *pCommand); void SetState(const CCommandBuffer::SState &State, bool Use2DArrayTexture = false); virtual bool IsNewApi() { return false; } void DestroyTexture(int Slot); static int TexFormatToOpenGLFormat(int TexFormat); static int TexFormatToImageColorChannelCount(int TexFormat); static void *Resize(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData); virtual void Cmd_Init(const SCommand_Init *pCommand); virtual void Cmd_Shutdown(const SCommand_Shutdown *pCommand) {} virtual void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand); virtual void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand); virtual void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand); virtual void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand); virtual void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand); virtual void Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) {} virtual void Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand); virtual void Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand); virtual void Cmd_Finish(const CCommandBuffer::SCommand_Finish *pCommand); virtual void Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand) {} virtual void Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand) {} virtual void Cmd_UpdateBufferObject(const CCommandBuffer::SCommand_UpdateBufferObject *pCommand) {} virtual void Cmd_CopyBufferObject(const CCommandBuffer::SCommand_CopyBufferObject *pCommand) {} virtual void Cmd_DeleteBufferObject(const CCommandBuffer::SCommand_DeleteBufferObject *pCommand) {} virtual void Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand) {} virtual void Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand) {} virtual void Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand) {} virtual void Cmd_IndicesRequiredNumNotify(const CCommandBuffer::SCommand_IndicesRequiredNumNotify *pCommand) {} virtual void Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand) {} virtual void Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand) {} virtual void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand) {} virtual void Cmd_RenderQuadLayer(const CCommandBuffer::SCommand_RenderQuadLayer *pCommand) {} virtual void Cmd_RenderText(const CCommandBuffer::SCommand_RenderText *pCommand) {} virtual void Cmd_RenderTextStream(const CCommandBuffer::SCommand_RenderTextStream *pCommand) {} virtual void Cmd_RenderQuadContainer(const CCommandBuffer::SCommand_RenderQuadContainer *pCommand) {} virtual void Cmd_RenderQuadContainerEx(const CCommandBuffer::SCommand_RenderQuadContainerEx *pCommand) {} virtual void Cmd_RenderQuadContainerAsSpriteMultiple(const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *pCommand) {} public: CCommandProcessorFragment_OpenGL(); virtual ~CCommandProcessorFragment_OpenGL() = default; bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand); }; class CCommandProcessorFragment_OpenGL2 : public CCommandProcessorFragment_OpenGL { struct SBufferContainer { SBufferContainer() {} SBufferContainerInfo m_ContainerInfo; }; std::vector m_BufferContainers; GL_SVertexTex3D m_aStreamVertices[1024 * 4]; struct SBufferObject { SBufferObject(TWGLuint BufferObjectID) : m_BufferObjectID(BufferObjectID) { m_pData = NULL; m_DataSize = 0; } TWGLuint m_BufferObjectID; void *m_pData; size_t m_DataSize; }; std::vector m_BufferObjectIndices; #ifndef BACKEND_GL_MODERN_API bool DoAnalyzeStep(size_t StepN, size_t CheckCount, size_t VerticesCount, uint8_t aFakeTexture[], size_t SingleImageSize); bool IsTileMapAnalysisSucceeded(); void RenderBorderTileEmulation(SBufferContainer &BufferContainer, const CCommandBuffer::SState &State, const float *pColor, const char *pBuffOffset, unsigned int DrawNum, const float *pOffset, const float *pDir, int JumpIndex); void RenderBorderTileLineEmulation(SBufferContainer &BufferContainer, const CCommandBuffer::SState &State, const float *pColor, const char *pBuffOffset, unsigned int IndexDrawNum, unsigned int DrawNum, const float *pOffset, const float *pDir); #endif void UseProgram(CGLSLTWProgram *pProgram); protected: void SetState(const CCommandBuffer::SState &State, CGLSLTWProgram *pProgram, bool Use2DArrayTextures = false); #ifndef BACKEND_GL_MODERN_API void Cmd_Init(const SCommand_Init *pCommand) override; void Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) override; void Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand) override; void Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand) override; void Cmd_UpdateBufferObject(const CCommandBuffer::SCommand_UpdateBufferObject *pCommand) override; void Cmd_CopyBufferObject(const CCommandBuffer::SCommand_CopyBufferObject *pCommand) override; void Cmd_DeleteBufferObject(const CCommandBuffer::SCommand_DeleteBufferObject *pCommand) override; void Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand) override; void Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand) override; void Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand) override; void Cmd_IndicesRequiredNumNotify(const CCommandBuffer::SCommand_IndicesRequiredNumNotify *pCommand) override; void Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand) override; void Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand) override; void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand) override; #endif CGLSLTileProgram *m_pTileProgram; CGLSLTileProgram *m_pTileProgramTextured; CGLSLPrimitiveProgram *m_pPrimitive3DProgram; CGLSLPrimitiveProgram *m_pPrimitive3DProgramTextured; bool m_UseMultipleTextureUnits; TWGLint m_MaxTextureUnits; struct STextureBound { int m_TextureSlot; bool m_Is2DArray; }; std::vector m_TextureSlotBoundToUnit; //the texture index generated by loadtextureraw is stored in an index calculated by max texture units bool IsAndUpdateTextureSlotBound(int IDX, int Slot, bool Is2DArray = false); public: CCommandProcessorFragment_OpenGL2() : CCommandProcessorFragment_OpenGL(), m_UseMultipleTextureUnits(false) {} }; class CCommandProcessorFragment_OpenGL3 : public CCommandProcessorFragment_OpenGL2 { }; #define MAX_STREAM_BUFFER_COUNT 30 // takes care of opengl 3.3+ related rendering class CCommandProcessorFragment_OpenGL3_3 : public CCommandProcessorFragment_OpenGL3 { bool m_UsePreinitializedVertexBuffer; int m_MaxQuadsAtOnce; static const int m_MaxQuadsPossible = 256; CGLSLPrimitiveProgram *m_pPrimitiveProgram; CGLSLPrimitiveProgram *m_pPrimitiveProgramTextured; CGLSLTileProgram *m_pBorderTileProgram; CGLSLTileProgram *m_pBorderTileProgramTextured; CGLSLTileProgram *m_pBorderTileLineProgram; CGLSLTileProgram *m_pBorderTileLineProgramTextured; CGLSLQuadProgram *m_pQuadProgram; CGLSLQuadProgram *m_pQuadProgramTextured; CGLSLTextProgram *m_pTextProgram; CGLSLPrimitiveExProgram *m_pPrimitiveExProgram; CGLSLPrimitiveExProgram *m_pPrimitiveExProgramTextured; CGLSLPrimitiveExProgram *m_pPrimitiveExProgramRotationless; CGLSLPrimitiveExProgram *m_pPrimitiveExProgramTexturedRotationless; CGLSLSpriteMultipleProgram *m_pSpriteProgramMultiple; TWGLuint m_LastProgramID; TWGLuint m_PrimitiveDrawVertexID[MAX_STREAM_BUFFER_COUNT]; TWGLuint m_PrimitiveDrawVertexIDTex3D; TWGLuint m_PrimitiveDrawBufferID[MAX_STREAM_BUFFER_COUNT]; TWGLuint m_PrimitiveDrawBufferIDTex3D; TWGLuint m_LastIndexBufferBound[MAX_STREAM_BUFFER_COUNT]; int m_LastStreamBuffer; TWGLuint m_QuadDrawIndexBufferID; unsigned int m_CurrentIndicesInBuffer; void DestroyBufferContainer(int Index, bool DeleteBOs = true); void AppendIndices(unsigned int NewIndicesCount); struct SBufferContainer { SBufferContainer() : m_VertArrayID(0), m_LastIndexBufferBound(0) {} TWGLuint m_VertArrayID; TWGLuint m_LastIndexBufferBound; SBufferContainerInfo m_ContainerInfo; }; std::vector m_BufferContainers; std::vector m_BufferObjectIndices; CCommandBuffer::SColorf m_ClearColor; void InitPrimExProgram(CGLSLPrimitiveExProgram *pProgram, class CGLSLCompiler *pCompiler, class IStorage *pStorage, bool Textured, bool Rotationless); protected: static int TexFormatToNewOpenGLFormat(int TexFormat); bool IsNewApi() override { return true; } void UseProgram(CGLSLTWProgram *pProgram); void UploadStreamBufferData(unsigned int PrimitiveType, const void *pVertices, size_t VertSize, unsigned int PrimitiveCount, bool AsTex3D = false); void RenderText(const CCommandBuffer::SState &State, int DrawNum, int TextTextureIndex, int TextOutlineTextureIndex, int TextureSize, const float *pTextColor, const float *pTextOutlineColor); void Cmd_Init(const SCommand_Init *pCommand) override; void Cmd_Shutdown(const SCommand_Shutdown *pCommand) override; void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand) override; void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand) override; void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) override; void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) override; void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand) override; void Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) override; void Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand) override; void Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand) override; void Cmd_UpdateBufferObject(const CCommandBuffer::SCommand_UpdateBufferObject *pCommand) override; void Cmd_CopyBufferObject(const CCommandBuffer::SCommand_CopyBufferObject *pCommand) override; void Cmd_DeleteBufferObject(const CCommandBuffer::SCommand_DeleteBufferObject *pCommand) override; void Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand) override; void Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand) override; void Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand) override; void Cmd_IndicesRequiredNumNotify(const CCommandBuffer::SCommand_IndicesRequiredNumNotify *pCommand) override; void Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand) override; void Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand) override; void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand) override; void Cmd_RenderQuadLayer(const CCommandBuffer::SCommand_RenderQuadLayer *pCommand) override; void Cmd_RenderText(const CCommandBuffer::SCommand_RenderText *pCommand) override; void Cmd_RenderTextStream(const CCommandBuffer::SCommand_RenderTextStream *pCommand) override; void Cmd_RenderQuadContainer(const CCommandBuffer::SCommand_RenderQuadContainer *pCommand) override; void Cmd_RenderQuadContainerEx(const CCommandBuffer::SCommand_RenderQuadContainerEx *pCommand) override; void Cmd_RenderQuadContainerAsSpriteMultiple(const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *pCommand) override; public: CCommandProcessorFragment_OpenGL3_3() = default; }; // takes care of sdl related commands class CCommandProcessorFragment_SDL { // SDL stuff SDL_Window *m_pWindow; SDL_GLContext m_GLContext; public: enum { CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_SDL, CMD_SHUTDOWN, }; struct SCommand_Init : public CCommandBuffer::SCommand { SCommand_Init() : SCommand(CMD_INIT) {} SDL_Window *m_pWindow; SDL_GLContext m_GLContext; }; struct SCommand_Shutdown : public CCommandBuffer::SCommand { SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {} }; private: void Cmd_Init(const SCommand_Init *pCommand); void Cmd_Shutdown(const SCommand_Shutdown *pCommand); void Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand); void Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand); void Cmd_VideoModes(const CCommandBuffer::SCommand_VideoModes *pCommand); public: CCommandProcessorFragment_SDL(); bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand); }; // command processor impelementation, uses the fragments to combine into one processor class CCommandProcessor_SDL_OpenGL : public CGraphicsBackend_Threaded::ICommandProcessor { CCommandProcessorFragment_OpenGL *m_pOpenGL; CCommandProcessorFragment_SDL m_SDL; CCommandProcessorFragment_General m_General; EBackendType m_BackendType; public: CCommandProcessor_SDL_OpenGL(EBackendType BackendType, int OpenGLMajor, int OpenGLMinor, int OpenGLPatch); virtual ~CCommandProcessor_SDL_OpenGL(); virtual void RunBuffer(CCommandBuffer *pBuffer); }; static constexpr size_t gs_GPUInfoStringSize = 256; // graphics backend implemented with SDL and OpenGL class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded { SDL_Window *m_pWindow; SDL_GLContext m_GLContext; ICommandProcessor *m_pProcessor; std::atomic m_TextureMemoryUsage; int m_NumScreens; SBackendCapabilites m_Capabilites; char m_aVendorString[gs_GPUInfoStringSize] = {}; char m_aVersionString[gs_GPUInfoStringSize] = {}; char m_aRendererString[gs_GPUInfoStringSize] = {}; bool m_UseNewOpenGL; char m_aErrorString[256]; static EBackendType DetectBackend(); static void ClampDriverVersion(EBackendType BackendType); public: virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage); virtual int Shutdown(); virtual int MemoryUsage() const; virtual int GetNumScreens() const { return m_NumScreens; } virtual void Minimize(); virtual void Maximize(); virtual void SetWindowParams(int FullscreenMode, bool IsBorderless); virtual bool SetWindowScreen(int Index); virtual int GetWindowScreen(); virtual int WindowActive(); virtual int WindowOpen(); virtual void SetWindowGrab(bool Grab); virtual void ResizeWindow(int w, int h); virtual void GetViewportSize(int &w, int &h); virtual void NotifyWindow(); virtual bool IsNewOpenGL() { return m_UseNewOpenGL; } virtual bool HasTileBuffering() { return m_Capabilites.m_TileBuffering; } virtual bool HasQuadBuffering() { return m_Capabilites.m_QuadBuffering; } virtual bool HasTextBuffering() { return m_Capabilites.m_TextBuffering; } virtual bool HasQuadContainerBuffering() { return m_Capabilites.m_QuadContainerBuffering; } virtual bool Has2DTextureArrays() { return m_Capabilites.m_2DArrayTextures; } virtual const char *GetErrorString() { if(m_aErrorString[0] != '\0') return m_aErrorString; return NULL; } virtual const char *GetVendorString() { return m_aVendorString; } virtual const char *GetVersionString() { return m_aVersionString; } virtual const char *GetRendererString() { return m_aRendererString; } static bool IsModernAPI(EBackendType BackendType); }; #endif // ENGINE_CLIENT_BACKEND_SDL_H