nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : CAPI_GL_Util.h nuclear@0: Content : Utility header for OpenGL nuclear@0: Created : March 27, 2014 nuclear@0: Authors : Andrew Reisse, David Borel nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #ifndef INC_OVR_CAPI_GL_Util_h nuclear@0: #define INC_OVR_CAPI_GL_Util_h nuclear@0: nuclear@0: #include "../../OVR_CAPI.h" nuclear@0: #include "../../Kernel/OVR_Array.h" nuclear@0: #include "../../Kernel/OVR_Math.h" nuclear@0: #include "../../Kernel/OVR_RefCount.h" nuclear@0: #include "../../Kernel/OVR_String.h" nuclear@0: #include "../../Kernel/OVR_Types.h" nuclear@0: #include "../../Kernel/OVR_Log.h" nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: #define WIN32_LEAN_AND_MEAN nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: #if !defined(OVR_DISABLE_GLE) // By default we use the GLE module in order to link to OpenGL functions. However, if an external user nuclear@0: #include "CAPI_GLE.h" // wants to use an alternative mechanism to connect to OpenGL functions, they can #define OVR_DISABLE_GLE. nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: #if defined(OVR_OS_MAC) nuclear@0: #include nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: namespace OVR nuclear@0: { nuclear@0: // Get the shared LibOVR GLEContext instance. nuclear@0: class GLEContext; nuclear@0: GLEContext* GetGLEContext(); nuclear@0: } nuclear@0: nuclear@0: namespace OVR { namespace CAPI { namespace GL { nuclear@0: void InitGLExtensions(); nuclear@0: nuclear@0: nuclear@0: // Rendering primitive type used to render Model. nuclear@0: enum PrimitiveType nuclear@0: { nuclear@0: Prim_Triangles, nuclear@0: Prim_Lines, nuclear@0: Prim_TriangleStrip, nuclear@0: Prim_Unknown, nuclear@0: Prim_Count nuclear@0: }; nuclear@0: nuclear@0: // Types of shaders that can be stored together in a ShaderSet. nuclear@0: enum ShaderStage nuclear@0: { nuclear@0: Shader_Vertex = 0, nuclear@0: Shader_Fragment = 2, nuclear@0: Shader_Pixel = 2, nuclear@0: Shader_Count = 3, nuclear@0: }; nuclear@0: nuclear@0: enum MapFlags nuclear@0: { nuclear@0: Map_Discard = 1, nuclear@0: Map_Read = 2, // do not use nuclear@0: Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // Buffer types used for uploading geometry & constants. nuclear@0: enum BufferUsage nuclear@0: { nuclear@0: Buffer_Unknown = 0, nuclear@0: Buffer_Vertex = 1, nuclear@0: Buffer_Index = 2, nuclear@0: Buffer_Uniform = 4, nuclear@0: Buffer_TypeMask = 0xff, nuclear@0: Buffer_ReadOnly = 0x100, // Buffer must be created with Data(). nuclear@0: }; nuclear@0: nuclear@0: enum TextureFormat nuclear@0: { nuclear@0: Texture_RGBA = 0x0100, nuclear@0: Texture_Depth = 0x8000, nuclear@0: Texture_TypeMask = 0xff00, nuclear@0: Texture_SamplesMask = 0x00ff, nuclear@0: Texture_RenderTarget = 0x10000, nuclear@0: Texture_GenMipmaps = 0x20000, nuclear@0: }; nuclear@0: nuclear@0: // Texture sampling modes. nuclear@0: enum SampleMode nuclear@0: { nuclear@0: Sample_Linear = 0, nuclear@0: Sample_Nearest = 1, nuclear@0: Sample_Anisotropic = 2, nuclear@0: Sample_FilterMask = 3, nuclear@0: nuclear@0: Sample_Repeat = 0, nuclear@0: Sample_Clamp = 4, nuclear@0: Sample_ClampBorder = 8, // If unsupported Clamp is used instead. nuclear@0: Sample_AddressMask =12, nuclear@0: nuclear@0: Sample_Count =13, nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // Rendering parameters/pointers describing GL rendering setup. nuclear@0: struct RenderParams nuclear@0: { nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: HWND Window; nuclear@0: HDC DC; nuclear@0: #elif defined(OVR_OS_LINUX) nuclear@0: struct _XDisplay* Disp; nuclear@0: #endif nuclear@0: nuclear@0: ovrSizei BackBufferSize; nuclear@0: int Multisample; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: class Buffer : public RefCountBase nuclear@0: { nuclear@0: public: nuclear@0: RenderParams* pParams; nuclear@0: size_t Size; nuclear@0: GLenum Use; nuclear@0: GLuint GLBuffer; nuclear@0: nuclear@0: public: nuclear@0: Buffer(RenderParams* r); nuclear@0: ~Buffer(); nuclear@0: nuclear@0: GLuint GetBuffer() { return GLBuffer; } nuclear@0: nuclear@0: virtual size_t GetSize() { return Size; } nuclear@0: virtual void* Map(size_t start, size_t size, int flags = 0); nuclear@0: virtual bool Unmap(void *m); nuclear@0: virtual bool Data(int use, const void* buffer, size_t size); nuclear@0: }; nuclear@0: nuclear@0: class Texture : public RefCountBase nuclear@0: { nuclear@0: bool IsUserAllocated; nuclear@0: nuclear@0: public: nuclear@0: RenderParams* pParams; nuclear@0: GLuint TexId; nuclear@0: int Width, Height; nuclear@0: nuclear@0: Texture(RenderParams* rp, int w, int h); nuclear@0: ~Texture(); nuclear@0: nuclear@0: virtual int GetWidth() const { return Width; } nuclear@0: virtual int GetHeight() const { return Height; } nuclear@0: nuclear@0: virtual void SetSampleMode(int sm); nuclear@0: nuclear@0: // Updates texture to point to specified resources nuclear@0: // - used for slave rendering. nuclear@0: void UpdatePlaceholderTexture(GLuint texId, nuclear@0: const Sizei& textureSize); nuclear@0: nuclear@0: virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const; nuclear@0: }; nuclear@0: nuclear@0: // Base class for vertex and pixel shaders. Stored in ShaderSet. nuclear@0: class Shader : public RefCountBase nuclear@0: { nuclear@0: friend class ShaderSet; nuclear@0: nuclear@0: protected: nuclear@0: ShaderStage Stage; nuclear@0: nuclear@0: public: nuclear@0: Shader(ShaderStage s) : Stage(s) {} nuclear@0: virtual ~Shader() {} nuclear@0: nuclear@0: ShaderStage GetStage() const { return Stage; } nuclear@0: nuclear@0: virtual void Set(PrimitiveType) const { } nuclear@0: virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); } nuclear@0: nuclear@0: protected: nuclear@0: virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; } nuclear@0: virtual bool SetUniformBool(const char* name, int n, const bool* v) { OVR_UNUSED3(name, n, v); return false; } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: nuclear@0: // A group of shaders, one per stage. nuclear@0: // A ShaderSet is applied for rendering with a given fill. nuclear@0: class ShaderSet : public RefCountBase nuclear@0: { nuclear@0: protected: nuclear@0: Ptr Shaders[Shader_Count]; nuclear@0: nuclear@0: struct Uniform nuclear@0: { nuclear@0: String Name; nuclear@0: int Location, Size; nuclear@0: int Type; // currently number of floats in vector nuclear@0: nuclear@0: Uniform() : Name(), Location(0), Size(0), Type(0){} nuclear@0: }; nuclear@0: Array UniformInfo; nuclear@0: nuclear@0: public: nuclear@0: GLuint Prog; nuclear@0: GLint ProjLoc, ViewLoc; nuclear@0: GLint TexLoc[8]; nuclear@0: bool UsesLighting; nuclear@0: int LightingVer; nuclear@0: nuclear@0: ShaderSet(); nuclear@0: ~ShaderSet(); nuclear@0: nuclear@0: virtual void SetShader(Shader *s); nuclear@0: virtual void UnsetShader(int stage); nuclear@0: Shader* GetShader(int stage) { return Shaders[stage]; } nuclear@0: nuclear@0: virtual void Set(PrimitiveType prim) const nuclear@0: { nuclear@0: glUseProgram(Prog); nuclear@0: nuclear@0: for (int i = 0; i < Shader_Count; i++) nuclear@0: if (Shaders[i]) nuclear@0: Shaders[i]->Set(prim); nuclear@0: } nuclear@0: nuclear@0: // Set a uniform (other than the standard matrices). It is undefined whether the nuclear@0: // uniforms from one shader occupy the same space as those in other shaders nuclear@0: // (unless a buffer is used, then each buffer is independent). nuclear@0: virtual bool SetUniform(const char* name, int n, const float* v); nuclear@0: bool SetUniform1f(const char* name, float x) nuclear@0: { nuclear@0: const float v[] = {x}; nuclear@0: return SetUniform(name, 1, v); nuclear@0: } nuclear@0: bool SetUniform2f(const char* name, float x, float y) nuclear@0: { nuclear@0: const float v[] = {x,y}; nuclear@0: return SetUniform(name, 2, v); nuclear@0: } nuclear@0: bool SetUniform3f(const char* name, float x, float y, float z) nuclear@0: { nuclear@0: const float v[] = {x,y,z}; nuclear@0: return SetUniform(name, 3, v); nuclear@0: } nuclear@0: bool SetUniform4f(const char* name, float x, float y, float z, float w = 1) nuclear@0: { nuclear@0: const float v[] = {x,y,z,w}; nuclear@0: return SetUniform(name, 4, v); nuclear@0: } nuclear@0: nuclear@0: bool SetUniformv(const char* name, const Vector3f& v) nuclear@0: { nuclear@0: const float a[] = {v.x,v.y,v.z,1}; nuclear@0: return SetUniform(name, 4, a); nuclear@0: } nuclear@0: nuclear@0: virtual bool SetUniform4x4f(const char* name, const Matrix4f& m) nuclear@0: { nuclear@0: Matrix4f mt = m.Transposed(); nuclear@0: return SetUniform(name, 16, &mt.M[0][0]); nuclear@0: } nuclear@0: nuclear@0: virtual bool SetUniform3x3f(const char* name, const Matrix4f& m) nuclear@0: { nuclear@0: Matrix4f mt = m.Transposed(); nuclear@0: // float3x3 is actually stored the same way as float4x3, with the last items ignored by the code. nuclear@0: return SetUniform(name, 12, &mt.M[0][0]); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: protected: nuclear@0: GLint GetGLShader(Shader* s); nuclear@0: bool Link(); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // Fill combines a ShaderSet (vertex, pixel) with textures, if any. nuclear@0: // Every model has a fill. nuclear@0: class ShaderFill : public RefCountBase nuclear@0: { nuclear@0: Ptr Shaders; nuclear@0: Ptr Textures[8]; nuclear@0: void* InputLayout; // HACK this should be abstracted nuclear@0: nuclear@0: public: nuclear@0: ShaderFill(ShaderSet* sh) : Shaders(sh) { InputLayout = NULL; } nuclear@0: ShaderFill(ShaderSet& sh) : Shaders(sh) { InputLayout = NULL; } nuclear@0: nuclear@0: ShaderSet* GetShaders() const { return Shaders; } nuclear@0: void* GetInputLayout() const { return InputLayout; } nuclear@0: nuclear@0: virtual void Set(PrimitiveType prim = Prim_Unknown) const { nuclear@0: Shaders->Set(prim); nuclear@0: for(int i = 0; i < 8; i++) nuclear@0: { nuclear@0: if(Textures[i]) nuclear@0: { nuclear@0: Textures[i]->Set(i); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: struct DisplayId nuclear@0: { nuclear@0: // Windows nuclear@0: String MonitorName; // Monitor name for fullscreen mode nuclear@0: nuclear@0: // MacOS nuclear@0: long CgDisplayId; // CGDirectDisplayID nuclear@0: nuclear@0: DisplayId() : CgDisplayId(0) {} nuclear@0: DisplayId(long id) : CgDisplayId(id) {} nuclear@0: DisplayId(String m, long id=0) : MonitorName(m), CgDisplayId(id) {} nuclear@0: nuclear@0: operator bool () const nuclear@0: { nuclear@0: return MonitorName.GetLength() || CgDisplayId; nuclear@0: } nuclear@0: nuclear@0: bool operator== (const DisplayId& b) const nuclear@0: { nuclear@0: return CgDisplayId == b.CgDisplayId && nuclear@0: (strstr(MonitorName.ToCStr(), b.MonitorName.ToCStr()) || nuclear@0: strstr(b.MonitorName.ToCStr(), MonitorName.ToCStr())); nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: class ShaderBase : public Shader nuclear@0: { nuclear@0: public: nuclear@0: RenderParams* pParams; nuclear@0: unsigned char* UniformData; nuclear@0: int UniformsSize; nuclear@0: nuclear@0: enum VarType nuclear@0: { nuclear@0: VARTYPE_FLOAT, nuclear@0: VARTYPE_INT, nuclear@0: VARTYPE_BOOL, nuclear@0: }; nuclear@0: nuclear@0: struct Uniform nuclear@0: { nuclear@0: const char* Name; nuclear@0: VarType Type; nuclear@0: int Offset; nuclear@0: int Size; nuclear@0: }; nuclear@0: nuclear@0: const Uniform* UniformRefl; nuclear@0: size_t UniformReflSize; nuclear@0: nuclear@0: ShaderBase(RenderParams* rp, ShaderStage stage) : nuclear@0: Shader(stage), nuclear@0: pParams(rp), nuclear@0: UniformData(NULL), nuclear@0: UniformsSize(0), nuclear@0: UniformRefl(NULL), nuclear@0: UniformReflSize(0) nuclear@0: { nuclear@0: } nuclear@0: ~ShaderBase() nuclear@0: { nuclear@0: if (UniformData) nuclear@0: { nuclear@0: OVR_FREE(UniformData); nuclear@0: UniformData = NULL; nuclear@0: } nuclear@0: nuclear@0: // Do not need to free UniformRefl nuclear@0: UniformRefl = NULL; nuclear@0: } nuclear@0: nuclear@0: void InitUniforms(const Uniform* refl, size_t reflSize); nuclear@0: bool SetUniform(const char* name, int n, const float* v); nuclear@0: bool SetUniformBool(const char* name, int n, const bool* v); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: template nuclear@0: class ShaderImpl : public ShaderBase nuclear@0: { nuclear@0: friend class ShaderSet; nuclear@0: nuclear@0: public: nuclear@0: ShaderImpl(RenderParams* rp, void* s, size_t size, const Uniform* refl, size_t reflSize) nuclear@0: : ShaderBase(rp, SStage) nuclear@0: , GLShader(0) nuclear@0: { nuclear@0: bool success; nuclear@0: OVR_UNUSED(size); nuclear@0: success = Compile((const char*) s); nuclear@0: OVR_ASSERT(success); nuclear@0: OVR_UNUSED(success); nuclear@0: InitUniforms(refl, reflSize); nuclear@0: } nuclear@0: ~ShaderImpl() nuclear@0: { nuclear@0: if (GLShader) nuclear@0: { nuclear@0: glDeleteShader(GLShader); nuclear@0: GLShader = 0; nuclear@0: } nuclear@0: } nuclear@0: bool Compile(const char* src) nuclear@0: { nuclear@0: if (!GLShader) nuclear@0: GLShader = glCreateShader(GLStage()); nuclear@0: nuclear@0: glShaderSource(GLShader, 1, &src, 0); nuclear@0: glCompileShader(GLShader); nuclear@0: GLint r; nuclear@0: glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r); nuclear@0: if (!r) nuclear@0: { nuclear@0: GLchar msg[1024]; nuclear@0: glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg); nuclear@0: if (msg[0]) nuclear@0: OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg)); nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: GLenum GLStage() const nuclear@0: { nuclear@0: return SType; nuclear@0: } nuclear@0: nuclear@0: private: nuclear@0: GLuint GLShader; nuclear@0: }; nuclear@0: nuclear@0: typedef ShaderImpl VertexShader; nuclear@0: typedef ShaderImpl FragmentShader; nuclear@0: nuclear@0: nuclear@0: nuclear@0: // Allows us to have independent OpenGL contexts for our systems. nuclear@0: class Context nuclear@0: { nuclear@0: bool initialized; nuclear@0: bool ownsContext; nuclear@0: int incarnation; nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: HDC hdc; nuclear@0: HGLRC systemContext; nuclear@0: #elif defined(OVR_OS_LINUX) nuclear@0: Display *x11Display; nuclear@0: GLXDrawable x11Drawable; nuclear@0: GLXContext systemContext; nuclear@0: XVisualInfo x11Visual; nuclear@0: #elif defined(OVR_OS_MAC) nuclear@0: CGLContextObj systemContext; nuclear@0: #endif nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: Context(); nuclear@0: void InitFromCurrent(); nuclear@0: void CreateShared( Context & ctx ); nuclear@0: #if defined(OVR_OS_MAC) nuclear@0: void SetSurface( Context & ctx ); nuclear@0: #endif nuclear@0: void Destroy(); nuclear@0: void Bind(); nuclear@0: void Unbind(); nuclear@0: int GetIncarnation() const { return incarnation; } nuclear@0: nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // AutoContext nuclear@0: // nuclear@0: // Implements a common sequence of function calls with the Context class. nuclear@0: // See the AutoContext constructor below for what it does. nuclear@0: // nuclear@0: // Example usage: nuclear@0: // void SomeClass::Draw() nuclear@0: // { nuclear@0: // AutoContext autoContext(someClassContext); nuclear@0: // nuclear@0: // nuclear@0: // } nuclear@0: nuclear@0: struct AutoContext nuclear@0: { nuclear@0: Context savedCurrentContext; nuclear@0: Context& ourContext; nuclear@0: nuclear@0: AutoContext(Context& context) : nuclear@0: savedCurrentContext(), ourContext(context) nuclear@0: { nuclear@0: // We use a member savedCurrentContext which is initialized here, as opposed to having the user pass in a nuclear@0: // pre-existing Context (which the user could declare as a global or C++ member variable). We have to do this nuclear@0: // because if we were to use some pre-existing Context the app might delete its underlying GL context behind our back nuclear@0: // or associate it with another thread, which would cause our bind of it in our dtor to be a bad operation. nuclear@0: savedCurrentContext.InitFromCurrent(); nuclear@0: if(ourContext.GetIncarnation() == 0) // If not yet initialized... nuclear@0: ourContext.CreateShared(savedCurrentContext); nuclear@0: ourContext.Bind(); nuclear@0: #if defined(OVR_OS_MAC) // To consider: merge the following into the Bind function. nuclear@0: ourContext.SetSurface(savedCurrentContext); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: ~AutoContext() nuclear@0: { nuclear@0: savedCurrentContext.Bind(); nuclear@0: } nuclear@0: nuclear@0: OVR_NON_COPYABLE(AutoContext) nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: }}} // namespace OVR::CAPI::GL nuclear@0: nuclear@0: nuclear@0: #endif // INC_OVR_CAPI_GL_Util_h