summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/opengl/GfxDeviceGL.cpp')
-rw-r--r--Runtime/GfxDevice/opengl/GfxDeviceGL.cpp3079
1 files changed, 3079 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp b/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp
new file mode 100644
index 0000000..dfc2926
--- /dev/null
+++ b/Runtime/GfxDevice/opengl/GfxDeviceGL.cpp
@@ -0,0 +1,3079 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "UnityGL.h"
+#include "GLContext.h"
+#include "TexturesGL.h"
+#include "TimerQueryGL.h"
+#include "ARBVBO.h"
+#include "NullVBO.h"
+#include "CombinerGL.h"
+#include "External/shaderlab/Library/program.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "ChannelsGL.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/Allocator/LinearAllocator.h"
+#include "GpuProgramsGL.h"
+#include "ArbGpuProgamGL.h"
+#include "TextureIdMapGL.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#if UNITY_EDITOR && UNITY_WIN
+#include "GLWindow.h"
+#endif
+#include "Runtime/Misc/Plugins.h"
+
+
+void InvalidateChannelStateGL(); // ChannelsGL.cpp
+void InvalidateFPParamCacheGL(); // GpuProgramsGL.cpp
+bool IsActiveRenderTargetWithColorGL(); // RenderTextureGL.cpp
+
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+static const unsigned int kBlendModeGL[] = {
+ GL_ZERO, GL_ONE, GL_DST_COLOR, GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR,
+ GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
+};
+
+static const unsigned int kBlendFuncGL[] = {
+ GL_FUNC_ADD, GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX,
+};
+
+static const unsigned int kCmpFuncGL[] = {
+ GL_NEVER, GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
+};
+
+static const unsigned int kStencilOpGL[] = {
+ GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR,
+ GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+};
+
+static const GLenum kColorMatModeGL[kColorMatTypeCount] = { 0, GL_EMISSION, GL_AMBIENT_AND_DIFFUSE };
+
+static const unsigned long kTexDimTableGL[kTexDimCount] = {0, GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, 0};
+
+static GLenum kWrapModeGL[kTexWrapCount] = { GL_REPEAT, GL_CLAMP_TO_EDGE };
+
+static const GLint kMinFilterGL[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+static const unsigned int kFogModeGL[kFogModeCount] = { GL_LINEAR, GL_LINEAR, GL_EXP, GL_EXP2 };
+
+GLenum kTopologyGL[kPrimitiveTypeCount] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_QUADS, GL_LINES, GL_LINE_STRIP, GL_POINTS };
+
+
+GfxThreadableDevice* CreateGLGfxDevice();
+const unsigned long* GetGLTextureDimensionTable();
+unsigned int GetGLShaderImplTarget( ShaderImplType implType );
+void InvalidateActiveShaderStateGL( ShaderType type );
+
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+#include "Runtime/Misc/ReproductionLog.h"
+#define GFX_LOG(pattern) LogToScreenshotLog(__FUNCTION__+(" "+Format pattern))
+#else
+#define GFX_LOG(pattern)
+#endif
+
+
+static inline void ActivateTextureUnitGL (int unit)
+{
+ // When using the fixed-function texture, the correct assert is:
+ //Assert(unit >= 0 && unit < gGraphicsCaps.maxTexUnits);
+
+ Assert(unit >= 0 && unit < gGraphicsCaps.maxTexImageUnits);
+
+ GFX_LOG(("%d", unit));
+ if (gGraphicsCaps.maxTexImageUnits > 1)
+ OGL_CALL(glActiveTextureARB(GL_TEXTURE0_ARB + unit));
+}
+
+
+// --------------------------------------------------------------------------
+
+struct DeviceBlendStateGL : public DeviceBlendState
+{
+ UInt32 alphaFunc;
+};
+
+
+struct DeviceDepthStateGL : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGL : public DeviceStencilState
+{
+ GLenum stencilFuncFront;
+ GLenum stencilFailOpFront;
+ GLenum depthFailOpFront;
+ GLenum depthPassOpFront;
+ GLenum stencilFuncBack;
+ GLenum stencilFailOpBack;
+ GLenum depthFailOpBack;
+ GLenum depthPassOpBack;
+};
+
+
+
+typedef std::map< GfxBlendState, DeviceBlendStateGL, memcmp_less<GfxBlendState> > CachedBlendStates;
+typedef std::map< GfxDepthState, DeviceDepthStateGL, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGL, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+
+// --------------------------------------------------------------------------
+
+
+struct TextureUnitStateGL
+{
+ GLuint texID;
+ TextureDimension texDim;
+ unsigned int combColor, combAlpha;
+ Vector4f color;
+ TexGenMode texGen;
+ float bias;
+
+ void Invalidate();
+ void Verify( int unit );
+};
+
+void TextureUnitStateGL::Invalidate()
+{
+ texID = -1;
+ texDim = kTexDimUnknown;
+ combColor = combAlpha = 0xFFFFFFFF;
+ color.Set( -1, -1, -1, -1 );
+ texGen = kTexGenUnknown;
+ bias = 1.0e6f;
+}
+
+// --------------------------------------------------------------------------
+
+
+struct VertexLightStateGL
+{
+ int enabled; // 0/1 or -1 if unknown
+ float attenQuad;
+ float spotAngle;
+
+ void Invalidate();
+ void Verify( int unit );
+};
+
+void VertexLightStateGL::Invalidate()
+{
+ enabled = -1;
+ attenQuad = -1.0f;
+ spotAngle = -1000.0f; // -1.0f is used to flag non-spot lights, so it would be a valid value
+}
+
+// --------------------------------------------------------------------------
+
+
+struct DeviceStateGL
+{
+ GLuint m_SharedFBO;
+ GLuint m_HelperFBO;
+ int m_TextureIDGenerator;
+
+ int depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // GL modes
+ int blendOp, blendOpAlpha; // GL modes
+ CompareFunction alphaFunc;
+ float alphaValue;
+ int alphaToMask;
+
+ CullMode culling;
+ bool appBackfaceMode, userBackfaceMode;
+ bool wireframe;
+ NormalizationMode normalization;
+ int scissor;
+
+
+ int lighting;
+ Vector4f matDiffuse, matAmbient, matSpecular, matEmissive;
+ Vector4f ambient;
+ float matShininess;
+ ColorMaterialMode colorMaterial;
+
+ float offsetFactor, offsetUnits;
+
+ GpuProgram* activeGpuProgram[kShaderTypeCount];
+ const GpuProgramParameters* activeGpuProgramParams[kShaderTypeCount];
+ ShaderImplType shaderEnabledImpl[kShaderTypeCount];
+ int shaderEnabledID[kShaderTypeCount];
+
+ int separateSpecular;
+
+ int colorWriteMask; // ColorWriteMask combinations
+
+ TextureUnitStateGL textures[kMaxSupportedTextureUnits];
+ VertexLightStateGL vertexLights[kMaxSupportedVertexLights];
+
+ DynamicVBO* m_DynamicVBO;
+
+ int viewport[4];
+ int scissorRect[4];
+
+ GfxPrimitiveType m_ImmediateMode;
+ dynamic_array<UInt8> m_ImmediateVertices;
+
+ #if GFX_USES_VIEWPORT_OFFSET
+ float viewportOffsetX, viewportOffsetY;
+ #endif
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ DeviceBlendStateGL* m_CurrBlendState;
+ DeviceDepthStateGL* m_CurrDepthState;
+ const DeviceStencilStateGL* m_CurrStencilState;
+ int m_StencilRef;
+ DeviceRasterState* m_CurrRasterState;
+ Matrix4x4f m_WorldMatrix;
+
+ void Invalidate(BuiltinShaderParamValues& builtins);
+ void Verify();
+};
+
+static void ApplyBackfaceMode( const DeviceStateGL& state );
+
+
+void DeviceStateGL::Invalidate (BuiltinShaderParamValues& builtins)
+{
+ int i;
+
+ depthFunc = kFuncUnknown;
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any GL mode
+ blendOp = blendOpAlpha = -1; // won't match any GL mode
+ alphaFunc = kFuncUnknown;
+ alphaValue = -1.0f;
+ alphaToMask = -1; // unknown
+
+ culling = kCullUnknown;
+ normalization = kNormalizationUnknown;
+ scissor = -1;
+
+ lighting = -1;
+ matDiffuse.Set( -1, -1, -1, -1 );
+ matAmbient.Set( -1, -1, -1, -1 );
+ matSpecular.Set( -1, -1, -1, -1 );
+ matEmissive.Set( -1, -1, -1, -1 );
+ ambient.Set( -1, -1, -1, -1 );
+ matShininess = -1.0f;
+ colorMaterial = kColorMatUnknown;
+
+ offsetFactor = offsetUnits = -1000.0f;
+ for( i = 0; i < kShaderTypeCount; ++i )
+ {
+ activeGpuProgram[i] = NULL;
+ activeGpuProgramParams[i] = NULL;
+ shaderEnabledImpl[i] = kShaderImplUndefined;
+ shaderEnabledID[i] = -1;
+ }
+
+ separateSpecular = -1;
+
+ colorWriteMask = -1; // TBD ?
+
+ m_StencilRef = -1;
+
+ for( i = 0; i < kMaxSupportedTextureUnits; ++i )
+ textures[i].Invalidate();
+ for( i = 0; i < kMaxSupportedVertexLights; ++i )
+ vertexLights[i].Invalidate();
+ InvalidateChannelStateGL();
+ InvalidateFPParamCacheGL();
+
+ // make sure we aren't using any programs
+ if( gGraphicsCaps.gl.hasGLSL )
+ glUseProgramObjectARB( 0 );
+ glDisable( GL_VERTEX_PROGRAM_ARB );
+ glDisable( GL_FRAGMENT_PROGRAM_ARB );
+
+ // misc. state
+ glHint (GL_FOG_HINT, GL_NICEST);
+ glLightModelf (GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0F);
+
+ // Setup GL_EYE_PLANE for R coordinate to reasonable value. This has to be done initially,
+ // and NOT when some arbitrary matrix is already set up. So it's important
+ // to load identity matrix here and only then setup GL_EYE_PLANE value.
+ glLoadIdentity();
+ m_WorldMatrix.SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatView).SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatProj).SetIdentity();
+ builtins.GetWritableMatrixParam(kShaderMatViewProj).SetIdentity();
+ const float zplane[4] = {0.0f,0.0f,1.0f,0.0f};
+ for( i = 0; i < gGraphicsCaps.maxTexUnits; ++i )
+ {
+ ActivateTextureUnitGL (i);
+ glTexGenfv (GL_R, GL_EYE_PLANE, zplane);
+ }
+
+ // make sure backface mode is in synch
+ ApplyBackfaceMode( *this );
+
+ // make sure no vertex buffers are bound
+ UnbindVertexBuffersGL();
+
+ m_ImmediateMode = kPrimitiveTriangles;
+ m_ImmediateVertices.clear();
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ Verify();
+ #endif
+}
+
+#include "GfxDeviceGL.h"
+
+// GLContext.cpp
+#if UNITY_WIN
+bool CreateMasterGraphicsContext();
+#endif
+void CleanupMasterContext();
+
+
+GfxThreadableDevice* CreateGLGfxDevice()
+{
+ #if UNITY_WIN
+ SetMasterContextClassName(L"WindowGLClassName");
+ if( !CreateMasterGraphicsContext() )
+ return NULL;
+ #endif
+ GraphicsContextHandle context = GetMasterGraphicsContext();
+ if( !context.IsValid() )
+ return NULL;
+
+ SetMainGraphicsContext( context );
+ ActivateGraphicsContext (context, true);
+
+ InitGLExtensions();
+ gGraphicsCaps.InitGL();
+ GLAssert();
+
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLGfxDevice", "");
+}
+
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+ STATE.m_SharedFBO = 0;
+ STATE.m_HelperFBO = 0;
+ STATE.m_TextureIDGenerator = 0;
+ STATE.appBackfaceMode = false;
+ STATE.userBackfaceMode = false;
+ STATE.m_DynamicVBO = NULL;
+ STATE.wireframe = false;
+ #if GFX_USES_VIEWPORT_OFFSET
+ STATE.viewportOffsetX = 0;
+ STATE.viewportOffsetY = 0;
+ #endif
+
+ OnCreate();
+ InvalidateState();
+
+ m_Renderer = kGfxRendererOpenGL;
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+ // Should be safe to assume we can get 24 bits for the framebuffer on desktop.
+ m_FramebufferDepthFormat = kDepthFormat24;
+ m_IsThreadable = true;
+
+ STATE.m_CurrBlendState = NULL;
+ STATE.m_CurrDepthState = NULL;
+ STATE.m_CurrStencilState = NULL;
+ STATE.m_CurrRasterState = NULL;
+
+ STATE.m_WorldMatrix.SetIdentity();
+
+ m_MaxBufferedFrames = -1; // no limiting
+
+ STATE.viewport[0] = STATE.viewport[1] = STATE.viewport[2] = STATE.viewport[3] = 0;
+ STATE.scissorRect[0] = STATE.scissorRect[1] = STATE.scissorRect[2] = STATE.scissorRect[3] = 0;
+
+ extern RenderSurfaceBase* CreateBackBufferColorSurfaceGL();
+ SetBackBufferColorSurface(CreateBackBufferColorSurfaceGL());
+
+ extern RenderSurfaceBase* CreateBackBufferDepthSurfaceGL();
+ SetBackBufferDepthSurface(CreateBackBufferDepthSurfaceGL());
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+#if !ENABLE_GFXDEVICE_REMOTE_PROCESS_WORKER
+ PluginsSetGraphicsDevice (NULL, kGfxRendererOpenGL, kGfxDeviceEventShutdown);
+#endif
+ if (STATE.m_SharedFBO)
+ glDeleteFramebuffersEXT (1, &STATE.m_SharedFBO);
+ if (STATE.m_HelperFBO)
+ glDeleteFramebuffersEXT (1, &STATE.m_HelperFBO);
+ delete STATE.m_DynamicVBO;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+
+ #if UNITY_WIN
+ CleanupMasterContext();
+ #endif
+ CleanupGLExtensions();
+}
+
+void GFX_GL_IMPL::OnDeviceCreated (bool callingFromRenderThread)
+{
+ // needs to activate graphics context on both main & render threads
+ ActivateGraphicsContext (GetMainGraphicsContext(), true);
+}
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ m_FogParams.Invalidate();
+ STATE.Invalidate(m_BuiltinParamValues);
+
+ // Disable fog initially. At least on Mac/Intel, initial state seems to be off
+ // for fixed function (correct), but on for fragment/vertex programs (incorrect).
+ glFogf( GL_FOG_DENSITY, 0.0f );
+ glDisable( GL_FOG );
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+}
+
+
+const int kFPParamCacheSize = 32; // don't make this larger than 32 (won't fit into s_FPParamCacheValid mask)
+extern UInt32 s_FPParamCacheValid;
+extern Vector4f s_FPParamCache[kFPParamCacheSize];
+
+
+#define SET_LOCAL_MATRIX_PARAM( type, index, ptr ) \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+0, ptr[0], ptr[4], ptr[8], ptr[12] )); \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+1, ptr[1], ptr[5], ptr[9], ptr[13] )); \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+2, ptr[2], ptr[6], ptr[10], ptr[14] )); \
+ OGL_CALL(glProgramLocalParameter4fARB( type, index+3, ptr[3], ptr[7], ptr[11], ptr[15] )); \
+
+
+#define SET_ENV_MATRIX_PARAM( type, index, ptr ) \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+0, ptr[0], ptr[4], ptr[8], ptr[12] )); \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+1, ptr[1], ptr[5], ptr[9], ptr[13] )); \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+2, ptr[2], ptr[6], ptr[10], ptr[14] )); \
+ OGL_CALL(glProgramEnvParameter4fARB( type, index+3, ptr[3], ptr[7], ptr[11], ptr[15] )); \
+
+
+static void GLSetShaderMatrixConstant (ShaderImplType type, int index, int rows, int cols, const float* ptr)
+{
+ if (type == kShaderImplVertex)
+ {
+ SET_LOCAL_MATRIX_PARAM(GL_VERTEX_PROGRAM_ARB, index, ptr);
+ }
+ else if (type == kShaderImplFragment)
+ {
+ if (gGraphicsCaps.gl.cacheFPParamsWithEnvs)
+ {
+ // don't care here about cache
+ UInt32 mask0 = index+0 < kFPParamCacheSize ? 1 << (index+0) : 0;
+ UInt32 mask1 = index+1 < kFPParamCacheSize ? 1 << (index+1) : 0;
+ UInt32 mask2 = index+2 < kFPParamCacheSize ? 1 << (index+2) : 0;
+ UInt32 mask3 = index+3 < kFPParamCacheSize ? 1 << (index+3) : 0;
+
+ s_FPParamCacheValid &= ~mask0;
+ s_FPParamCacheValid &= ~mask1;
+ s_FPParamCacheValid &= ~mask2;
+ s_FPParamCacheValid &= ~mask3;
+
+ SET_ENV_MATRIX_PARAM(GL_FRAGMENT_PROGRAM_ARB, index, ptr);
+ }
+ else
+ {
+ SET_LOCAL_MATRIX_PARAM(GL_FRAGMENT_PROGRAM_ARB, index, ptr);
+ }
+ }
+ else if (type == kShaderImplBoth)
+ {
+ Assert(rows == 4 && cols == 4);
+ OGL_CALL(glUniformMatrix4fvARB(index, 1, false, ptr));
+ }
+ else
+ {
+ AssertString("unknown shader type");
+ }
+}
+
+
+#undef SET_ENV_MATRIX_PARAM
+#undef SET_LOCAL_MATRIX_PARAM
+
+static void GLSetShaderVectorConstant (ShaderImplType shaderType, ShaderParamType type, int index, int dim, const Vector4f& val)
+{
+ if (shaderType == kShaderImplVertex)
+ {
+ OGL_CALL(glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, index, val.GetPtr()));
+ }
+ else if (shaderType == kShaderImplFragment)
+ {
+ if (gGraphicsCaps.gl.cacheFPParamsWithEnvs)
+ {
+ UInt32 mask = 1 << index;
+ if (index >= kFPParamCacheSize)
+ {
+ OGL_CALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, val.GetPtr()));
+ }
+ else
+ {
+ if( !(s_FPParamCacheValid & mask) || s_FPParamCache[index] != val )
+ {
+ OGL_CALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, val.GetPtr()));
+ s_FPParamCache[index] = val;
+ }
+ s_FPParamCacheValid |= mask;
+ }
+ }
+ else
+ OGL_CALL(glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, index, val.GetPtr()));
+ }
+ else if (shaderType == kShaderImplBoth)
+ {
+ if (type == kShaderParamFloat)
+ {
+ switch (dim) {
+ case 1: OGL_CALL(glUniform1fvARB(index, 1, val.GetPtr())); break;
+ case 2: OGL_CALL(glUniform2fvARB(index, 1, val.GetPtr())); break;
+ case 3: OGL_CALL(glUniform3fvARB(index, 1, val.GetPtr())); break;
+ case 4: OGL_CALL(glUniform4fvARB(index, 1, val.GetPtr())); break;
+ default: AssertString ("unknown uniform dimension"); break;
+ }
+ }
+ else
+ {
+ // In theory Uniform*f can also be used to load bool uniforms, in practice
+ // some drivers don't like that. So load both integers and bools via *i functions.
+ int ival[4] = {val.x, val.y, val.z, val.w};
+ switch (dim) {
+ case 1: OGL_CALL(glUniform1ivARB(index, 1, ival)); break;
+ case 2: OGL_CALL(glUniform2ivARB(index, 1, ival)); break;
+ case 3: OGL_CALL(glUniform3ivARB(index, 1, ival)); break;
+ case 4: OGL_CALL(glUniform4ivARB(index, 1, ival)); break;
+ default: AssertString ("unknown uniform dimension"); break;
+ }
+ }
+ }
+ else
+ {
+ AssertString("unknown shader type");
+ }
+}
+
+
+struct SetValuesFunctorGL
+{
+ SetValuesFunctorGL(GfxDevice& device, const ShaderImplType* shaderEnabledImpl) : m_Device(device), m_ShaderEnabledImpl(shaderEnabledImpl) { }
+ GfxDevice& m_Device;
+ const ShaderImplType* m_ShaderEnabledImpl;
+ void SetVectorVal (ShaderType shaderType, ShaderParamType type, int index, const float* ptr, int cols, const GpuProgramParameters& params, int cbIndex)
+ {
+ GLSetShaderVectorConstant (m_ShaderEnabledImpl[shaderType], type, index, cols, *(const Vector4f*)ptr);
+ }
+ void SetMatrixVal (ShaderType shaderType, int index, const Matrix4x4f* ptr, int rows, const GpuProgramParameters& params, int cbIndex)
+ {
+ GLSetShaderMatrixConstant (m_ShaderEnabledImpl[shaderType], index, rows, 4, (const float*)ptr);
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ UInt32 texIndex = UInt32(index) >> 24;
+ m_Device.SetTexture (shaderType, texIndex, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+void GFX_GL_IMPL::BeforeDrawCall( bool immediateMode )
+{
+ GFX_LOG(("%d",immediateMode));
+
+ // Special Matrix parameters (defined by Unity as built-ins
+ // but not present in GL default state)
+ const BuiltinShaderParamIndices& paramsVS = *m_BuiltinParamIndices[kShaderVertex];
+ const BuiltinShaderParamIndices& paramsFS = *m_BuiltinParamIndices[kShaderFragment];
+
+ // M matrix
+ if (paramsVS.mat[kShaderInstanceMatM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsVS.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.m_WorldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderVertex], matParam.gpuIndex, matParam.rows, matParam.cols, mat.GetPtr());
+ }
+ if (paramsFS.mat[kShaderInstanceMatM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsFS.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.m_WorldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderFragment], matParam.gpuIndex, matParam.rows, matParam.cols, mat.GetPtr());
+ }
+ // Inverse M matrix
+ if (paramsVS.mat[kShaderInstanceMatInvM].gpuIndex >= 0 || paramsFS.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ Matrix4x4f mat = STATE.m_WorldMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ if (paramsVS.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsVS.mat[kShaderInstanceMatInvM];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderVertex], matParam.gpuIndex, matParam.rows, matParam.cols, inverseMat.GetPtr());
+ }
+ if (paramsFS.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = paramsFS.mat[kShaderInstanceMatInvM];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLSetShaderMatrixConstant (STATE.shaderEnabledImpl[kShaderFragment], matParam.gpuIndex, matParam.rows, matParam.cols, inverseMat.GetPtr());
+ }
+ }
+
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ const BuiltinShaderParamIndices::VectorParamData& parVS = paramsVS.vec[i];
+ if (parVS.gpuIndex >= 0)
+ GLSetShaderVectorConstant (STATE.shaderEnabledImpl[kShaderVertex], kShaderParamFloat, parVS.gpuIndex, parVS.dim, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i));
+ const BuiltinShaderParamIndices::VectorParamData& parFS = paramsFS.vec[i];
+ if (parFS.gpuIndex >= 0)
+ GLSetShaderVectorConstant (STATE.shaderEnabledImpl[kShaderFragment], kShaderParamFloat, parFS.gpuIndex, parFS.dim, m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i));
+ }
+
+ SetValuesFunctorGL setValuesFunc(*this, STATE.shaderEnabledImpl);
+ ApplyMaterialPropertyBlockValues(m_MaterialProperties, STATE.activeGpuProgram, STATE.activeGpuProgramParams, setValuesFunc);
+}
+
+
+DeviceBlendState* GFX_GL_IMPL::CreateBlendState(const GfxBlendState& state)
+{
+
+ std::pair<CachedBlendStates::iterator, bool> result = STATE.m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendStateGL()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendStateGL& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(GfxBlendState));
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+ glstate.alphaFunc = kCmpFuncGL[state.alphaTest];
+
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GFX_GL_IMPL::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = STATE.m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateGL()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGL& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(GfxDepthState));
+ glstate.depthFunc = kCmpFuncGL[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GFX_GL_IMPL::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = STATE.m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateGL()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGL& st = result.first->second;
+ memcpy (&st.sourceState, &state, sizeof(state));
+ st.stencilFuncFront = kCmpFuncGL[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpGL[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpGL[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpGL[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncGL[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpGL[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpGL[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpGL[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+
+DeviceRasterState* GFX_GL_IMPL::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = STATE.m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(DeviceRasterState));
+
+ return &result.first->second;
+}
+
+void GFX_GL_IMPL::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendStateGL* devstate = (DeviceBlendStateGL*)state;
+
+ if (STATE.m_CurrBlendState == devstate && alphaRef == STATE.alphaValue)
+ return;
+
+ STATE.m_CurrBlendState = devstate;
+ if (!STATE.m_CurrBlendState)
+ return;
+
+ int mask = devstate->sourceState.renderTargetWriteMask;
+ if (!IsActiveRenderTargetWithColorGL())
+ mask = 0;
+ if( mask != STATE.colorWriteMask ) {
+ OGL_CALL(glColorMask( (mask & kColorWriteR) != 0, (mask & kColorWriteG) != 0, (mask & kColorWriteB) != 0, (mask & kColorWriteA) != 0 ));
+ STATE.colorWriteMask = mask;
+ }
+
+ const GfxBlendState& desc = state->sourceState;
+ const CompareFunction mode = state->sourceState.alphaTest;
+ const GLenum glsrc = kBlendModeGL[desc.srcBlend];
+ const GLenum gldst = kBlendModeGL[desc.dstBlend];
+ const GLenum glsrca = kBlendModeGL[desc.srcBlendAlpha];
+ const GLenum gldsta = kBlendModeGL[desc.dstBlendAlpha];
+ const GLenum glfunc = kBlendFuncGL[desc.blendOp];
+ const GLenum glfunca = kBlendFuncGL[desc.blendOpAlpha];
+ const bool blendDisabled = (glsrc == GL_ONE && gldst == GL_ZERO && glsrca == GL_ONE && gldsta == GL_ZERO);
+
+ GFX_LOG(("%d, %d, %d, %f", desc.srcBlend, desc.dstBlend, mode, alphaRef));
+
+ // alpha blending states
+
+ if( blendDisabled )
+ {
+ if( STATE.blending != 0 )
+ {
+ OGL_CALL(glDisable (GL_BLEND));
+ STATE.blending = 0;
+ }
+ }
+ else
+ {
+ if( glsrc != STATE.srcBlend || gldst != STATE.destBlend || glsrca != STATE.srcBlendAlpha || gldsta != STATE.destBlendAlpha )
+ {
+ if (gGraphicsCaps.hasSeparateAlphaBlend)
+ OGL_CALL(glBlendFuncSeparateEXT (glsrc, gldst, glsrca, gldsta));
+ else
+ OGL_CALL(glBlendFunc( glsrc, gldst ));
+ STATE.srcBlend = glsrc;
+ STATE.destBlend = gldst;
+ STATE.srcBlendAlpha = glsrca;
+ STATE.destBlendAlpha = gldsta;
+ }
+ if (glfunc != STATE.blendOp || glfunca != STATE.blendOpAlpha)
+ {
+ bool supports = true;
+ if( (glfunc == GL_FUNC_SUBTRACT || glfunc == GL_FUNC_REVERSE_SUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (glfunca == GL_FUNC_SUBTRACT || glfunca == GL_FUNC_REVERSE_SUBTRACT) && !gGraphicsCaps.hasBlendSub )
+ supports = false;
+ if( (glfunc == GL_MIN || glfunc == GL_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+ if( (glfunca == GL_MIN || glfunca == GL_MAX) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ if (gGraphicsCaps.hasSeparateAlphaBlend)
+ OGL_CALL(glBlendEquationSeparateEXT (glfunc, glfunca));
+ else
+ OGL_CALL(glBlendEquation (glfunc));
+ STATE.blendOp = glfunc;
+ STATE.blendOpAlpha = glfunca;
+ }
+ }
+ if( STATE.blending != 1 )
+ {
+ OGL_CALL(glEnable( GL_BLEND ));
+ STATE.blending = 1;
+ }
+ }
+
+ // alpha testing states
+#if UNITY_EDITOR // gles2.0 doesn't have FF alpha testing(only discard/clip on shader side), so disable on editor while emulating
+ bool skipAlphaTestFF = (gGraphicsCaps.IsEmulatingGLES20() && IsShaderActive(kShaderFragment));
+ // possible that vertex shader will be used with FF "frag shader" (like Transparent/vertexlit.shader),
+ // which will change alphatesting. So later on when real frag shaders come, we need to force disable alpha
+ // testing or enjoy nasty artefacts (like active alpha testing messing up the whole scene).
+ if ( skipAlphaTestFF && STATE.alphaFunc!=kFuncDisabled )
+ {
+ OGL_CALL(glDisable (GL_ALPHA_TEST));
+ STATE.alphaFunc = kFuncDisabled;
+ }
+
+ if ( !skipAlphaTestFF )
+ {
+#endif
+ if( mode != STATE.alphaFunc || alphaRef != STATE.alphaValue )
+ {
+ if( mode != kFuncDisabled )
+ {
+ OGL_CALL(glAlphaFunc( kCmpFuncGL[mode], alphaRef ));
+ OGL_CALL(glEnable( GL_ALPHA_TEST ));
+ }
+ else
+ {
+ OGL_CALL(glDisable (GL_ALPHA_TEST));
+ }
+
+ STATE.alphaFunc = mode;
+ STATE.alphaValue = alphaRef;
+ }
+#if UNITY_EDITOR
+ }
+#endif
+
+#if 0
+ int atomask = alphaToMask ? 1 : 0;
+ if( atomask != STATE.alphaToMask && gGraphicsCaps.hasMultiSample )
+ {
+ if( atomask )
+ OGL_CALL(glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE ));
+ else
+ OGL_CALL(glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE ));
+ STATE.alphaToMask = atomask;
+ }
+#endif
+}
+
+
+void GFX_GL_IMPL::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ STATE.m_CurrRasterState = NULL;
+ return;
+ }
+
+ STATE.m_CurrRasterState = devstate;
+
+ CullMode cull = devstate->sourceState.cullMode;
+ if( cull != STATE.culling )
+ {
+ switch (cull) {
+ case kCullOff:
+ OGL_CALL(glDisable (GL_CULL_FACE));
+ break;
+ case kCullFront:
+ OGL_CALL(glCullFace (GL_FRONT));
+ OGL_CALL(glEnable (GL_CULL_FACE));
+ break;
+ case kCullBack:
+ OGL_CALL(glCullFace (GL_BACK));
+ OGL_CALL(glEnable (GL_CULL_FACE));
+ break;
+ }
+ STATE.culling = cull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != STATE.offsetFactor || zUnits != STATE.offsetUnits )
+ {
+ OGL_CALL(glPolygonOffset( zFactor, zUnits ));
+ if( zFactor || zUnits )
+ OGL_CALL(glEnable (GL_POLYGON_OFFSET_FILL));
+ else
+ OGL_CALL(glDisable (GL_POLYGON_OFFSET_FILL));
+
+ STATE.offsetFactor = zFactor;
+ STATE.offsetUnits = zUnits;
+ }
+}
+
+
+void GFX_GL_IMPL::SetDepthState(const DeviceDepthState* state)
+{
+ DeviceDepthStateGL* devstate = (DeviceDepthStateGL*)state;
+ if (STATE.m_CurrDepthState == devstate)
+ return;
+
+ STATE.m_CurrDepthState = devstate;
+
+ if (!STATE.m_CurrDepthState)
+ return;
+
+ const CompareFunction testFunc = devstate->sourceState.depthFunc;
+ if( testFunc != STATE.depthFunc )
+ {
+ if( testFunc != kFuncDisabled ) {
+ OGL_CALL(glDepthFunc (kCmpFuncGL[testFunc]));
+ OGL_CALL(glEnable (GL_DEPTH_TEST));
+ } else {
+ OGL_CALL(glDisable (GL_DEPTH_TEST));
+ }
+
+ STATE.depthFunc = testFunc;
+ }
+
+ const int writeMode = devstate->sourceState.depthWrite ? GL_TRUE : GL_FALSE;
+ if( writeMode != STATE.depthWrite )
+ {
+ OGL_CALL(glDepthMask (writeMode));
+ STATE.depthWrite = writeMode;
+ }
+}
+
+void GFX_GL_IMPL::SetStencilState (const DeviceStencilState* state, int stencilRef)
+{
+ if (STATE.m_CurrStencilState == state && STATE.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateGL* st = static_cast<const DeviceStencilStateGL*>(state);
+ STATE.m_CurrStencilState = st;
+ if (!st)
+ return;
+
+ if (st->sourceState.stencilEnable)
+ glEnable (GL_STENCIL_TEST);
+ else
+ glDisable (GL_STENCIL_TEST);
+ if (gGraphicsCaps.hasTwoSidedStencil)
+ {
+ glStencilFuncSeparate (GL_FRONT, st->stencilFuncFront, stencilRef, st->sourceState.readMask);
+ glStencilOpSeparate (GL_FRONT, st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront);
+ glStencilFuncSeparate (GL_BACK, st->stencilFuncBack, stencilRef, st->sourceState.readMask);
+ glStencilOpSeparate (GL_BACK, st->stencilFailOpBack, st->depthFailOpBack, st->depthPassOpBack);
+ }
+ else
+ {
+ glStencilFunc (st->stencilFuncFront, stencilRef, st->sourceState.readMask);
+ glStencilOp (st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront);
+ }
+ glStencilMask (st->sourceState.writeMask);
+
+ STATE.m_StencilRef = stencilRef;
+}
+
+
+void GFX_GL_IMPL::SetSRGBWrite (bool enable)
+{
+ if (!gGraphicsCaps.hasSRGBReadWrite)
+ return;
+
+ if (enable)
+ OGL_CALL(glEnable(GL_FRAMEBUFFER_SRGB_EXT));
+ else
+ OGL_CALL(glDisable(GL_FRAMEBUFFER_SRGB_EXT));
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return gGraphicsCaps.hasSRGBReadWrite ?
+ glIsEnabled(GL_FRAMEBUFFER_SRGB_EXT):
+ false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ GFX_LOG(("%d, {%f, %f, %f, %f}, %f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil));
+
+ if (!IsActiveRenderTargetWithColorGL())
+ clearFlags &= ~kGfxClearColor;
+
+ // In OpenGL, clears are affected by color write mask and depth writing parameters.
+ // So make sure to set them!
+ GLbitfield flags = 0;
+ if (clearFlags & kGfxClearColor)
+ {
+ if (STATE.colorWriteMask != 15)
+ {
+ OGL_CALL(glColorMask( true, true, true, true ));
+ STATE.colorWriteMask = 15;
+ STATE.m_CurrBlendState = NULL;
+ }
+ flags |= GL_COLOR_BUFFER_BIT;
+ glClearColor (color[0], color[1], color[2], color[3]);
+ }
+ if (clearFlags & kGfxClearDepth)
+ {
+ OGL_CALL(glDepthMask (GL_TRUE));
+ STATE.depthWrite = GL_TRUE;
+ STATE.m_CurrDepthState = NULL;
+ flags |= GL_DEPTH_BUFFER_BIT;
+ glClearDepth (depth);
+ }
+ if (clearFlags & kGfxClearStencil)
+ {
+ //@TODO: need to set stencil writes on?
+ flags |= GL_STENCIL_BUFFER_BIT;
+ glClearStencil (stencil);
+ }
+ glClear (flags);
+}
+
+
+static void ApplyBackfaceMode( const DeviceStateGL& state )
+{
+ const bool bFlip = (state.appBackfaceMode != state.userBackfaceMode);
+
+ if( bFlip )
+ OGL_CALL(glFrontFace( GL_CCW ));
+ else
+ OGL_CALL(glFrontFace( GL_CW ));
+
+ if(state.culling == kCullUnknown || state.culling == kCullOff)
+ return;
+
+ DeviceRasterState* devstate = state.m_CurrRasterState;
+ if(!devstate)
+ return;
+
+ GfxDevice &device = GetRealGfxDevice();
+ device.SetRasterState(0);
+ device.SetRasterState(devstate);
+}
+
+
+void GFX_GL_IMPL::SetUserBackfaceMode( bool enable )
+{
+ GFX_LOG(("%d", enable));
+ if( STATE.userBackfaceMode == enable )
+ return;
+ STATE.userBackfaceMode = enable;
+ ApplyBackfaceMode( STATE );
+}
+
+
+void GFX_GL_IMPL::SetWireframe( bool wire )
+{
+ if( wire )
+ {
+ OGL_CALL(glEnable (GL_POLYGON_OFFSET_LINE));
+ OGL_CALL(glPolygonMode (GL_FRONT_AND_BACK, GL_LINE));
+ STATE.wireframe = true;
+ }
+ else
+ {
+ OGL_CALL(glDisable (GL_POLYGON_OFFSET_LINE));
+ OGL_CALL(glPolygonMode (GL_FRONT_AND_BACK, GL_FILL));
+ STATE.wireframe = false;
+ }
+}
+
+bool GFX_GL_IMPL::GetWireframe() const
+{
+ return STATE.wireframe;
+}
+
+
+
+void GFX_GL_IMPL::SetInvertProjectionMatrix (bool enable)
+{
+ Assert (!enable); // projection should never be flipped upside down on OpenGL
+}
+
+bool GFX_GL_IMPL::GetInvertProjectionMatrix() const
+{
+ return false;
+}
+
+#if GFX_USES_VIEWPORT_OFFSET
+void GFX_GL_IMPL::SetViewportOffset( float x, float y )
+{
+ GFX_LOG(("%f, %f", x, y));
+ STATE.viewportOffsetX = x;
+ STATE.viewportOffsetY = y;
+}
+
+void GFX_GL_IMPL::GetViewportOffset( float &x, float &y ) const
+{
+ x = STATE.viewportOffsetX;
+ y = STATE.viewportOffsetY;
+}
+#endif
+
+void GFX_GL_IMPL::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ GFX_LOG(("{%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f}",
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]));
+
+ Matrix4x4f& projMat = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj);
+ CopyMatrix (matrix.GetPtr(), projMat.GetPtr());
+ OGL_CALL(glMatrixMode( GL_PROJECTION ));
+ OGL_CALL(glLoadMatrixf (matrix.GetPtr()));
+ OGL_CALL(glMatrixMode( GL_MODELVIEW ));
+}
+
+void GFX_GL_IMPL::SetWorldMatrix( const float matrix[16] )
+{
+ GFX_LOG(("{%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f}",
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]));
+
+ CopyMatrix(matrix, STATE.m_WorldMatrix.GetPtr());
+
+ OGL_CALL(glLoadMatrixf(m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr()));
+ OGL_CALL(glMultMatrixf(matrix));
+}
+
+void GFX_GL_IMPL::SetViewMatrix( const float matrix[16] )
+{
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(matrix);
+
+ const Matrix4x4f& projMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatProj);
+ Matrix4x4f& viewMat = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatView);
+ Matrix4x4f& viewProjMat = m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatViewProj);
+ CopyMatrix (matrix, viewMat.GetPtr());
+ MultiplyMatrices4x4 (&projMat, &viewMat, &viewProjMat);
+ STATE.m_WorldMatrix.SetIdentity();
+}
+
+
+void GFX_GL_IMPL::GetMatrix( float outMatrix[16] ) const
+{
+ OGL_CALL(glGetFloatv( GL_MODELVIEW_MATRIX, outMatrix ));
+}
+
+
+const float* GFX_GL_IMPL::GetWorldMatrix() const
+{
+ return STATE.m_WorldMatrix.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetProjectionMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatProj).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetDeviceProjectionMatrix() const
+{
+ return GetProjectionMatrix();
+}
+
+
+
+void GFX_GL_IMPL::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ GFX_LOG(("%d, %d", mode, backface));
+
+ if( mode != STATE.normalization )
+ {
+ if( mode == kNormalizationDisabled )
+ {
+ OGL_CALL(glDisable (GL_NORMALIZE));
+ OGL_CALL(glDisable (GL_RESCALE_NORMAL));
+ }
+ else if( mode == kNormalizationScale )
+ {
+ OGL_CALL(glDisable (GL_NORMALIZE));
+ OGL_CALL(glEnable (GL_RESCALE_NORMAL));
+ }
+ else
+ {
+ OGL_CALL(glEnable (GL_NORMALIZE));
+ OGL_CALL(glDisable (GL_RESCALE_NORMAL));
+ }
+
+ STATE.normalization = mode;
+ }
+ if( STATE.appBackfaceMode != backface )
+ {
+ STATE.appBackfaceMode = backface;
+ ApplyBackfaceMode( STATE );
+ }
+}
+
+void GFX_GL_IMPL::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ GFX_LOG(("%d, %d, %d", on, separateSpecular, colorMaterial));
+
+ int lighting = on ? 1 : 0;
+ if( lighting != STATE.lighting )
+ {
+ if( lighting )
+ OGL_CALL(glEnable( GL_LIGHTING ));
+ else
+ OGL_CALL(glDisable( GL_LIGHTING ));
+ STATE.lighting = lighting;
+ }
+
+ int sepSpec = separateSpecular ? 1 : 0;
+
+ if (STATE.separateSpecular != sepSpec)
+ {
+ // Never set separate specular and color sum to different values.
+ // Otherwise SIS 76x on OpenGL will hang up the machine.
+ // And we probably never need them to be different anyway :)
+ if( separateSpecular )
+ {
+ OGL_CALL(glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR ));
+ OGL_CALL(glEnable( GL_COLOR_SUM_EXT ));
+ }
+ else
+ {
+ OGL_CALL(glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR ));
+ OGL_CALL(glDisable( GL_COLOR_SUM_EXT ));
+ }
+ STATE.separateSpecular = sepSpec;
+ }
+
+ if( colorMaterial != STATE.colorMaterial )
+ {
+ if( colorMaterial != kColorMatDisabled )
+ {
+ OGL_CALL(glColorMaterial( GL_FRONT_AND_BACK, kColorMatModeGL[colorMaterial] ));
+ OGL_CALL(glEnable( GL_COLOR_MATERIAL ));
+ }
+ else
+ {
+ OGL_CALL(glDisable( GL_COLOR_MATERIAL ));
+
+ // looks like by disabling ColorMaterial OpenGL driver will reset material props
+ // therefore we invalidate materials cache
+ STATE.matAmbient.Set(-1, -1, -1, -1);
+ STATE.matDiffuse.Set(-1, -1, -1, -1);
+ STATE.matSpecular.Set(-1, -1, -1, -1);
+ STATE.matEmissive.Set(-1, -1, -1, -1);
+ }
+
+ STATE.colorMaterial = colorMaterial;
+ }
+}
+
+void GFX_GL_IMPL::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ GFX_LOG(("{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f",
+ ambient[0], ambient[1], ambient[2], ambient[3],
+ diffuse[0], diffuse[1], diffuse[2], diffuse[3],
+ specular[0], specular[1], specular[2], specular[3],
+ emissive[0], emissive[1], emissive[2], emissive[3],
+ shininess));
+ if (STATE.matAmbient != ambient) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_AMBIENT, ambient));
+ STATE.matAmbient.Set (ambient);
+ }
+
+ if (STATE.matDiffuse != diffuse) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_DIFFUSE, diffuse));
+ STATE.matDiffuse.Set (diffuse);
+ }
+
+ if (STATE.matSpecular != specular) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_SPECULAR, specular));
+ STATE.matSpecular.Set (specular);
+ }
+
+ if (STATE.matEmissive != emissive) {
+ OGL_CALL(glMaterialfv (GL_FRONT, GL_EMISSION, emissive));
+ STATE.matEmissive.Set (emissive);
+ }
+
+ if( STATE.matShininess != shininess ) {
+ float glshine = std::max<float>( std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+ OGL_CALL(glMaterialf (GL_FRONT, GL_SHININESS, glshine));
+ STATE.matShininess = shininess;
+ }
+
+ // From shaderstate, the material is set after the ColorMaterial.
+ // So here if color material is used, setup invalid values to material
+ // cache; otherwise they would get out of sync.
+
+ switch( STATE.colorMaterial ) {
+ case kColorMatEmission: STATE.matEmissive.Set(-1, -1, -1, -1); break;
+ case kColorMatAmbientAndDiffuse:
+ STATE.matAmbient.Set(-1, -1, -1, -1);
+ STATE.matDiffuse.Set(-1, -1, -1, -1);
+ break;
+ }
+}
+
+
+void GFX_GL_IMPL::SetColor( const float color[4] )
+{
+ GFX_LOG(("{%f, %f, %f, %f}", color[0], color[1], color[2], color[3]));
+ OGL_CALL(glColor4fv( color ));
+}
+
+
+void GFX_GL_IMPL::SetViewport( int x, int y, int width, int height )
+{
+ GFX_LOG(("{%d, %d, %d, %d}", x, y, width, height));
+ STATE.viewport[0] = x;
+ STATE.viewport[1] = y;
+ STATE.viewport[2] = width;
+ STATE.viewport[3] = height;
+ OGL_CALL(glViewport( x, y, width, height ));
+}
+
+void GFX_GL_IMPL::GetViewport( int* port ) const
+{
+ port[0] = STATE.viewport[0];
+ port[1] = STATE.viewport[1];
+ port[2] = STATE.viewport[2];
+ port[3] = STATE.viewport[3];
+}
+
+void GFX_GL_IMPL::SetScissorRect( int x, int y, int width, int height )
+{
+ GFX_LOG(("{%d, %d, %d, %d}", x, y, width, height));
+ if (STATE.scissor != 1)
+ {
+ OGL_CALL(glEnable( GL_SCISSOR_TEST ));
+ STATE.scissor = 1;
+ }
+
+ STATE.scissorRect[0] = x;
+ STATE.scissorRect[1] = y;
+ STATE.scissorRect[2] = width;
+ STATE.scissorRect[3] = height;
+ OGL_CALL(glScissor( x, y, width, height ));
+
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ GFX_LOG((""));
+ if (STATE.scissor != 0)
+ {
+ OGL_CALL(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ scissor[0] = STATE.scissorRect[0];
+ scissor[1] = STATE.scissorRect[1];
+ scissor[2] = STATE.scissorRect[2];
+ scissor[3] = STATE.scissorRect[3];
+}
+
+bool GFX_GL_IMPL::IsCombineModeSupported( unsigned int combiner )
+{
+ return TextureCombinersGL::IsCombineModeSupported( combiner );
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ GFX_LOG(("%d, %d, %d", count, hasVertexColorOrLighting, usesAddSpecular));
+ TextureCombinersGL* implGL = TextureCombinersGL::Create( count, texEnvs, props );
+ return TextureCombinersHandle( implGL );
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ GFX_LOG((""));
+ TextureCombinersGL* implGL = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGL);
+ delete implGL;
+ textureCombiners.Reset();
+}
+
+static void ClearTextureUnitGL (DeviceStateGL& state, int unit)
+{
+ GFX_LOG(("%d", unit));
+ DebugAssert (unit >= 0 && unit < gGraphicsCaps.maxTexUnits);
+
+ TextureUnitStateGL& currTex = state.textures[unit];
+ if (currTex.texDim == kTexDimNone)
+ return;
+
+ if (state.shaderEnabledImpl[kShaderFragment] == kShaderImplUndefined)
+ {
+ ActivateTextureUnitGL (unit);
+
+ if( currTex.texDim != kTexDimUnknown )
+ {
+ OGL_CALL(glDisable( kTexDimTableGL[currTex.texDim] ));
+ }
+ else
+ {
+ OGL_CALL(glDisable( GL_TEXTURE_2D ));
+ OGL_CALL(glDisable( GL_TEXTURE_1D ));
+ OGL_CALL(glDisable( GL_TEXTURE_CUBE_MAP_ARB ));
+ if( gGraphicsCaps.has3DTexture )
+ OGL_CALL(glDisable( GL_TEXTURE_3D ));
+ }
+ currTex.texDim = kTexDimNone;
+ }
+ else
+ {
+ currTex.texDim = kTexDimUnknown;
+ }
+}
+
+
+void GFX_GL_IMPL::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ TextureCombinersGL* implGL = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGL);
+ AssertIf( !implGL );
+ AssertIf (IsShaderActive( kShaderFragment ));
+
+ int i = 0;
+ for( ; i < gGraphicsCaps.maxTexUnits && i < implGL->count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGL->texEnvs[i];
+
+ // set the texture
+ ApplyTexEnvData (i, i, texEnvData[i]);
+ // apply the combiner
+ Vector4f texcolorVal = texColors[i];
+ TextureUnitStateGL& unitState = STATE.textures[i];
+
+ // TODO: on Joe's machine this is sometimes failing (FPS tutorial, rotate scene view).
+ // So we just don't check, which disables the cache.
+ //if (current.texColor != texcolorVal) {
+ unitState.color = texcolorVal;
+ OGL_CALL(glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texcolorVal.GetPtr() ));
+ //}
+
+ // setup combine modes
+ ApplyCombinerGL( unitState.combColor, unitState.combAlpha, binding.m_CombColor, binding.m_CombAlpha );
+ }
+
+ // clear unused textures
+ for (; i < gGraphicsCaps.maxTexUnits; ++i)
+ ClearTextureUnitGL (STATE, i);
+
+ // Get us back to TU 0, so we know where we stand
+ ActivateTextureUnitGL (0);
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ TextureCombinersGL* implGL = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGL);
+ AssertIf( !implGL );
+
+ int count = std::min(implGL->count, gGraphicsCaps.maxTexUnits);
+
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implGL->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implGL->texEnvs[i].m_TextureName.index, implGL->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, count);
+ for( int i = 0; i < count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGL->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GFX_GL_IMPL::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+const unsigned long* GetGLTextureDimensionTable() { return kTexDimTableGL; }
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ GFX_LOG(("%d, %d, %d", unit, texture.m_ID, dim));
+ DebugAssertIf( dim < kTexDim2D || dim > kTexDimCUBE );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ TextureUnitStateGL& currTex = STATE.textures[unit];
+
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexImageUnits );
+ ActivateTextureUnitGL (unit);
+
+ int dimGLTarget = kTexDimTableGL[dim];
+
+
+ if (!IsShaderActive(kShaderFragment))
+ {
+ switch( currTex.texDim ) {
+ case kTexDimUnknown:
+ // We don't know which target is enabled, so disable all but the one we
+ // want to set, and enable only that one
+ if( dim != kTexDimDeprecated1D ) OGL_CALL(glDisable( GL_TEXTURE_1D ));
+ if( dim != kTexDim2D ) OGL_CALL(glDisable( GL_TEXTURE_2D ));
+ if( dim != kTexDimCUBE ) OGL_CALL(glDisable( GL_TEXTURE_CUBE_MAP_ARB ));
+ if( dim != kTexDim3D && gGraphicsCaps.has3DTexture ) OGL_CALL(glDisable( GL_TEXTURE_3D ));
+ OGL_CALL(glEnable( dimGLTarget ));
+ break;
+ case kTexDimNone:
+ // Texture unit was disabled. So simply enable our target.
+ OGL_CALL(glEnable( dimGLTarget ));
+ break;
+ default:
+ // Disable current and enable ours if they're different.
+ if( currTex.texDim != dim ) {
+ OGL_CALL(glDisable( kTexDimTableGL[currTex.texDim] ));
+ OGL_CALL(glEnable( dimGLTarget ));
+ }
+ break;
+ }
+ currTex.texDim = dim;
+ }
+ else
+ {
+ currTex.texDim = kTexDimUnknown;
+ }
+
+ if( targetTex != currTex.texID )
+ {
+ OGL_CALL(glBindTexture(dimGLTarget, targetTex));
+ currTex.texID = targetTex;
+ }
+ m_Stats.AddUsedTexture(texture);
+ if (gGraphicsCaps.hasMipLevelBias && bias != currTex.bias && bias != std::numeric_limits<float>::infinity())
+ {
+ OGL_CALL(glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias ));
+ currTex.bias = bias;
+ }
+}
+
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ GFX_LOG(("%d, %d, %d, %d, {%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f}",
+ unit, dim, texGen, identity,
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[4], matrix[5], matrix[6], matrix[7],
+ matrix[8], matrix[9], matrix[10], matrix[11],
+ matrix[12], matrix[13], matrix[14], matrix[15]
+ ));
+
+ Assert (unit >= 0 && unit < kMaxSupportedTextureCoords);
+
+ // This assumes the current texture unit is already set!!!
+ TextureUnitStateGL& unitState = STATE.textures[unit];
+
+ // -------- texture matrix
+
+ if (unit < gGraphicsCaps.maxTexUnits)
+ {
+ OGL_CALL(glMatrixMode( GL_TEXTURE ));
+ OGL_CALL(glLoadMatrixf( matrix ));
+ OGL_CALL(glMatrixMode( GL_MODELVIEW ));
+ }
+
+ // -------- texture coordinate generation
+
+ if( IsShaderActive(kShaderVertex) )
+ {
+ // Ok, in theory we could just return here, as texgen is completely ignored
+ // with vertex shaders. However, on Mac OS X 10.4.10, Radeon X1600 and GeForce 8600M,
+ // there seems to be a bug. Repro case: shader that uses a single cubemap texture and
+ // CubeReflect texgen, and use glow on the camera = glow is wrong.
+ //
+ // On Windows / GeForce 7600 on OpenGL (93.71) the behaviour is correct. But I feel safer
+ // just using the workaround everywhere...
+ //
+ // So: with vertex shaders, force texgen to be disabled. The redundant change check below
+ // will catch most of the cases cheaply anyway.
+ texGen = kTexGenDisabled;
+ }
+
+ if( texGen == unitState.texGen )
+ return;
+
+ switch( texGen )
+ {
+ case kTexGenDisabled:
+ OGL_CALL(glDisable (GL_TEXTURE_GEN_S));
+ OGL_CALL(glDisable (GL_TEXTURE_GEN_T));
+ OGL_CALL(glDisable (GL_TEXTURE_GEN_R));
+ break;
+ case kTexGenSphereMap:
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glDisable(GL_TEXTURE_GEN_R));
+ break;
+ case kTexGenObject: {
+ float zplane[4] = {0,0,1,0};
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR));
+ OGL_CALL(glTexGenfv (GL_R, GL_OBJECT_PLANE, zplane));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ }
+ case kTexGenEyeLinear: {
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ }
+ case kTexGenCubeReflect:
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ case kTexGenCubeNormal:
+ OGL_CALL(glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB));
+ OGL_CALL(glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_S));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_T));
+ OGL_CALL(glEnable(GL_TEXTURE_GEN_R));
+ break;
+ }
+
+ unitState.texGen = texGen;
+}
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d", texture.m_ID, texDim, filter, wrap, anisoLevel, hasMipMap, colorSpace));
+
+ TextureIdMapGL_QueryOrCreate(texture);
+
+ GLuint target = GetGLTextureDimensionTable()[texDim];
+ SetTexture (kShaderFragment, 0, 0, texture, texDim, std::numeric_limits<float>::infinity());
+
+ // Anisotropic texturing...
+ if( gGraphicsCaps.hasAnisoFilter && target != GL_TEXTURE_3D )
+ {
+ anisoLevel = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel ));
+ }
+
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_WRAP_S, kWrapModeGL[wrap] ));
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_WRAP_T, kWrapModeGL[wrap] ));
+ if( target == GL_TEXTURE_3D || target == GL_TEXTURE_CUBE_MAP_ARB )
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_WRAP_R, kWrapModeGL[wrap] ));
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_MAG_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST ));
+ if( hasMipMap )
+ OGL_CALL(glTexParameteri( target, GL_TEXTURE_MIN_FILTER, kMinFilterGL[filter] ));
+ else
+ OGL_CALL(glTexParameteri (target, GL_TEXTURE_MIN_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST));
+}
+
+
+
+
+// Dark corner of OpenGL:
+// (this is at least on OS X 10.4.9, MacBook Pro)
+// Before switching to new context, unbind any textures/shaders/whatever in the previous one!
+//
+// Otherwise is some _rare_ cases the textures will leak VRAM. Cases like this:
+// 1) create a depth render texture (this creates actual GL texture; as copy is required)
+// 2) create another render texture, render into it using the depth texture
+// 3) release both depth RT and this other RT
+// 4) the memory used by GL texture from step 1 is leaked!
+//
+// It seems like OS X is refcounting resources between contexts, yet when
+// a context is destroyed the refcounts are not released. Or something like that.
+//
+// The texture leak also happens if shader programs are not unbound (hard to correlate why, but...)!
+void GFX_GL_IMPL::UnbindObjects()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+
+ // unbind textures
+ int texUnits = gGraphicsCaps.maxTexUnits;
+ for( int i = 0; i < texUnits; ++i )
+ ClearTextureUnitGL (GetGLDeviceState(device), i);
+ // unbind shaders
+ ShaderLab::SubProgram* programs[kShaderTypeCount] = {0};
+ GraphicsHelper::SetShaders (*this, programs, NULL);
+ // unbind VBOs
+ UnbindVertexBuffersGL();
+}
+
+
+
+unsigned int GetGLShaderImplTarget( ShaderImplType implType )
+{
+ switch( implType )
+ {
+ case kShaderImplVertex: return GL_VERTEX_PROGRAM_ARB;
+ case kShaderImplFragment: return GL_FRAGMENT_PROGRAM_ARB;
+ }
+ DebugAssertIf( true );
+ return 0;
+}
+
+void InvalidateActiveShaderStateGL( ShaderType type )
+{
+ GfxDevice& device = GetRealGfxDevice();
+ AssertIf( device.GetRenderer() != kGfxRendererOpenGL );
+ GFX_GL_IMPL& deviceGL = static_cast<GFX_GL_IMPL&>( device );
+
+ DeviceStateGL& state = GetGLDeviceState(deviceGL);
+ state.activeGpuProgram[type] = NULL;
+ state.activeGpuProgramParams[type] = NULL;
+ state.shaderEnabledImpl[type] = kShaderImplUndefined;
+ state.shaderEnabledID[type] = -1;
+}
+
+
+static void InternalSetShader( DeviceStateGL& state, GfxDeviceStats& stats, FogMode fog, ShaderType type, GpuProgram* program, const GpuProgramParameters* params )
+{
+ if( program )
+ {
+ GpuProgramGL& shaderGL = static_cast<GpuProgramGL&>(*program);
+ ShaderImplType implType = shaderGL.GetImplType();
+ GLShaderID id = 0;
+ if (implType == kShaderImplBoth) {
+ id = static_cast<GlslGpuProgram&>(shaderGL).GetGLProgram(fog, const_cast<GpuProgramParameters&>(*params));
+ } else {
+ id = static_cast<ArbGpuProgram&>(shaderGL).GetGLProgram(fog);
+ }
+ // set the shader
+ DebugAssert (id > 0);
+
+ ShaderImplType oldType = state.shaderEnabledImpl[type];
+ if( oldType != implType )
+ {
+ // impl type is different, disable old one and enable new one
+ if( oldType == kShaderImplBoth )
+ OGL_CALL(glUseProgramObjectARB( 0 ));
+ else if( oldType != kShaderImplUndefined )
+ OGL_CALL(glDisable( GetGLShaderImplTarget(oldType) ));
+ else {
+ // we don't know which one is currently enabled, so disable everything we have!
+ if (type == kShaderVertex)
+ OGL_CALL(glDisable(GL_VERTEX_PROGRAM_ARB));
+ if (type == kShaderFragment)
+ OGL_CALL(glDisable(GL_FRAGMENT_PROGRAM_ARB));
+ }
+
+ if( implType == kShaderImplBoth )
+ {
+ Assert (type == kShaderVertex);
+ OGL_CALL(glUseProgramObjectARB( id ));
+ }
+ else
+ {
+ GLenum gltarget = GetGLShaderImplTarget( implType );
+ OGL_CALL(glEnable( gltarget ));
+ OGL_CALL(glBindProgramARB( gltarget, id ));
+ }
+
+ state.shaderEnabledImpl[type] = implType;
+ state.shaderEnabledID[type] = id;
+ }
+ else
+ {
+ // impl type is the same, just use new shader (if different)
+
+ if( state.shaderEnabledID[type] != id )
+ {
+ if( implType == kShaderImplBoth )
+ {
+ OGL_CALL(glUseProgramObjectARB( id ));
+ }
+ else
+ {
+ OGL_CALL(glBindProgramARB( GetGLShaderImplTarget(implType), id ));
+ }
+ state.shaderEnabledID[type] = id;
+ }
+ }
+ }
+ else
+ {
+ // clear the shader
+ if( state.shaderEnabledID[type] == 0 )
+ {
+ // shader disabled, do nothing
+ DebugAssertIf( state.shaderEnabledImpl[type] != kShaderImplUndefined );
+ }
+ else if( state.shaderEnabledID[type] == -1 )
+ {
+ // shader state unknown, disable everything
+ DebugAssertIf( state.shaderEnabledImpl[type] != kShaderImplUndefined );
+ if( gGraphicsCaps.gl.hasGLSL )
+ OGL_CALL(glUseProgramObjectARB( 0 ));
+ if( type == kShaderVertex )
+ {
+ OGL_CALL(glDisable( GL_VERTEX_PROGRAM_ARB ));
+ }
+ else if( type == kShaderFragment )
+ {
+ OGL_CALL(glDisable( GL_FRAGMENT_PROGRAM_ARB ));
+ }
+ else
+ {
+ AssertIf( true );
+ }
+ state.shaderEnabledID[type] = 0;
+ }
+ else
+ {
+ // some shader enabled, disable just that one
+ ShaderImplType oldType = state.shaderEnabledImpl[type];
+ DebugAssertIf( oldType == kShaderImplUndefined );
+ if( oldType == kShaderImplBoth )
+ {
+ OGL_CALL(glUseProgramObjectARB( 0 ));
+ }
+ else
+ OGL_CALL(glDisable( GetGLShaderImplTarget(oldType) ));
+ state.shaderEnabledImpl[type] = kShaderImplUndefined;
+ state.shaderEnabledID[type] = 0;
+ }
+ }
+
+ // Note: set activeGpuProgram after doing everything above. Above code might be
+ // creating shader combinations for Fog on demand, which can reset activeGpuProgram to NULL.
+ state.activeGpuProgram[type] = program;
+ state.activeGpuProgramParams[type] = params;
+}
+
+void GFX_GL_IMPL::SetShadersThreadable(GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ GFX_LOG(("%d, %d",
+ (vertexProgram? vertexProgram->GetImplType() : kShaderImplUndefined),
+ (fragmentProgram? fragmentProgram->GetImplType() : kShaderImplUndefined)));
+
+ // GLSL is only supported like this:
+ // vertex shader actually is both vertex & fragment linked program
+ // fragment shader is unused
+
+ FogMode fogMode = m_FogParams.mode;
+ if (vertexProgram && vertexProgram->GetImplType() == kShaderImplBoth)
+ {
+ Assert (fragmentProgram == 0 || fragmentProgram->GetImplType() == kShaderImplBoth);
+
+ InternalSetShader( STATE, m_Stats, m_FogParams.mode, kShaderVertex, vertexProgram, params[kShaderVertex] );
+ }
+ else
+ {
+ Assert ((fragmentProgram ? fragmentProgram->GetImplType() != kShaderImplBoth : true));
+
+ InternalSetShader( STATE, m_Stats, fogMode, kShaderVertex, vertexProgram, params[kShaderVertex] );
+ InternalSetShader( STATE, m_Stats, fogMode, kShaderFragment, fragmentProgram, params[kShaderFragment] );
+ }
+
+ for (int pt = 0; pt < kShaderTypeCount; ++pt)
+ {
+ if (programs[pt])
+ {
+ programs[pt]->ApplyGpuProgram (*params[pt], paramsBuffer[pt]);
+ m_BuiltinParamIndices[pt] = &params[pt]->GetBuiltinParams();
+ }
+ else
+ {
+ m_BuiltinParamIndices[pt] = &m_NullParamIndices;
+ }
+ }
+}
+
+
+void GFX_GL_IMPL::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ GpuProgramGL& shaderGL = static_cast<GpuProgramGL&>(program->GetGpuProgram());
+ ShaderImplType implType = shaderGL.GetImplType();
+ if (implType == kShaderImplBoth) {
+ static_cast<GlslGpuProgram&>(shaderGL).GetGLProgram(fogMode, program->GetParams(fogMode));
+ } else {
+ static_cast<ArbGpuProgram&>(shaderGL).GetGLProgram(fogMode);
+ }
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+}
+
+void GFX_GL_IMPL::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ GpuProgram& program = subprogram->GetGpuProgram();
+ GpuProgramGL& shaderGL = static_cast<GpuProgramGL&>(program);
+ ShaderType lastType = kShaderFragment;
+ if (shaderGL.GetImplType() == kShaderImplBoth)
+ lastType = kShaderVertex;
+
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ GLShaderID id = shaderGL.GetGLProgramIfCreated(FogMode(i));
+ for (int j = kShaderVertex; j <= lastType; ++j)
+ {
+ if (id && STATE.shaderEnabledID[j] == id)
+ {
+ STATE.activeGpuProgram[j] = NULL;
+ STATE.activeGpuProgramParams[j] = NULL;
+ STATE.shaderEnabledID[j] = -1;
+ STATE.shaderEnabledImpl[j] = kShaderImplUndefined;
+ }
+ }
+ }
+ delete subprogram;
+}
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ GFX_LOG(("%d", startLight));
+
+ DebugAssertIf( startLight < 0 || startLight > kMaxSupportedVertexLights );
+ const Vector4f black(0.0F, 0.0F, 0.0F, 0.0F);
+
+ const int maxLights = gGraphicsCaps.maxLights;
+ for (int i = startLight; i < maxLights; ++i)
+ {
+ if( STATE.vertexLights[i].enabled != 0 )
+ {
+ OGL_CALL(glLightfv (GL_LIGHT0 + i, GL_DIFFUSE, black.GetPtr()));
+ OGL_CALL(glDisable( GL_LIGHT0 + i ));
+
+ STATE.vertexLights[i].enabled = 0;
+ }
+ }
+
+ for (int i = startLight; i < gGraphicsCaps.maxLights; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), black);
+ }
+}
+
+void GFX_GL_IMPL::SetLight( int light, const GfxVertexLight& data)
+{
+ GFX_LOG(("%d, [{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f, %f, %f, %d]",
+ light,
+ data.position.x, data.position.y, data.position.z, data.position.w,
+ data.spotDirection.x, data.spotDirection.y, data.spotDirection.z, data.spotDirection.w,
+ data.color.x, data.color.y, data.color.z, data.color.w,
+ data.range, data.quadAtten, data.spotAngle, data.type));
+
+ DebugAssert(light >= 0 && light < kMaxSupportedVertexLights);
+
+ VertexLightStateGL& state = STATE.vertexLights[light];
+
+ if (state.enabled != 1)
+ {
+ OGL_CALL(glEnable( GL_LIGHT0 + light ));
+ state.enabled = 1;
+ }
+
+ float dir[4];
+
+ // position
+ if( data.position.w == 0.0f )
+ {
+ // negate directional light for OpenGL
+ dir[0] = -data.position.x;
+ dir[1] = -data.position.y;
+ dir[2] = -data.position.z;
+ dir[3] = 0.0f;
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_POSITION, dir ));
+ }
+ else
+ {
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_POSITION, data.position.GetPtr() ));
+ }
+
+ // spot direction
+ if( data.spotAngle != -1.0f )
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_SPOT_DIRECTION, data.spotDirection.GetPtr() ));
+
+ // colors
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_DIFFUSE, data.color.GetPtr() ));
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_SPECULAR, data.color.GetPtr() ));
+ static float zeroColor[4] = { 0, 0, 0, 0 };
+ OGL_CALL(glLightfv( GL_LIGHT0 + light, GL_AMBIENT, zeroColor ));
+
+ // attenuation
+ if( state.attenQuad != data.quadAtten ) {
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_CONSTANT_ATTENUATION, 1.0f ));
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_QUADRATIC_ATTENUATION, data.quadAtten ));
+ state.attenQuad = data.quadAtten;
+ }
+
+ // angles
+ if( state.spotAngle != data.spotAngle )
+ {
+ if( data.spotAngle == -1.0f )
+ {
+ // non-spot light
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_SPOT_CUTOFF, 180.0f ));
+ }
+ else
+ {
+ // spot light
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_SPOT_CUTOFF, data.spotAngle * 0.5f ));
+ OGL_CALL(glLightf( GL_LIGHT0 + light, GL_SPOT_EXPONENT, 18.0f - data.spotAngle * 0.1f ));
+ }
+ state.spotAngle = data.spotAngle;
+ }
+
+ SetupVertexLightParams (light, data);
+}
+
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ GFX_LOG(("{%f, %f, %f, %f}", ambient[0], ambient[1], ambient[2], ambient[3]));
+ if( STATE.ambient != ambient )
+ {
+ OGL_CALL(glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambient ));
+ STATE.ambient.Set( ambient );
+ }
+}
+
+void GFX_GL_IMPL::EnableFog (const GfxFogParams& fog)
+{
+ GFX_LOG(("%d, %f, %f, %f, {%f, %f, %f, %f}", fog.mode, fog.start, fog.end, fog.density, fog.color.x, fog.color.y, fog.color.z, fog.color.w));
+
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ if( m_FogParams.mode != fog.mode )
+ {
+ OGL_CALL(glFogi( GL_FOG_MODE, kFogModeGL[fog.mode] ));
+ OGL_CALL(glEnable( GL_FOG ));
+ m_FogParams.mode = fog.mode;
+ }
+ if( m_FogParams.start != fog.start )
+ {
+ OGL_CALL(glFogf( GL_FOG_START, fog.start ));
+ m_FogParams.start = fog.start;
+ }
+ if( m_FogParams.end != fog.end )
+ {
+ OGL_CALL(glFogf( GL_FOG_END, fog.end ));
+ m_FogParams.end = fog.end;
+ }
+ if( m_FogParams.density != fog.density )
+ {
+ OGL_CALL(glFogf( GL_FOG_DENSITY, fog.density ));
+ m_FogParams.density = fog.density;
+ }
+ if( m_FogParams.color != fog.color )
+ {
+ OGL_CALL(glFogfv( GL_FOG_COLOR, fog.color.GetPtr() ));
+ m_FogParams.color = fog.color;
+ }
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ GFX_LOG((""));
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ OGL_CALL(glFogf( GL_FOG_DENSITY, 0.0f ));
+ OGL_CALL(glDisable( GL_FOG ));
+
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new ARBVBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GFX_GL_IMPL::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GFX_GL_IMPL::GetDynamicVBO()
+{
+ if( STATE.m_DynamicVBO == NULL )
+ {
+ // DynamicARBVBO is slower! Just don't use it for now.
+ // STATE.m_DynamicVBO = new DynamicARBVBO( 1024 * 1024 ); // initial size: 1MiB VB
+ STATE.m_DynamicVBO = new DynamicNullVBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+
+
+// ---------- render textures
+
+
+// defined in RenderTextureGL
+RenderSurfaceHandle CreateRenderColorSurfaceGL (TextureID textureID, int width, int height, int samples, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, GLuint globalSharedFBO);
+RenderSurfaceHandle CreateRenderDepthSurfaceGL (TextureID textureID, int width, int height, int samples, UInt32 createFlags, DepthBufferFormat depthFormat, GLuint globalSharedFBO);
+void DestroyRenderSurfaceGL (RenderSurfaceHandle& rsHandle);
+bool SetRenderTargetGL (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, GLuint globalSharedFBO);
+RenderSurfaceHandle GetActiveRenderColorSurfaceGL(int index);
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGL();
+void ResolveDepthIntoTextureGL (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle, GLuint globalSharedFBO, GLuint helperFBO);
+void GrabIntoRenderTextureGL (RenderSurfaceHandle rs, int x, int y, int width, int height, GLuint globalSharedFBO);
+
+
+static GLuint GetFBO (GLuint* fbo)
+{
+ if (!(*fbo) && gGraphicsCaps.hasRenderToTexture)
+ {
+ glGenFramebuffersEXT (1, fbo);
+ }
+ return *fbo;
+}
+
+static GLuint GetSharedFBO (DeviceStateGL& state)
+{
+ return GetFBO (&state.m_SharedFBO);
+}
+
+static GLuint GetHelperFBO (DeviceStateGL& state)
+{
+ return GetFBO (&state.m_HelperFBO);
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ GFX_LOG(("%d, %d, %d, %d, %d", textureID.m_ID, width, height, createFlags, format));
+ GLuint fbo = GetSharedFBO(STATE);
+ RenderSurfaceHandle rs = CreateRenderColorSurfaceGL (textureID, width, height, samples, dim, createFlags, format, fbo);
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+ return rs;
+}
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ GFX_LOG(("%d, %d, %d, %d", textureID.m_ID, width, height, depthFormat));
+ GLuint fbo = GetSharedFBO(STATE);
+ RenderSurfaceHandle rs = CreateRenderDepthSurfaceGL (textureID, width, height, samples, createFlags, depthFormat, fbo);
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+ return rs;
+}
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ GFX_LOG((""));
+
+ // Early out if render surface is not created (don't do flush in that case)
+ if( !rs.IsValid() )
+ return;
+
+ DestroyRenderSurfaceGL (rs);
+
+ #if UNITY_WIN
+ // Invalidating state causes problems with Windows OpenGL editor (case 490767).
+ int flags = kGLContextSkipInvalidateState;
+ #else
+ // Not invalidating state makes the cached state not match the actual state on OS X (case 494066).
+ int flags = 0;
+ #endif
+
+ GraphicsContextHandle ctx = GetMainGraphicsContext();
+ if( ctx.IsValid() )
+ ActivateGraphicsContext (ctx, false, flags);
+
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+}
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ GFX_LOG(("%d", face));
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+ SetRenderTargetGL (count, colorHandles, depthHandle, mipLevel, face, GetSharedFBO(STATE));
+ // changing render target might mean different color clear flags; so reset current state
+ STATE.m_CurrBlendState = NULL;
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+}
+void GFX_GL_IMPL::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ GFX_LOG((""));
+ ::ResolveDepthIntoTextureGL (colorHandle, depthHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+void GFX_GL_IMPL::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ void ResolveColorSurfaceGL (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO);
+
+ ResolveColorSurfaceGL (srcHandle, dstHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ return GetActiveRenderColorSurfaceGL(index);
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceGL();
+}
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+
+// ---------- uploading textures
+
+void GFX_GL_IMPL::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d", texture.m_ID, dimension, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace));
+ ::UploadTexture2DGL( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, usageMode, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d, %d", texture.m_ID, mipLevel, x, y, width, height, format, colorSpace));
+ ::UploadTextureSubData2DGL( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d", texture.m_ID, faceDataSize, size, format, mipCount, uploadFlags, colorSpace));
+ ::UploadTextureCubeGL( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d, %d", texture.m_ID, width, height, depth, format, mipCount, uploadFlags ));
+ ::UploadTexture3DGL( texture, srcData, width, height, depth, format, mipCount, uploadFlags );
+}
+
+void GFX_GL_IMPL::DeleteTexture( TextureID texture )
+{
+ GFX_LOG(("%d", texture.m_ID));
+ GLuint texname = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(texname == 0)
+ return;
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texture.m_ID);
+ glDeleteTextures( 1, &texname );
+
+ // invalidate texture unit states that used this texture
+ for( int i = 0; i < kMaxSupportedTextureUnits; ++i )
+ {
+ TextureUnitStateGL& currTex = STATE.textures[i];
+ if( currTex.texID == texname )
+ currTex.Invalidate();
+ }
+
+ TextureIdMap::RemoveTexture(texture);
+}
+
+
+// ---------- context
+
+GfxDevice::PresentMode GFX_GL_IMPL::GetPresentMode()
+{
+ return kPresentBeforeUpdate;
+}
+void GFX_GL_IMPL::BeginFrame()
+{
+ GFX_LOG((""));
+ m_InsideFrame = true;
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ GFX_LOG((""));
+ m_InsideFrame = false;
+}
+bool GFX_GL_IMPL::IsValidState()
+{
+ #if UNITY_LINUX
+ // The Linux webplayer sometimes appears to lose sync with the GL cache.
+ // This is a quick, best-guess test for that which allows us to recover gracefully.
+ float temp;
+ glGetFloatv (GL_FRONT_FACE, &temp);
+ return CompareApproximately(temp,(STATE.appBackfaceMode != STATE.userBackfaceMode)?GL_CCW:GL_CW,0.0001f);
+ #endif
+ return true;
+}
+
+void GFX_GL_IMPL::PresentFrame()
+{
+ GFX_LOG((""));
+ PresentContextGL( GetMainGraphicsContext() );
+ #if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+ #endif
+}
+
+void GFX_GL_IMPL::FinishRendering()
+{
+ GFX_LOG((""));
+ OGL_CALL(glFinish());
+}
+
+
+// ---------- immediate mode rendering
+
+template <class T>
+void WriteToArray(const T& data, dynamic_array<UInt8>& dest)
+{
+ int offset = dest.size();
+ dest.resize_uninitialized(offset + sizeof(T), true);
+ *reinterpret_cast<T*>(&dest[offset]) = data;
+}
+
+
+void GFX_GL_IMPL::ImmediateVertex( float x, float y, float z )
+{
+ GFX_LOG(("%f, %f, %f", x, y ,z));
+ WriteToArray(UInt32(kImmediateVertex), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateNormal( float x, float y, float z )
+{
+ GFX_LOG(("%f, %f, %f", x, y ,z));
+ WriteToArray(UInt32(kImmediateNormal), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateColor( float r, float g, float b, float a )
+{
+ GFX_LOG(("%f, %f, %f, %f", r, g, b, a));
+ WriteToArray(UInt32(kImmediateColor), STATE.m_ImmediateVertices);
+ WriteToArray(ColorRGBAf(r, g, b, a), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateTexCoordAll( float x, float y, float z )
+{
+ GFX_LOG(("%f, %f, %f", x, y ,z));
+ WriteToArray(UInt32(kImmediateTexCoordAll), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ GFX_LOG(("%d, %f, %f, %f", unit, x, y ,z));
+ WriteToArray(UInt32(kImmediateTexCoord), STATE.m_ImmediateVertices);
+ WriteToArray(UInt32(unit), STATE.m_ImmediateVertices);
+ WriteToArray(Vector3f(x, y, z), STATE.m_ImmediateVertices);
+}
+
+void GFX_GL_IMPL::ImmediateBegin( GfxPrimitiveType type )
+{
+ GFX_LOG(("%d", type));
+ STATE.m_ImmediateMode = type;
+ STATE.m_ImmediateVertices.clear();
+}
+
+void GFX_GL_IMPL::ImmediateEnd()
+{
+ GFX_LOG((""));
+
+ int size = STATE.m_ImmediateVertices.size();
+ if (size == 0)
+ return;
+
+ // when beginning immediate mode, turn off any vertex array components that might be
+ // activated
+ UnbindVertexBuffersGL();
+ ClearActiveChannelsGL();
+ ActivateChannelsGL();
+ BeforeDrawCall( true );
+ glBegin (kTopologyGL[STATE.m_ImmediateMode]);
+
+ const UInt8* src = STATE.m_ImmediateVertices.data();
+ const UInt8* end = src + size;
+ int vertexCount = 0;
+ while (src < end)
+ {
+ UInt32 type = *reinterpret_cast<const UInt32*>(src);
+ src += sizeof(UInt32);
+ switch (type)
+ {
+ case kImmediateVertex:
+ {
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ glVertex3f( v.x, v.y, v.z );
+ ++vertexCount;
+ break;
+ }
+ case kImmediateNormal:
+ {
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ glNormal3f( v.x, v.y, v.z );
+ break;
+ }
+ case kImmediateColor:
+ {
+ const ColorRGBAf& col = *reinterpret_cast<const ColorRGBAf*>(src);
+ src += sizeof(ColorRGBAf);
+ glColor4f( col.r, col.g, col.b, col.a );
+ break;
+ }
+ case kImmediateTexCoordAll:
+ {
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ for( int i = gGraphicsCaps.maxTexCoords; i--; )
+ {
+ if( STATE.textures[i].texGen <= kTexGenDisabled )
+ glMultiTexCoord3fARB( GL_TEXTURE0_ARB + i, v.x, v.y, v.z );
+ }
+ break;
+ }
+ case kImmediateTexCoord:
+ {
+ UInt32 unit = *reinterpret_cast<const UInt32*>(src);
+ src += sizeof(UInt32);
+ const Vector3f& v = *reinterpret_cast<const Vector3f*>(src);
+ src += sizeof(Vector3f);
+ glMultiTexCoord3fARB( GL_TEXTURE0_ARB + unit, v.x, v.y, v.z );
+ break;
+ }
+ default:
+ ErrorString("Unknown immediate type!");
+ break;
+ }
+ }
+
+ glEnd();
+ // NOTE: all immediate mode calls CAN'T call glGetError - this will result in an error!
+ // So all of Immediate* calls do not use OGL_CALL macro; instead we're just checking
+ // for errors after glEnd.
+ GLAssert();
+
+ STATE.m_ImmediateVertices.clear();
+
+ // stats
+ switch( STATE.m_ImmediateMode ) {
+ case kPrimitiveTriangles:
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ }
+}
+
+extern GLint gDefaultFBOGL;
+
+
+void GLReadPixelsWrapper (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
+{
+ // When running in Safari 64-bit, we cannot just perform a normal glReadPixels.
+ // This is because we are rendering into an FBO which is allocated by the Safari process.
+ // Apparently there are some memory access restrictions, I have not been able to read from that fbo.
+ // Also, reading from FBOs with FSAA is not possible.
+ // We work around this by allocating an FBO of our own, blitting into that, and reading from there.
+
+ GLuint fbo;
+ GLuint fboTexture;
+ GLint oldTexture;
+ bool useWorkaround = (gDefaultFBOGL != 0);
+
+ if (useWorkaround)
+ {
+ GLint curFBO;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &curFBO);
+ if (curFBO != gDefaultFBOGL)
+ useWorkaround = false;
+ }
+ if (useWorkaround)
+ {
+ if( !gGraphicsCaps.gl.hasFrameBufferBlit )
+ {
+ ErrorString ("Cannot read from image without framebuffer blit extension!");
+ return;
+ }
+
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTexture);
+
+ // Create FBO
+ glGenTextures(1, &fboTexture);
+ glBindTexture(GL_TEXTURE_2D, fboTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glGenFramebuffersEXT(1, &fbo);
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fbo );
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fboTexture, 0);
+ GLAssert();
+
+ // Blit into it
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, gDefaultFBOGL);
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo);
+ glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
+ GLAssert();
+ }
+
+ glReadPixels( x, y, width, height, format, type, pixels );
+
+ if (useWorkaround)
+ {
+ // Restore old state
+ glBindTexture(GL_TEXTURE_2D, oldTexture);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, gDefaultFBOGL);
+
+ // Destroy FBO
+ glDeleteFramebuffersEXT( 1, &fbo );
+ glDeleteTextures(1, &fboTexture);
+ GLAssert();
+ }
+}
+
+bool GFX_GL_IMPL::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ GFX_LOG(("%d, %d, %d, %d", left, bottom, width, height));
+ GLReadPixelsWrapper ( left, bottom, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba32 );
+ return true;
+}
+
+bool GFX_GL_IMPL::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ GFX_LOG(("%d, %d, %d, %d, %d, %d", left, bottom, width, height, destX, destY));
+ return ReadbackTextureGL( image, left, bottom, width, height, destX, destY );
+}
+
+void GFX_GL_IMPL::GrabIntoRenderTexture (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ GFX_LOG(("%d, %d, %d, %d", x, y, width, height));
+ ::GrabIntoRenderTextureGL (rs, x, y, width, height, GetHelperFBO (STATE));
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultRTFormat() const { return kRTFormatARGB32; }
+RenderTextureFormat GFX_GL_IMPL::GetDefaultHDRRTFormat() const { return kRTFormatARGBHalf; }
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+
+void GFX_GL_IMPL::SetActiveContext (void* ctx)
+{
+ GraphicsContextGL* glctx = reinterpret_cast<GraphicsContextGL*> (ctx);
+ ActivateGraphicsContextGL (*glctx, kGLContextSkipInvalidateState | kGLContextSkipUnbindObjects | kGLContextSkipFlush);
+}
+
+
+#if ENABLE_PROFILER
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ TimerQueryGL* query = new TimerQueryGL;
+ return query;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGL.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if (!gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGL.EndTimerQueries();
+}
+
+bool GFX_GL_IMPL::TimerQueriesIsActive()
+{
+ if (!gGraphicsCaps.hasTimerQuery )
+ return false;
+
+ return g_TimerQueriesGL.IsActive();
+}
+
+#endif // ENABLE_PROFILER
+
+// -------- editor-only functions
+
+#if UNITY_EDITOR
+
+void GFX_GL_IMPL::SetAntiAliasFlag( bool aa )
+{
+ if( aa )
+ {
+ OGL_CALL(glHint (GL_LINE_SMOOTH_HINT, GL_NICEST));
+ OGL_CALL(glEnable (GL_LINE_SMOOTH));
+ }
+ else
+ {
+ OGL_CALL(glHint (GL_LINE_SMOOTH_HINT, GL_FASTEST));
+ OGL_CALL(glDisable (GL_LINE_SMOOTH));
+ }
+}
+
+
+void GFX_GL_IMPL::DrawUserPrimitives( GfxPrimitiveType type, int vertexCount, UInt32 vertexChannels, const void* data, int stride )
+{
+ if( vertexCount == 0 )
+ return;
+
+ AssertIf( !data || vertexCount < 0 || vertexChannels == 0 );
+
+ UnbindVertexBuffersGL();
+ ClearActiveChannelsGL();
+ int offset = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ if( !( vertexChannels & (1<<i) ) )
+ continue;
+ VertexComponent destComponent = kSuitableVertexComponentForChannel[i];
+ SetChannelDataGL( (ShaderChannel)i, destComponent, (const UInt8*)data + offset, stride );
+ offset += VBO::GetDefaultChannelByteSize(i);
+ }
+ ActivateChannelsGL();
+ BeforeDrawCall(false);
+
+ OGL_CALL(glDrawArrays(kTopologyGL[type], 0, vertexCount));
+
+ // stats
+ switch (type) {
+ case kPrimitiveTriangles:
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ m_Stats.AddDrawCall( vertexCount / 4 * 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLineStrip:
+ m_Stats.AddDrawCall( vertexCount-1, vertexCount );
+ break;
+ }
+}
+
+int GFX_GL_IMPL::GetCurrentTargetAA() const
+{
+ int fsaa = 0;
+ if( gGraphicsCaps.hasMultiSample )
+ glGetIntegerv( GL_SAMPLES_ARB, &fsaa );
+ return fsaa;
+}
+
+#if UNITY_WIN
+ GfxDeviceWindow* GFX_GL_IMPL::CreateGfxWindow( HWND window, int width, int height, DepthBufferFormat depthFormat, int antiAlias )
+ {
+ return new GLWindow(window, width, height, depthFormat, antiAlias);
+ }
+#endif
+
+
+#endif // UNITY_EDITOR
+
+
+
+// --------------------------------------------------------------------------
+// -------- verification of state
+
+
+
+#if GFX_DEVICE_VERIFY_ENABLE
+
+
+void VerifyGLState(GLenum gl, float val, const char *str);
+#define VERIFY(s,t) VerifyGLState (s, t, #s " (" #t ")")
+void VerifyGLStateI(GLenum gl, int val, const char *str);
+#define VERIFYI(s,t) VerifyGLStateI (s, t, #s " (" #t ")")
+void VerifyGLState4(GLenum gl, const float *val, const char *str);
+#define VERIFY4(s,t) VerifyGLState4 (s, t, #s " (" #t ")")
+
+void VerifyEnabled(GLenum gl, bool val, const char *str);
+#define VERIFYENAB(s,t) VerifyEnabled ( s, t, #s " (" #t ")")
+
+void VerifyMaterial (GLenum gl, const float *val, const char *str);
+#define VERIFYMAT(s,t) VerifyMaterial ( s, t, #s " (" #t ")")
+
+void VerifyTexParam (int target, GLenum gl, float val, const char *str);
+#define VERIFYTP(s,t) VerifyTexParam (target, s, t, #s " (" #t ")")
+
+void VerifyTexEnv (GLenum target, GLenum gl, int val, const char *str);
+#define VERIFYTE(g,s,t) VerifyTexEnv (g, s, t, #s " (" #t ")")
+
+void VerifyTexEnv4 (GLenum target, GLenum gl, const float *val, const char *str);
+#define VERIFYTE4(g,s,t) VerifyTexEnv4(g, s, t, #s " (" #t ")")
+
+void VerifyTexGen (GLenum coord, GLenum gl, int val, const char *str);
+#define VERIFYTG(c,s,t) VerifyTexGen (c, s, t, #s " (" #t ")")
+
+
+static void VERIFY_PRINT( const char* format, ... )
+{
+ va_list va;
+ va_start (va, format);
+ ErrorString( VFormat( format, va ) );
+ va_end (va);
+}
+
+const float kVerifyDelta = 0.0001f;
+
+void VerifyGLState(GLenum gl, float val, const char *str)
+{
+ float temp;
+ glGetFloatv (gl, &temp);
+ if( !CompareApproximately(temp,val,kVerifyDelta) ) {
+ VERIFY_PRINT ("%s differs from cache (%f != %f)\n", str, val, temp);
+ }
+}
+
+void VerifyGLStateI(GLenum gl, int val, const char *str)
+{
+ int temp;
+ glGetIntegerv (gl, &temp);
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (0x%x != 0x%x)\n", str, val, temp);
+ }
+}
+
+void VerifyGLState4 (GLenum gl, const float *val, const char *str)
+{
+ GLfloat temp[4];
+ glGetFloatv (gl, temp);
+ if( !CompareApproximately(temp[0],val[0],kVerifyDelta) ||
+ !CompareApproximately(temp[1],val[1],kVerifyDelta) ||
+ !CompareApproximately(temp[2],val[2],kVerifyDelta) ||
+ !CompareApproximately(temp[3],val[3],kVerifyDelta) )
+ {
+ VERIFY_PRINT ("%s differs from cache (%f,%f,%f,%f) != (%f,%f,%f,%f)\n", str, val[0],val[1],val[2],val[3], temp[0],temp[1],temp[2],temp[3]);
+ }
+}
+
+void VerifyEnabled(GLenum gl, bool val, const char *str)
+{
+ bool temp = glIsEnabled (gl) ? true : false;
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%d != %d)\n", str, val, temp);
+ }
+}
+
+void VerifyMaterial (GLenum gl, const float *val, const char *str)
+{
+ GLfloat temp[4];
+ glGetMaterialfv (GL_FRONT, gl, temp);
+ if( !CompareApproximately(temp[0],val[0],kVerifyDelta) ||
+ !CompareApproximately(temp[1],val[1],kVerifyDelta) ||
+ !CompareApproximately(temp[2],val[2],kVerifyDelta) ||
+ !CompareApproximately(temp[3],val[3],kVerifyDelta) )
+ {
+ VERIFY_PRINT ("%s differs from cache (%f,%f,%f,%f) != (%f,%f,%f,%f)\n",str, val[0],val[1],val[2],val[3], temp[0],temp[1],temp[2],temp[3]);
+ }
+}
+
+void VerifyTexParam (int target, GLenum gl, float val, const char *str)
+{
+ float temp;
+ glGetTexParameterfv (target, gl, &temp);
+ if( !CompareApproximately(temp,val,kVerifyDelta) ) {
+ VERIFY_PRINT ("%s differs from cache (%f != %f)\n", str, val, temp);
+ }
+}
+
+void VerifyTexEnv (GLenum target, GLenum gl, int val, const char *str)
+{
+ GLint temp;
+ glGetTexEnviv (target, gl, &temp);
+ if (temp != val) {
+ VERIFY_PRINT ("%s differs from cache (%d != %d)\n", str, (int)val, (int)temp);
+ }
+}
+
+void VerifyTexEnv4( GLenum target, GLenum gl, const float *val, const char *str )
+{
+ GLfloat temp[4];
+ glGetTexEnvfv(target, gl, temp);
+ float val0 = clamp01(val[0]);
+ float val1 = clamp01(val[1]);
+ float val2 = clamp01(val[2]);
+ float val3 = clamp01(val[3]);
+ if( !CompareApproximately(temp[0],val0,kVerifyDelta) ||
+ !CompareApproximately(temp[1],val1,kVerifyDelta) ||
+ !CompareApproximately(temp[2],val2,kVerifyDelta) ||
+ !CompareApproximately(temp[3],val3,kVerifyDelta) )
+ {
+ VERIFY_PRINT ("%s differs from cache (%f,%f,%f,%f) != (%f,%f,%f,%f)\n", str, val0,val1,val2,val3, temp[0],temp[1],temp[2],temp[3]);
+ }
+}
+
+
+void VerifyTexGen(GLenum coord, GLenum gl, int val, const char *str)
+{
+ GLint temp;
+ glGetTexGeniv (coord, gl, &temp);
+ if (temp != val) {
+ VERIFY_PRINT ("TexGen %s differs from cache (%d != %d)\n", str, val, (int)temp);
+ }
+}
+
+
+void GFX_GL_IMPL::VerifyState()
+{
+ STATE.Verify();
+
+ if( m_FogParams.mode != kFogUnknown )
+ {
+ VERIFYENAB( GL_FOG, m_FogParams.mode != 0 );
+ //VERIFY( GL_FOG_MODE, GetFogMode()[m_FogParams.mode] ); // TBD
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ VERIFY( GL_FOG_START, m_FogParams.start );
+ VERIFY( GL_FOG_END, m_FogParams.end );
+ VERIFY( GL_FOG_DENSITY, m_FogParams.density );
+ float clampedFogColor[4];
+ clampedFogColor[0] = clamp01(m_FogParams.color.x);
+ clampedFogColor[1] = clamp01(m_FogParams.color.y);
+ clampedFogColor[2] = clamp01(m_FogParams.color.z);
+ clampedFogColor[3] = clamp01(m_FogParams.color.w);
+
+ VERIFY4( GL_FOG_COLOR, clampedFogColor );
+ }
+ }
+}
+
+
+void DeviceStateGL::Verify()
+{
+ GLAssert();
+ if( depthFunc != kFuncUnknown ) {
+ VERIFYENAB( GL_DEPTH_TEST, depthFunc != kFuncDisabled );
+ if( depthFunc != kFuncDisabled ) {
+ VERIFY( GL_DEPTH_FUNC, kCmpFuncGL[depthFunc] );
+ }
+ }
+ if( depthWrite != -1 ) {
+ GLboolean temp;
+ glGetBooleanv( GL_DEPTH_WRITEMASK, &temp );
+ if( temp != depthWrite )
+ printf_console( "DepthWrite mismatch (%d != %d)\n", (int)temp, (int)depthWrite );
+ }
+ if( blending != -1 ) {
+ VERIFYENAB( GL_BLEND, blending != 0 );
+ if( blending ) {
+ VERIFYI( GL_BLEND_SRC, srcBlend );
+ VERIFYI( GL_BLEND_DST, destBlend );
+ VERIFYI( GL_BLEND_EQUATION_RGB, blendOp );
+ if( gGraphicsCaps.hasSeparateAlphaBlend ) {
+ VERIFYI( GL_BLEND_SRC_ALPHA, srcBlendAlpha );
+ VERIFYI( GL_BLEND_DST_ALPHA, destBlendAlpha );
+ VERIFYI( GL_BLEND_EQUATION_ALPHA, blendOpAlpha );
+ }
+
+ }
+ }
+
+ if( alphaFunc != kFuncUnknown ) {
+ VERIFYENAB( GL_ALPHA_TEST, alphaFunc != kFuncDisabled );
+ if( alphaFunc != kFuncDisabled ) {
+ VERIFY( GL_ALPHA_TEST_FUNC, kCmpFuncGL[alphaFunc] );
+ if( alphaValue != -1 )
+ VERIFY( GL_ALPHA_TEST_REF, alphaValue );
+ }
+ }
+
+ if( culling != kCullUnknown ) {
+ VERIFYENAB( GL_CULL_FACE, culling != kCullOff );
+ switch (culling) {
+ case kCullOff: break;
+ case kCullFront: VERIFY (GL_CULL_FACE_MODE, GL_FRONT); break;
+ case kCullBack: VERIFY (GL_CULL_FACE_MODE, GL_BACK); break;
+ default: printf_console( "Invalid face cull mode: %d\n", (int)culling ); break;
+ }
+ }
+ VERIFY( GL_FRONT_FACE, (appBackfaceMode != userBackfaceMode) ? GL_CCW : GL_CW );
+
+ if( scissor != -1 ) {
+ VERIFYENAB( GL_SCISSOR_TEST, scissor != 0 );
+ }
+
+ if( lighting != -1 ) {
+ VERIFYENAB( GL_LIGHTING, lighting != 0 );
+ if( lighting && colorMaterial == kColorMatDisabled ) {
+ if( matAmbient.x != -1.0f )
+ VERIFYMAT( GL_AMBIENT, matAmbient.GetPtr() );
+ if( matDiffuse.x != -1.0f )
+ VERIFYMAT( GL_DIFFUSE, matDiffuse.GetPtr() );
+ if( matSpecular.x != -1.0f )
+ VERIFYMAT( GL_SPECULAR, matSpecular.GetPtr() );
+ if( matEmissive.x != -1.0f )
+ VERIFYMAT( GL_EMISSION, matEmissive.GetPtr() );
+ if( matShininess != -1.0f )
+ {
+ GLfloat temp;
+ glGetMaterialfv( GL_FRONT, GL_SHININESS, &temp );
+ if( !CompareApproximately(temp, matShininess * 128.0f, kVerifyDelta) )
+ printf_console( "Shininess mismatch: %f != %f\n", (float)temp, (float)matShininess );
+ }
+ }
+ }
+
+ // verify bound/enabled programs
+ for( int type = kShaderVertex; type < kShaderTypeCount; ++type )
+ {
+ ShaderImplType currType = shaderEnabledImpl[type];
+ if( currType == kShaderImplBoth )
+ {
+ GLhandleARB gl = glGetHandleARB( GL_PROGRAM_OBJECT_ARB );
+ if( gl != shaderEnabledID[type] )
+ {
+ VERIFY_PRINT( "GLSL program differs from cache (%d != %d)\n", gl, shaderEnabledID[type] );
+ }
+ }
+ else if( currType == kShaderImplUndefined )
+ {
+ AssertIf( shaderEnabledID[type] != -1 && shaderEnabledID[type] != 0 );
+ if( shaderEnabledID[type] == 0 )
+ {
+ if( type == kShaderVertex )
+ {
+ VERIFYENAB( GL_VERTEX_PROGRAM_ARB, false );
+ }
+ else
+ {
+ VERIFYENAB( GL_FRAGMENT_PROGRAM_ARB, false );
+ }
+ }
+ }
+ else
+ {
+ GLenum gltarget = GetGLShaderImplTarget( currType );
+ VERIFYENAB( gltarget, true );
+ int gl;
+ glGetProgramivARB( gltarget, GL_PROGRAM_BINDING_ARB, &gl );
+ if( gl != shaderEnabledID[type] )
+ {
+ VERIFY_PRINT( "Program #%i differs from cache (%d != %d)\n", type, gl, shaderEnabledID[type] );
+ }
+ }
+ }
+
+ // Verify all texture units
+ for( int i = 0; i < gGraphicsCaps.maxTexUnits; ++i ) {
+ textures[i].Verify(i);
+ }
+}
+
+static const unsigned long kTextureBindingTable[6] = {0, GL_TEXTURE_BINDING_1D, GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB};
+
+void TextureUnitStateGL::Verify( int texUnit )
+{
+ if( gGraphicsCaps.maxTexUnits <= 1 ) // do not crash on no-multi-texture machines
+ return;
+
+ if( texDim == kTexDimUnknown )
+ return;
+
+ glActiveTextureARB(GL_TEXTURE0_ARB + texUnit);
+
+ VERIFYI(GL_TEXTURE_2D, texDim == kTexDim2D);
+ VERIFYI(GL_TEXTURE_CUBE_MAP_ARB, texDim == kTexDimCUBE);
+ if( gGraphicsCaps.has3DTexture ) {
+ VERIFYI(GL_TEXTURE_3D, texDim == kTexDim3D);
+ }
+
+ if( color.x != -1 && color.y != -1 && color.z != -1 && color.w != -1 )
+ VERIFYTE4( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color.GetPtr() );
+
+ if( texDim != kTexDimNone && texID != -1 )
+ VERIFYI( kTextureBindingTable[texDim], texID );
+
+ if( texGen != kTexGenUnknown )
+ {
+ switch( texGen )
+ {
+ case kTexGenDisabled:
+ VERIFYENAB( GL_TEXTURE_GEN_S, false );
+ VERIFYENAB( GL_TEXTURE_GEN_T, false );
+ VERIFYENAB( GL_TEXTURE_GEN_R, false );
+ break;
+ case kTexGenSphereMap:
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, false );
+ break;
+ case kTexGenObject: {
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ }
+ case kTexGenEyeLinear: {
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ }
+ case kTexGenCubeReflect:
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ case kTexGenCubeNormal:
+ VERIFYTG(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
+ VERIFYTG(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
+ VERIFYTG(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB);
+ VERIFYENAB( GL_TEXTURE_GEN_S, true );
+ VERIFYENAB( GL_TEXTURE_GEN_T, true );
+ VERIFYENAB( GL_TEXTURE_GEN_R, true );
+ break;
+ }
+ }
+}
+
+
+#endif // GFX_DEVICE_VERIFY_ENABLE