summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp')
-rw-r--r--Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp2815
1 files changed, 2815 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
new file mode 100644
index 0000000..cf59ce6
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
@@ -0,0 +1,2815 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES20
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Quaternion.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "ContextGLES20.h"
+#include "VBOGLES20.h"
+#include "TexturesGLES20.h"
+#include "CombinerGLES20.h"
+#include "GpuProgramsGLES20.h"
+#include "FixedFunctionStateGLES20.h"
+#include "ShaderGeneratorGLES20.h"
+#include "RenderTextureGLES20.h"
+#include "DebugGLES20.h"
+#include "TimerQueryGLES20.h"
+#include "TextureIdMapGLES20.h"
+#include "UnityGLES20Ext.h"
+
+#if UNITY_IPHONE
+ #include "PlatformDependent/iPhonePlayer/iPhoneSettings.h"
+#elif UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/ContextGLES.h"
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+
+#if NV_STATE_FILTERING
+
+void filteredInitGLES20();
+void filteredBindBufferGLES20(GLenum target, GLuint buffer, bool isImmediate);
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
+void filteredDeleteBuffersGLES20(GLsizei n, const GLuint *buffers);
+
+#ifdef glBindBuffer
+#undef glBindBuffer
+#endif
+#define glBindBuffer(a, b) filteredBindBufferGLES20(a, b, true)
+#ifndef glDeleteBuffers
+#define glDeleteBuffers filteredDeleteBuffersGLES20
+#endif
+#ifndef glVertexAttribPointer
+#define glVertexAttribPointer filteredVertexAttribPointerGLES20
+#endif
+
+#endif
+
+// let's play safe here:
+// ios/glesemu works just fine
+// and shadows demands more careful eps choice - do it later
+#define WORKAROUND_POLYGON_OFFSET UNITY_ANDROID
+
+// forward declarations
+
+bool IsActiveRenderTargetWithColorGLES2(); // RenderTextureGL.cpp
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+// local forward declarations
+struct DeviceStateGLES20;
+static void ApplyBackfaceMode( const DeviceStateGLES20& state );
+static GLuint GetSharedFBO (DeviceStateGLES20& state);
+
+extern GLint gDefaultFBO;
+
+// NOTE: GLES2.0 supports only 4 lights for now
+enum { kMaxSupportedVertexLightsByGLES20 = 4 };
+
+// Constant tables
+static const unsigned int kBlendModeES2[] = {
+ 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 kBlendFuncES2[] = {
+ GL_FUNC_ADD, GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT, GL_MIN_EXT, GL_MAX_EXT,
+};
+
+static const unsigned int kCmpFuncES2[] = {
+ GL_NEVER, GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
+};
+
+static const unsigned int kStencilOpES2[] = {
+ GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR,
+ GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+};
+
+static const GLenum kWrapModeES2[kTexWrapCount] = {
+ GL_REPEAT,
+ GL_CLAMP_TO_EDGE,
+};
+
+static const GLint kMinFilterES2[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+// --------------------------------------------------------------------------
+
+struct DeviceDepthStateGLES20 : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGLES20 : public DeviceStencilState
+{
+ GLenum stencilFuncFront;
+ GLenum stencilFailOpFront;
+ GLenum depthFailOpFront;
+ GLenum depthPassOpFront;
+ GLenum stencilFuncBack;
+ GLenum stencilFailOpBack;
+ GLenum depthFailOpBack;
+ GLenum depthPassOpBack;
+};
+
+
+typedef std::map< GfxBlendState, DeviceBlendState, memcmp_less<GfxBlendState> > CachedBlendStates;
+typedef std::map< GfxDepthState, DeviceDepthStateGLES20, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGLES20, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+// --------------------------------------------------------------------------
+struct TextureUnitStateGLES2
+{
+ GLuint texID;
+ TextureDimension texDim;
+ unsigned int combColor, combAlpha;
+ Vector4f color;
+ TexGenMode texGen;
+ Matrix4x4f textureMatrix;
+ float bias;
+
+ // right let waste space here instead of device-level int
+ bool identityMatrix;
+ bool isProjected;
+ bool posForTexGen;
+ bool nrmForTexGen;
+
+ void Invalidate();
+
+ static bool PositionRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenObject || mode == kTexGenEyeLinear);
+ }
+
+ static bool NormalRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenSphereMap || mode == kTexGenCubeReflect || mode == kTexGenCubeNormal);
+ }
+
+ void SetTexGen( TexGenMode mode )
+ {
+ posForTexGen = PositionRequiredForTexGen(mode);
+ nrmForTexGen = NormalRequiredForTexGen(mode);
+
+ texGen = mode;
+ }
+};
+
+void TextureUnitStateGLES2::Invalidate()
+{
+ texID = -1;
+ texDim = kTexDimNone;
+ combColor = combAlpha = 0xFFFFFFFF;
+ color.Set( -1, -1, -1, -1 );
+ texGen = kTexGenUnknown;
+ posForTexGen = 0;
+ nrmForTexGen = 0;
+ textureMatrix.SetIdentity();
+ identityMatrix = true;
+ isProjected = false;
+ bias = 1.0e6f;
+}
+
+
+// --------------------------------------------------------------------------
+// TODO: optimize this. Right now we just send off whole 8 float3 UVs with each
+// immediate mode vertex. We could at least detect the number of them used from
+// ImmediateTexCoord calls.
+struct ImmediateVertexGLES20 {
+ Vector3f vertex;
+ Vector3f normal;
+ UInt32 color;
+ Vector3f texCoords[8];
+};
+
+struct ImmediateModeGLES20 {
+ std::vector<ImmediateVertexGLES20> m_Vertices;
+ ImmediateVertexGLES20 m_Current;
+ GfxPrimitiveType m_Mode;
+ UInt16* m_QuadsIB;
+
+ int m_IndexBufferQuadsID;
+
+ ImmediateModeGLES20();
+ ~ImmediateModeGLES20();
+ void Invalidate();
+};
+
+// --------------------------------------------------------------------------
+struct DeviceStateGLES20
+{
+ GLuint m_SharedFBO;
+ GLuint m_HelperFBO;
+ int m_TextureIDGenerator;
+
+ int depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // Blend modes
+ int blendOp, blendOpAlpha;
+ CompareFunction alphaTest;
+ float alphaValue;
+
+ CullMode culling;
+ bool appBackfaceMode, userBackfaceMode;
+ NormalizationMode
+ normalization;
+ int scissor;
+
+ bool lighting;
+ bool separateSpecular;
+ SimpleVec4 matDiffuse, matAmbient, matSpecular, matEmissive;
+ SimpleVec4 ambient;
+ float matShininess;
+ ColorMaterialMode
+ colorMaterial;
+
+ float offsetFactor, offsetUnits;
+
+ int colorWriteMask; // ColorWriteMask combinations
+ SimpleVec4 color;
+
+ TextureUnitStateGLES2
+ textures[kMaxSupportedTextureUnitsGLES];
+ int textureCount;
+ int activeTextureUnit;
+
+ // pure optimization: texGen is very special case and is used sparingly
+ UInt32 positionTexGen;
+ UInt32 normalTexGen;
+
+ int vertexLightCount;
+ LightType vertexLightTypes[kMaxSupportedVertexLights];
+
+ TransformState transformState;
+
+ DynamicVBO* m_DynamicVBO;
+ bool vboContainsColor;
+
+ int viewport[4];
+ int scissorRect[4];
+
+ // should be set before BeforeDrawCall call
+ GfxPrimitiveType drawCallTopology;
+
+
+ GpuProgram* activeProgram;
+ const GpuProgramParameters* activeProgramParams;
+ dynamic_array<UInt8> activeProgramParamsBuffer;
+ UInt32 activeProgramID;
+
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ const DeviceBlendState* m_CurrBlendState;
+ const DeviceDepthStateGLES20* m_CurrDepthState;
+ const DeviceStencilStateGLES20* m_CurrStencilState;
+ int m_StencilRef;
+ const DeviceRasterState* m_CurrRasterState;
+
+ ImmediateModeGLES20 m_Imm;
+
+public:
+ DeviceStateGLES20();
+ void Invalidate();
+ void ComputeFixedFunctionState(FixedFunctionStateGLES20& state, const GfxFogParams& fog) const;
+
+ inline void ApplyTexGen( UInt32 unit );
+ inline void DropTexGen( UInt32 unit );
+};
+
+DeviceStateGLES20::DeviceStateGLES20()
+: m_DynamicVBO(0)
+{
+ m_TextureIDGenerator = 0;
+}
+
+void DeviceStateGLES20::Invalidate()
+{
+ DBG_LOG_GLES20("Invalidate");
+ int i;
+
+ depthFunc = -1; //unknown
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any GL mode
+ blendOp = blendOpAlpha = -1;
+ alphaTest = kFuncUnknown;
+ alphaValue = -1.0f;
+
+ culling = kCullUnknown;
+ normalization = kNormalizationUnknown;
+ scissor = -1;
+
+ lighting = false;
+ separateSpecular = false;
+
+ 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;
+
+ colorWriteMask = -1; // TBD ?
+ m_StencilRef = -1;
+
+ color.set( -1, -1, -1, -1 );
+
+ activeTextureUnit = -1;
+ for( i = 0; i < kMaxSupportedTextureUnitsGLES; ++i )
+ textures[i].Invalidate();
+ textureCount = 0;
+
+ positionTexGen = 0;
+ normalTexGen = 0;
+
+ vertexLightCount = 0;
+ for ( i = 0; i < kMaxSupportedVertexLights; ++i)
+ vertexLightTypes[i] = kLightDirectional;
+
+ // make sure backface mode is in sync
+ appBackfaceMode = false;
+ userBackfaceMode = false;
+ ApplyBackfaceMode( *this );
+
+ vboContainsColor = true;
+
+ viewport[0] = 0;
+ viewport[1] = 0;
+ viewport[2] = 0;
+ viewport[3] = 0;
+
+ scissorRect[0] = 0;
+ scissorRect[1] = 0;
+ scissorRect[2] = 0;
+ scissorRect[3] = 0;
+
+ activeProgram = 0;
+ activeProgramParams = 0;
+ activeProgramParamsBuffer.resize_uninitialized(0);
+ activeProgramID = -1;
+
+ m_Imm.Invalidate();
+
+ InvalidateVertexInputCacheGLES20();
+
+ GLESAssert();
+}
+
+void DeviceStateGLES20::ComputeFixedFunctionState(FixedFunctionStateGLES20& state, const GfxFogParams& fog) const
+{
+ if (lighting)
+ {
+ int numLights = vertexLightCount;
+ bool onlyDir = true;
+ for(int i = 0 ; i < numLights ; ++i)
+ {
+ onlyDir = onlyDir && (vertexLightTypes[i] == kLightDirectional);
+ state.SetLightType(i,vertexLightTypes[i]);
+ }
+
+ state.lightingEnabled = true;
+ state.lightCount = numLights;
+ state.onlyDirectionalLights = onlyDir;
+ state.specularEnabled = separateSpecular;
+
+ switch (colorMaterial)
+ {
+ case kColorMatDisabled:
+ break;
+
+ case kColorMatAmbientAndDiffuse:
+ state.useVertexColorAsAmbientAndDiffuse = true;
+ break;
+
+ case kColorMatEmission:
+ state.useVertexColorAsEmission = true;
+ break;
+
+ default:
+ ErrorString("Unsupported color material mode");
+ break;
+ }
+ }
+ else
+ {
+ state.lightingEnabled = false;
+ state.lightCount = 0;
+ }
+
+ state.useUniformInsteadOfVertexColor = !vboContainsColor;
+ state.texUnitCount = textureCount;
+
+ for (int i = 0; i < textureCount; i++)
+ {
+ Assert(textures[i].texDim != kTexDimUnknown);
+ Assert(textures[i].texDim != kTexDimNone);
+ Assert(textures[i].texDim != kTexDimAny);
+ Assert(textures[i].texDim != kTexDim3D); // OpenGLES2.0 does NOT supports 3D textures
+ state.texUnitCube[i] = (textures[i].texDim == kTexDimCUBE);
+ state.texUnitColorCombiner[i] = textures[i].combColor,
+ state.texUnitAlphaCombiner[i] = textures[i].combAlpha;
+ state.texUnitGen[i] = textures[i].texGen;
+
+ bool needMatrix = !textures[i].identityMatrix || textures[i].texGen > kTexGenDisabled;
+ state.SetTexUnitMatrixParam(i, needMatrix, textures[i].isProjected);
+ }
+
+ state.fogMode = fog.mode;
+ switch (fog.mode)
+ {
+ case kFogUnknown:
+ case kFogDisabled:
+ state.fogMode = kFogDisabled;
+ default:
+ break;
+ }
+
+ if(gGraphicsCaps.gles20.hasAlphaTestQCOM)
+ {
+ // we dont want to generate special shader if we have alpha-test done gl style
+ state.alphaTest = kFuncDisabled;
+ }
+ else
+ {
+ state.alphaTest = alphaTest;
+ switch (alphaTest)
+ {
+ case kFuncNever: /* \todo Disable drawing. */
+ case kFuncUnknown:
+ case kFuncDisabled:
+ case kFuncAlways:
+ state.alphaTest = kFuncDisabled;
+ default:
+ break;
+ }
+ }
+
+ state.setupPointSize = drawCallTopology == kPrimitivePoints;
+}
+
+inline void DeviceStateGLES20::ApplyTexGen( UInt32 unit )
+{
+ const TextureUnitStateGLES2& state = textures[unit];
+
+ positionTexGen = state.posForTexGen ? positionTexGen | (1<<unit)
+ : positionTexGen & ~(1<<unit);
+
+ normalTexGen = state.nrmForTexGen ? normalTexGen | (1<<unit)
+ : normalTexGen & ~(1<<unit);
+}
+
+inline void DeviceStateGLES20::DropTexGen( UInt32 unit )
+{
+ positionTexGen &= ~(1<<unit);
+ normalTexGen &= ~(1<<unit);
+}
+
+
+#include "GfxDeviceGLES20.h"
+#include "Runtime/GfxDevice/GLESCommon.h"
+
+void GfxDeviceGLES20_MarkWorldViewProjDirty()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+}
+
+void GfxDeviceGLES20_DisableDepthTest()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).depthFunc = GL_NEVER;
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+}
+
+void GfxDeviceGLES20_SetDrawCallTopology(GfxPrimitiveType topology)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).drawCallTopology = topology;
+}
+
+
+#define GL_RT_COMMON_GLES2 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GLES2
+
+void GraphicsCaps::InitGLES20()
+{
+ GLES_InitCommonCaps(this);
+ gGlesExtFunc.InitExtFunc();
+
+ shaderCaps = kShaderLevel3;
+
+ maxLights = kMaxSupportedVertexLightsByGLES20; // vertex light count
+ hasAnisoFilter = QueryExtension("GL_EXT_texture_filter_anisotropic"); // has anisotropic filtering?
+ if (hasAnisoFilter)
+ {
+ #if UNITY_PEPPER
+ // Google's implementation incorrectly reports a maxAnisoLevel of 1.
+ // Until they fix this, just set it here.
+ maxAnisoLevel = 16;
+ #else
+ GLES_CHK(glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisoLevel ));
+ #endif
+ }
+ else
+ maxAnisoLevel = 1;
+
+
+ maxTexImageUnits = 8;
+ GLES_CHK(glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTexImageUnits));
+ maxTexImageUnits = std::max<int>(std::min<int>( maxTexImageUnits, kMaxSupportedTextureUnitsGLES ), 1);
+
+ maxTexUnits = maxTexImageUnits;
+ maxTexCoords = maxTexImageUnits;
+
+ GLES_CHK(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderTextureSize));
+
+ hasMipLevelBias = QueryExtension("GL_EXT_texture_lod_bias"); // can apply bias for mips?
+ hasMipMaxLevel = QueryExtension("GL_APPLE_texture_max_level");
+
+ gles20.hasAppleMSAA = QueryExtension("GL_APPLE_framebuffer_multisample");
+ gles20.hasImgMSAA = QueryExtension("GL_IMG_multisampled_render_to_texture") && gGlesExtFunc.glRenderbufferStorageMultisampleIMG && gGlesExtFunc.glFramebufferTexture2DMultisampleIMG;
+
+ hasMultiSampleAutoResolve = gles20.hasImgMSAA;
+ hasMultiSample = gles20.hasAppleMSAA || gles20.hasImgMSAA;
+
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+ hasBlendSub = true;
+ hasBlendMinMax = QueryExtension("GL_EXT_blend_minmax");
+
+ hasS3TCCompression = false;
+
+ hasAutoMipMapGeneration = true;
+
+ has3DTexture = false;
+
+ npot = kNPOTRestricted;
+ if( QueryExtension("GL_OES_texture_npot") || QueryExtension("GL_ARB_texture_non_power_of_two") )
+ npot = kNPOTFull;
+
+ npotRT = npot;
+
+ hasRenderToTexture = true; // We have render-to-texture functionality.
+ hasShadowCollectorPass = false;
+
+ hasHighPrecisionTextureCombiners = false;
+
+ hasRenderToCubemap = true;
+
+ supportsTextureFormat[kTexFormatBGRA32] = QueryExtension("GL_APPLE_texture_format_BGRA8888") || QueryExtension("GL_EXT_texture_format_BGRA8888");
+ supportsTextureFormat[kTexFormatAlphaLum16] = false;
+ supportsTextureFormat[kTexFormatARGB32] = false; // OpenGL ES has no support for INT_8_8_8_8 format, so don't bother with Unity reversed formats at all
+ supportsTextureFormat[kTexFormatBGR24] = false; // OpenGL ES has no support for INT_8_8_8_8 format, so don't bother with Unity reversed formats at all
+ supportsTextureFormat[kTexFormatDXT1] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_EXT_texture_compression_dxt1");
+ supportsTextureFormat[kTexFormatDXT3] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt3");
+ supportsTextureFormat[kTexFormatDXT5] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt5");
+ supportsTextureFormat[kTexFormatPVRTC_RGB2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGB4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatETC_RGB4] = QueryExtension("GL_OES_compressed_ETC1_RGB8_texture");
+ supportsTextureFormat[kTexFormatATC_RGB4] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+ supportsTextureFormat[kTexFormatATC_RGBA8] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+
+ {
+ const bool supportsHalfRB = QueryExtension("GL_EXT_color_buffer_half_float");
+ // if we dont have OES_texture_half_float we cant have half texture (only RB)
+ const bool supportsHalfRT = supportsHalfRB && QueryExtension("GL_OES_texture_half_float");
+ const bool supportsRG = QueryExtension("GL_EXT_texture_rg");
+
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = supportsHalfRT;
+ supportsRenderTextureFormat[kRTFormatR8] = supportsRG;
+ supportsRenderTextureFormat[kRTFormatRHalf] = supportsHalfRT && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRGHalf] = supportsHalfRT && supportsRG;
+ }
+
+ {
+ FBColorFormatCheckerGLES2 checker;
+
+ supportsRenderTextureFormat[kRTFormatRGB565] = checker.CheckFormatSupported(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ supportsRenderTextureFormat[kRTFormatARGB4444] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4);
+ supportsRenderTextureFormat[kRTFormatARGB1555] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);
+
+ // for float formats there is no ext to check rif they are renderable, so do manually
+ if(QueryExtension("OES_texture_float"))
+ {
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_FLOAT);
+ if(QueryExtension("GL_EXT_texture_rg"))
+ {
+ // TODO: move all this unity gles ext header
+ supportsRenderTextureFormat[kRTFormatRFloat] = checker.CheckFormatSupported(0x1903, 0x1903, GL_FLOAT);
+ supportsRenderTextureFormat[kRTFormatRGFloat] = checker.CheckFormatSupported(0x8227, 0x8227, GL_FLOAT);
+ }
+ }
+ }
+
+ const bool isPvrGpu = (rendererString.find("PowerVR") != string::npos);
+ const bool isMaliGpu = (rendererString.find("Mali") != string::npos);
+ const bool isAdrenoGpu = (rendererString.find("Adreno") != string::npos);
+ const bool isTegraGpu = (rendererString.find("Tegra") != string::npos);
+
+ // Only support stencil when we have packed depth stencil, for simplicity
+ hasStencil = hasTwoSidedStencil = hasRenderTargetStencil = QueryExtension("GL_OES_packed_depth_stencil");
+
+#if UNITY_BB10
+ if(isAdrenoGpu)
+ hasRenderTargetStencil = false;
+#endif
+
+ // Adreno 2xx seems to not like a texture attached to color & depth & stencil at once;
+ // Adreno 3xx seems to be fine. Most 3xx devices have GL_OES_depth_texture_cube_map extension
+ // present, and 2xx do not. So detect based on that.
+ const bool isAdreno2xx = isAdrenoGpu && !QueryExtension("GL_OES_depth_texture_cube_map");
+ if (isAdreno2xx)
+ hasRenderTargetStencil = false;
+
+ hasNativeDepthTexture = QueryExtension("GL_OES_depth_texture") || QueryExtension ("GL_GOOGLE_depth_texture");
+#if UNITY_ANDROID
+ if(android::systeminfo::ApiLevel() < android::apiHoneycomb || (android::systeminfo::ApiLevel() < android::apiIceCreamSandwich && isPvrGpu))
+ hasNativeDepthTexture = false;
+#endif
+
+ hasStencilInDepthTexture = hasRenderTargetStencil && hasNativeDepthTexture;
+ gles20.has24DepthForFBO = QueryExtension("GL_OES_depth24");
+
+ hasNativeShadowMap = QueryExtension("GL_EXT_shadow_samplers");
+
+ supportsRenderTextureFormat[kRTFormatA2R10G10B10] = false;
+ supportsRenderTextureFormat[kRTFormatARGB64] = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = hasNativeDepthTexture;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasNativeShadowMap;
+
+
+ has16BitFloatVertex = QueryExtension("GL_OES_vertex_half_float");
+ needsToSwizzleVertexColors = false;
+
+ // ---- driver bug/workaround flags
+
+#if UNITY_LINUX
+ if(isTegraGpu)
+ {
+ supportsRenderTextureFormat[kRTFormatDepth] = hasNativeDepthTexture = true;
+ hasRenderTargetStencil = true;
+ hasStencilInDepthTexture = true;
+ hasTwoSidedStencil = true;
+ }
+#endif
+
+ hasSRGBReadWrite = QueryExtension("GL_EXT_sRGB");
+ // TODO: we should check srgb+compressed on gpu-case basis, but for now it is tegra only anyway ;-)
+ hasSRGBReadWrite = hasSRGBReadWrite && QueryExtension("GL_NV_sRGB_formats");
+
+ disableSoftShadows = true;
+
+ hasShadowCollectorPass = false;
+
+ // ---- gles20 specifics
+
+ gles20.hasGLSL = true;
+ gles20.maxAttributes = 16;
+ gles20.hasNLZ = QueryExtension("GL_NV_depth_nonlinear");
+ gles20.hasAlphaTestQCOM = QueryExtension("GL_QCOM_alpha_test") && gGlesExtFunc.glAlphaFuncQCOM;
+
+ gles20.hasMapbuffer = QueryExtension("GL_OES_mapbuffer") && gGlesExtFunc.glMapBufferOES && gGlesExtFunc.glUnmapBufferOES;
+ gles20.hasMapbufferRange= QueryExtension("GL_EXT_map_buffer_range") && gGlesExtFunc.glMapBufferRangeEXT && gGlesExtFunc.glUnmapBufferOES;
+
+#if UNITY_PEPPER
+ // it was cut out in ifdef before, so lets play safe and assume we dont have map
+ gles20.hasMapbuffer = gles20.hasMapbufferRange = false;
+#endif
+
+ gles20.hasBinaryShaders = (UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN) && QueryExtension("GL_OES_get_program_binary") && GlslGpuProgramGLES20::InitBinaryShadersSupport();
+
+ gles20.hasNVMRT = QueryExtension("GL_NV_draw_buffers") && QueryExtension("GL_NV_fbo_color_attachments") && gGlesExtFunc.glDrawBuffersNV;
+ if(gles20.hasNVMRT)
+ {
+ GLES_CHK(glGetIntegerv(GL_MAX_DRAW_BUFFERS_NV, &maxMRTs));
+ maxMRTs = clamp<int> (maxMRTs, 1, kMaxSupportedRenderTargets);
+ }
+
+#if UNITY_IPHONE
+ if( iphone::GetDeviceGeneration() == iphone::kiPhoneGenerationiPad2Gen && iphone::isIOSVersionOrNewerButBefore("4.3", "5.0") )
+ gles20.buggyColorMaskBlendMSAA = true;
+#endif
+
+#if UNITY_ANDROID || UNITY_BB10
+ // Adreno (Qualcomm chipsets) have a driver bug (Android2.2)
+ // which fails to patch vfetch instructions under certain circumstances
+ gles20.buggyVFetchPatching = isAdrenoGpu;
+#endif
+
+ // Mali-400 MP? They promised to fix drivers (they know about the issue)
+ gles20.buggyDisableVAttrKeepsActive = isMaliGpu;
+
+#if UNITY_ANDROID
+ // samsung galaxy on mali + ics update ruined shader compiler
+ // most osam part: on some shaders tex sample in vprog result in crash close to kernel (no callstack in logcat)
+ gles20.buggyVprogTextures = isMaliGpu && (android::systeminfo::ApiLevel() >= android::apiIceCreamSandwich);
+#endif
+
+ // on android pvr if we enable dynamic geom drawing from memory sometimes we hit crash in the driver
+ // it seems that some internal caching goes crazy - solved by actually drawing watermark
+ // This is also needed on BB10 devices (both pvr and adreno) to fix video display issues
+ gles20.needToRenderWatermarkDueToDrawFromMemoryBuggy = (UNITY_ANDROID && isPvrGpu) || UNITY_BB10;
+
+ // PowerVR GPUs have slow discard
+ gles20.slowAlphaTest = isPvrGpu;
+
+ // on adreno devices drivers don't like if+discard combo
+ // so set slowAlphaTest here too, to remove trivial clip
+ gles20.slowAlphaTest |= isAdrenoGpu;
+
+ // orphaning path is terribly slower universally
+ // apart from some corner cases on ios where it is not faster anyway
+ gles20.hasVBOOrphaning = false;
+
+ gles20.hasDebugMarkers = QueryExtension("GL_EXT_debug_marker") && gGlesExtFunc.glPushGroupMarkerEXT && gGlesExtFunc.glPopGroupMarkerEXT;
+ gles20.hasDiscardFramebuffer = QueryExtension("GL_EXT_discard_framebuffer") && gGlesExtFunc.glDiscardFramebufferEXT;
+ gles20.hasHalfLinearFilter = QueryExtension("GL_OES_texture_half_float_linear");
+
+
+ gles20.slowDynamicVBO = isPvrGpu || isAdrenoGpu || isMaliGpu;
+ gles20.forceStaticBatchFromMem = isMaliGpu;
+#if UNITY_IPHONE
+ gles20.forceStaticBatchFromMem = true;
+#endif
+ gles20.needFlushAfterTextureUpload = isAdrenoGpu;
+
+ gles20.needFlushAfterTextureUpload = isAdrenoGpu;
+
+ hasTiledGPU = isPvrGpu || isAdrenoGpu || isMaliGpu;
+
+ // on Tegra (for now all drivers) there is a bug that sometimes results in samplers not being reported
+ // the only known workaround is to force highp default precision
+ gles20.forceHighpFSPrec = isTegraGpu;
+
+ // on Adreno (Nexus4 at least) there is a bug that sometimes results in program link crash
+ // the only known workaround is to force highp default precision
+ gles20.forceHighpFSPrec = isAdrenoGpu;
+
+
+ GLES_CHK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gles20.maxAttributes));
+ GLES_CHK(glGetIntegerv(GL_MAX_VARYING_VECTORS, &gles20.maxVaryings));
+
+ if(gles20.hasAppleMSAA)
+ GLES_CHK(glGetIntegerv(GL_MAX_SAMPLES_APPLE, &gles20.maxSamples));
+ if(gles20.hasImgMSAA)
+ GLES_CHK(glGetIntegerv(GL_MAX_SAMPLES_IMG, &gles20.maxSamples));
+
+#if ENABLE_PROFILER
+ g_TimerQueriesGLES.Init();
+#endif
+#if NV_STATE_FILTERING
+ filteredInitGLES20();
+#endif
+}
+
+//chai: extern 函数
+GfxDevice* CreateGLES20GfxDevice()
+{
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN || UNITY_ANDROID
+ InitializeGLES20();
+#endif
+ gGraphicsCaps.InitGLES20();
+#if UNITY_EDITOR
+ return NULL;
+#else
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLES20GfxDevice","");
+#endif
+}
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ ;;printf_console("Creating OpenGLES2.0 graphics device\n");
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+
+ // ??? ReJ: (Q for Tomas) WHY InvalidateState was commented out here? InvalidateState is necessary to enable correct back-face culling
+ OnCreate();
+ InvalidateState();
+#if UNITY_PEPPER || UNITY_WEBGL
+ m_Renderer = kGfxRendererOpenGLES20Desktop;
+#else
+ m_Renderer = kGfxRendererOpenGLES20Mobile;
+#endif
+ m_IsThreadable = true;
+
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+
+ STATE.m_CurrBlendState = NULL;
+ STATE.m_CurrDepthState = NULL;
+ STATE.m_CurrStencilState = NULL;
+ STATE.m_CurrRasterState = NULL;
+ STATE.m_SharedFBO = STATE.m_HelperFBO = 0;
+
+ InitBackBufferGLES2(&m_BackBufferColor.object, &m_BackBufferDepth.object);
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+ delete STATE.m_DynamicVBO;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+#if UNITY_WIN || UNITY_ANDROID
+ ShutdownGLES20();
+#endif
+}
+
+
+static void ActivateTextureUnitGLES2 (DeviceStateGLES20& state, int unit)
+{
+ if (state.activeTextureUnit == unit)
+ return;
+ GLES_CHK(glActiveTexture(GL_TEXTURE0 + unit));
+ state.activeTextureUnit = unit;
+}
+
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ DBG_LOG_GLES20("InvalidateState");
+ m_FogParams.Invalidate();
+ STATE.transformState.Invalidate(m_BuiltinParamValues);
+ STATE.Invalidate();
+
+#if NV_STATE_FILTERING
+ StateFiltering_InvalidateVBOCacheGLES20();
+#endif
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(1,1,1,1));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, Vector4f(0,0,0,0));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(1,1,1,1));
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), Vector4f(0,0,0,0));
+}
+
+
+
+DeviceBlendState* GFX_GL_IMPL::CreateBlendState(const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = STATE.m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ DebugAssertIf(kFuncUnknown==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, DeviceDepthStateGLES20()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGLES20& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ glstate.depthFunc = kCmpFuncES2[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, DeviceStencilStateGLES20()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGLES20& st = result.first->second;
+ memcpy (&st.sourceState, &state, sizeof(st.sourceState));
+ st.stencilFuncFront = kCmpFuncES2[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpES2[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpES2[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpES2[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncES2[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpES2[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpES2[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpES2[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(glstate.sourceState));
+
+ return &result.first->second;
+}
+
+void GFX_GL_IMPL::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendState* devstate = (DeviceBlendState*)state;
+
+ if (STATE.m_CurrBlendState == devstate && alphaRef == STATE.alphaValue)
+ return;
+
+ STATE.m_CurrBlendState = devstate;
+ if (!STATE.m_CurrBlendState)
+ return;
+
+ const GfxBlendState& desc = state->sourceState;
+ const GLenum glsrc = kBlendModeES2[desc.srcBlend];
+ const GLenum gldst = kBlendModeES2[desc.dstBlend];
+ const GLenum glsrca = kBlendModeES2[desc.srcBlendAlpha];
+ const GLenum gldsta = kBlendModeES2[desc.dstBlendAlpha];
+ const GLenum glfunc = kBlendFuncES2[desc.blendOp];
+ const GLenum glfunca = kBlendFuncES2[desc.blendOpAlpha];
+ const bool blendDisabled = (glsrc == GL_ONE && gldst == GL_ZERO && glsrca == GL_ONE && gldsta == GL_ZERO);
+
+ int mask = devstate->sourceState.renderTargetWriteMask;
+
+ if (!IsActiveRenderTargetWithColorGLES2())
+ mask = 0;
+
+#if UNITY_IPHONE
+ if( (mask && mask != 15) && gGraphicsCaps.gles20.buggyColorMaskBlendMSAA && !blendDisabled && IsActiveMSAARenderTargetGLES2() )
+ mask = 15;
+#endif
+
+ if( mask != STATE.colorWriteMask )
+ {
+ GLES_CHK(glColorMask( (mask & kColorWriteR) != 0, (mask & kColorWriteG) != 0, (mask & kColorWriteB) != 0, (mask & kColorWriteA) != 0 ));
+ STATE.colorWriteMask = mask;
+ }
+
+ if( blendDisabled )
+ {
+ if( STATE.blending != 0 )
+ {
+ GLES_CHK(glDisable (GL_BLEND));
+ STATE.blending = 0;
+ }
+ }
+ else
+ {
+ if( glsrc != STATE.srcBlend || gldst != STATE.destBlend || glsrca != STATE.srcBlendAlpha || gldsta != STATE.destBlendAlpha )
+ {
+ GLES_CHK(glBlendFuncSeparate(glsrc, gldst, glsrca, gldsta));
+ 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_MIN_EXT || glfunc == GL_MAX_EXT) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+ if( (glfunca == GL_MIN_EXT || glfunca == GL_MAX_EXT) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ GLES_CHK(glBlendEquationSeparate(glfunc, glfunca));
+ STATE.blendOp = glfunc;
+ STATE.blendOpAlpha = glfunca;
+ }
+ }
+ if( STATE.blending != 1 )
+ {
+ GLES_CHK(glEnable( GL_BLEND ));
+ STATE.blending = 1;
+ }
+ }
+ // fragment shader is expected to implement per fragment culling
+ CompareFunction alphaTest = devstate->sourceState.alphaTest;
+
+ if (gGraphicsCaps.gles20.slowAlphaTest)
+ {
+ // Alpha testing is slow on some GPUs
+ // skip trivial cases such as (alpha > 0)
+ if (alphaTest == kFuncGreater && alphaRef <= 0.01)
+ alphaTest = kFuncDisabled;
+ }
+
+ if( gGraphicsCaps.gles20.hasAlphaTestQCOM && (alphaTest != STATE.alphaTest || alphaRef != STATE.alphaValue) )
+ {
+ if( alphaTest != kFuncDisabled )
+ {
+ GLES_CHK(gGlesExtFunc.glAlphaFuncQCOM(kCmpFuncES2[alphaTest], alphaRef));
+ GLES_CHK(glEnable(GL_ALPHA_TEST_QCOM));
+ }
+ else
+ {
+ GLES_CHK(glDisable(GL_ALPHA_TEST_QCOM));
+ }
+ }
+
+ STATE.alphaTest = alphaTest;
+ STATE.alphaValue = alphaRef;
+}
+
+
+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:
+ GLES_CHK(glDisable (GL_CULL_FACE));
+ break;
+ case kCullFront:
+ GLES_CHK(glCullFace (GL_FRONT));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ case kCullBack:
+ GLES_CHK(glCullFace (GL_BACK));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ default:
+ break;
+ }
+ STATE.culling = cull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != STATE.offsetFactor || zUnits != STATE.offsetUnits )
+ {
+
+#if WORKAROUND_POLYGON_OFFSET
+ // on some androids polygon offset work the other way (positive push to viewer)
+ // so we tweak projection matrix to do offset
+ // also use available depth precision better (on Adreno for example fix bugs with z-fighting)
+ // Game Programming Gems Vol1
+ // Eric Lengyel's "Tweaking a Vertex's Projected Depth Value"
+ // we use simplified formula: just smallest possible eps directly (multiplied by zUnits)
+ // we calculate eps for 16bit depth (good enough for larger depth)
+ // in projection matrix [2,2] = (f+n)/(n-f)
+ // so eps would be BitsMult * -1/proj[2,2]
+
+ static const float _BitsMult = -1.0f / (float)0xFFFF; // FFFF = 2^16-1, minus sign incorporated here
+
+ float* matrixElem = &m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).Get(2,2);
+
+ const float eps = _BitsMult / *matrixElem;
+ *matrixElem *= (1.0f + zUnits*eps);
+#else
+ GLES_CHK(glPolygonOffset( zFactor, zUnits ));
+ if( zFactor || zUnits )
+ GLES_CHK(glEnable (GL_POLYGON_OFFSET_FILL));
+ else
+ GLES_CHK(glDisable (GL_POLYGON_OFFSET_FILL));
+#endif
+ STATE.offsetFactor = zFactor;
+ STATE.offsetUnits = zUnits;
+ }
+}
+
+
+void GFX_GL_IMPL::SetDepthState(const DeviceDepthState* state)
+{
+ DeviceDepthStateGLES20* devstate = (DeviceDepthStateGLES20*)state;
+ if (STATE.m_CurrDepthState == devstate)
+ return;
+
+ STATE.m_CurrDepthState = devstate;
+
+ if (!STATE.m_CurrDepthState)
+ return;
+
+ const int depthFunc = devstate->depthFunc;
+ if( depthFunc != STATE.depthFunc )
+ {
+ if( depthFunc != GL_NEVER ) {
+ GLES_CHK(glDepthFunc (depthFunc));
+ GLES_CHK(glEnable (GL_DEPTH_TEST));
+ } else {
+ GLES_CHK(glDisable (GL_DEPTH_TEST));
+ }
+
+ STATE.depthFunc = depthFunc;
+ }
+
+ const int writeMode = devstate->sourceState.depthWrite ? GL_TRUE : GL_FALSE;
+ if( writeMode != STATE.depthWrite )
+ {
+ GLES_CHK(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 DeviceStencilStateGLES20* st = static_cast<const DeviceStencilStateGLES20*>(state);
+ STATE.m_CurrStencilState = st;
+ if (!st)
+ return;
+
+ if (st->sourceState.stencilEnable)
+ GLES_CHK(glEnable (GL_STENCIL_TEST));
+ else
+ GLES_CHK(glDisable (GL_STENCIL_TEST));
+ if (gGraphicsCaps.hasTwoSidedStencil)
+ {
+ GLES_CHK(glStencilFuncSeparate (GL_FRONT, st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_FRONT, st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ GLES_CHK(glStencilFuncSeparate (GL_BACK, st->stencilFuncBack, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_BACK, st->stencilFailOpBack, st->depthFailOpBack, st->depthPassOpBack));
+ }
+ else
+ {
+ GLES_CHK(glStencilFunc (st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOp (st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ }
+ GLES_CHK(glStencilMask (st->sourceState.writeMask));
+
+ STATE.m_StencilRef = stencilRef;
+}
+
+void GFX_GL_IMPL::SetSRGBWrite (bool enable)
+{
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ DBG_LOG_GLES20("Clear(%d, (%.2f, %.2f, %.2f, %.2f), %.2f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil);
+
+ EnsureDefaultFBInitedGLES2();
+
+ if (!IsActiveRenderTargetWithColorGLES2())
+ clearFlags &= ~kGfxClearColor;
+
+ float clearColorAlpha = color[3];
+
+ // 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)
+ {
+ GLES_CHK(glColorMask( true, true, true, true ));
+ STATE.colorWriteMask = 15;
+ STATE.m_CurrBlendState = NULL;
+ }
+ flags |= GL_COLOR_BUFFER_BIT;
+ GLES_CHK(glClearColor( color[0], color[1], color[2], clearColorAlpha ));
+ }
+ if (clearFlags & kGfxClearDepth)
+ {
+ GLES_CHK(glDepthMask (GL_TRUE));
+ STATE.depthWrite = GL_TRUE;
+ STATE.m_CurrDepthState = NULL;
+ flags |= GL_DEPTH_BUFFER_BIT;
+ GLES_CHK(glClearDepthf( depth ));
+ }
+ if (clearFlags & kGfxClearStencil)
+ {
+ //@TODO: need to set stencil writes on?
+ flags |= GL_STENCIL_BUFFER_BIT;
+ GLES_CHK(glClearStencil (stencil));
+ }
+ GLES_CHK(glClear(flags));
+}
+
+static void ApplyBackfaceMode( const DeviceStateGLES20& state )
+{
+ DBG_LOG_GLES20("ApplyBackfaceMode");
+ if (state.appBackfaceMode != state.userBackfaceMode)
+ GLES_CHK(glFrontFace( GL_CCW ));
+ else
+ GLES_CHK(glFrontFace( GL_CW ));
+}
+
+
+void GFX_GL_IMPL::SetUserBackfaceMode( bool enable )
+{
+ DBG_LOG_GLES20("SetUserBackfaceMode(%s)", GetBoolString(enable));
+ if( STATE.userBackfaceMode == enable )
+ return;
+
+ STATE.userBackfaceMode = enable;
+ ApplyBackfaceMode( STATE );
+}
+
+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;
+}
+
+void GFX_GL_IMPL::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ DBG_LOG_GLES20("SetProjectionMatrix(...)");
+
+ CopyMatrix(matrix.GetPtr(), STATE.transformState.projectionMatrixOriginal.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).GetPtr());
+ STATE.transformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+
+void GFX_GL_IMPL::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix( matrix, STATE.transformState.worldMatrix.GetPtr() );
+ STATE.transformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GFX_GL_IMPL::SetViewMatrix( const float matrix[16] )
+{
+ STATE.transformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+
+void GFX_GL_IMPL::GetMatrix( float outMatrix[16] ) const
+{
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (STATE.transformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GFX_GL_IMPL::GetWorldMatrix() const
+{
+ return STATE.transformState.worldMatrix.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetProjectionMatrix() const
+{
+ return STATE.transformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetDeviceProjectionMatrix() const
+{
+ return GetProjectionMatrix();
+}
+
+void GFX_GL_IMPL::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ DBG_LOG_GLES20("SetNormalizationBackface(%d %s)", mode, backface?"back":"front");
+ STATE.normalization = mode;
+ if( STATE.appBackfaceMode != backface )
+ {
+ STATE.appBackfaceMode = backface;
+ ApplyBackfaceMode( STATE );
+ }
+}
+
+void GFX_GL_IMPL::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ DBG_LOG_GLES20("SetFFLighting(%s, %s, %d)", on?"True":"False", separateSpecular?"True":"False", colorMaterial);
+ STATE.lighting = on;
+ STATE.separateSpecular = separateSpecular;
+ 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 )
+{
+ DBG_LOG_GLES20("SetMaterial()");
+ STATE.matAmbient.set (ambient[0], ambient[1], ambient[2], 1.0F);
+ STATE.matDiffuse.set (diffuse);
+ STATE.matSpecular.set (specular[0], specular[1], specular[2], 1.0F);
+ STATE.matEmissive.set (emissive[0], emissive[1], emissive[2], 1.0F);
+ float glshine = std::max<float>(std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+ STATE.matShininess = glshine;
+}
+
+
+void GFX_GL_IMPL::SetColor( const float color[4] )
+{
+ DBG_LOG_GLES20("SetColor()");
+ STATE.color.set( color );
+ // Emulate OpenGL's behaviour
+ ImmediateColor( color[0], color[1], color[2], color[3] );
+}
+
+void GFX_GL_IMPL::SetViewport( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES20("SetViewport(%d, %d, %d, %d)", x, y, width, height);
+ STATE.viewport[0] = x;
+ STATE.viewport[1] = y;
+ STATE.viewport[2] = width;
+ STATE.viewport[3] = height;
+ GLES_CHK(glViewport( x, y, width, height ));
+}
+
+void GFX_GL_IMPL::GetViewport( int* port ) const
+{
+ DBG_LOG_GLES20("GetViewport()");
+ 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 )
+{
+ DBG_LOG_GLES20("SetScissorRect(%d, %d, %d, %d)", x, y, width, height);
+ if (STATE.scissor != 1)
+ {
+ GLES_CHK(glEnable( GL_SCISSOR_TEST ));
+ STATE.scissor = 1;
+ }
+
+
+ STATE.scissorRect[0] = x;
+ STATE.scissorRect[1] = y;
+ STATE.scissorRect[2] = width;
+ STATE.scissorRect[3] = height;
+ GLES_CHK(glScissor( x, y, width, height ));
+
+}
+
+bool GFX_GL_IMPL::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ DBG_LOG_GLES20("DisableScissor()");
+ if (STATE.scissor != 0)
+ {
+ GLES_CHK(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ DBG_LOG_GLES20("IsScissorEnabled():returns %s", STATE.scissor == 1?"True":"False");
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ DBG_LOG_GLES20("GetScissorRect()");
+ scissor[0] = STATE.scissorRect[0];
+ scissor[1] = STATE.scissorRect[1];
+ scissor[2] = STATE.scissorRect[2];
+ scissor[3] = STATE.scissorRect[3];
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ DBG_LOG_GLES20("CreateTextureCombiners()");
+ TextureCombinersGLES2* implGLES = TextureCombinersGLES2::Create (count, texEnvs);
+ return TextureCombinersHandle (implGLES);
+}
+
+
+void GFX_GL_IMPL::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ DBG_LOG_GLES20("SetTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES2);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ STATE.textureCount = count;
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+
+ // set the texture
+ ApplyTexEnvData (i, i, texEnvData[i]);
+
+ // setup texture unit state
+ TextureUnitStateGLES2& texUnitState = STATE.textures[i];
+ texUnitState.color = texColors[i];
+ texUnitState.combColor = binding.m_CombColor;
+ texUnitState.combAlpha = binding.m_CombAlpha;
+ }
+ // we can just create mask and "and" with it
+ // but consider me lazy
+ for( int i = count ; i < gGraphicsCaps.maxTexUnits ; ++i)
+ STATE.DropTexGen(i);
+
+ STATE.activeProgram = 0;
+
+ // Get us back to TU 0, so we know where we stand
+ ActivateTextureUnitGLES2 (STATE, 0);
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ DBG_LOG_GLES20("DeleteTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersGLES2);
+ delete implGLES;
+ textureCombiners.Reset();
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ DBG_LOG_GLES20("SetTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES2);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implGLES->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implGLES->texEnvs[i].m_TextureName.index, implGLES->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, implGLES->count);
+ for( int i = 0; i < implGLES->count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GFX_GL_IMPL::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DBG_LOG_GLES20("SetTexture(%d %d)", unit, texture.m_ID);
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ TextureUnitStateGLES2& currTex = STATE.textures[unit];
+ if (STATE.textureCount > unit && targetTex == currTex.texID)
+ {
+ return;
+ }
+ ActivateTextureUnitGLES2 (STATE, unit);
+
+ switch (dim)
+ {
+ case kTexDim2D: GLES_CHK(glBindTexture(GL_TEXTURE_2D, targetTex)); break;
+ case kTexDimCUBE: GLES_CHK(glBindTexture(GL_TEXTURE_CUBE_MAP, targetTex)); break;
+ default: break;
+ }
+
+ if (STATE.textureCount <= unit)
+ STATE.textureCount = unit+1;
+ currTex.texID = targetTex;
+ currTex.texDim = dim;
+ if (currTex.texGen == kTexGenUnknown)
+ currTex.texGen = kTexGenDisabled;
+
+ STATE.ApplyTexGen(unit);
+
+ GLESAssert();
+
+ #if defined(GL_EXT_texture_lod_bias) && !UNITY_LINUX
+ if (gGraphicsCaps.hasMipLevelBias && unitState.bias != bias && bias != std::numeric_limits<float>::infinity())
+ {
+ GL_CHK(glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias ));
+ unitState.bias = bias;
+ }
+ #endif
+}
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ DBG_LOG_GLES20("SetTextureTransform()");
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ TextureUnitStateGLES2& unitState = STATE.textures[unit];
+
+ unitState.SetTexGen(texGen);
+ unitState.textureMatrix = *(Matrix4x4f const*)matrix;
+
+ // Since we will set texture matrix even if TexGen is disabled (since matrix can contain scale/offset information)
+ // we set textureMatrix to identity to be on a safe side
+ if (identity)
+ unitState.textureMatrix.SetIdentity();
+ unitState.identityMatrix = identity;
+
+ unitState.isProjected = false;
+ if (!identity && dim==kTexDim2D)
+ unitState.isProjected = (matrix[3] != 0.0f || matrix[7] != 0.0f || matrix[11] != 0.0f || matrix[15] != 1.0f);
+
+ STATE.ApplyTexGen(unit);
+}
+
+static const unsigned long kGLES20TextureDimensionTable[kTexDimCount] = {0, -1/*GL_TEXTURE_1D*/, GL_TEXTURE_2D, -1/*GL_TEXTURE_3D*/, GL_TEXTURE_CUBE_MAP, 0};
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ DBG_LOG_GLES20("SetTextureParams()");
+
+ TextureIdMapGLES20_QueryOrCreate(texture);
+
+ GLuint target = kGLES20TextureDimensionTable[texDim];
+ SetTexture (kShaderFragment, 0, 0, texture, texDim, std::numeric_limits<float>::infinity());
+
+ // Anisotropic texturing...
+ if( gGraphicsCaps.hasAnisoFilter )
+ {
+ anisoLevel = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel ));
+ }
+
+ GLenum wrapMode = kWrapModeES2[wrap];
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_S, wrapMode ));
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_T, wrapMode ));
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAG_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST ));
+ if( hasMipMap )
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MIN_FILTER, kMinFilterES2[filter] ));
+ else
+ GLES_CHK(glTexParameteri (target, GL_TEXTURE_MIN_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST));
+
+ GLESAssert();
+}
+
+void GFX_GL_IMPL::SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ DBG_LOG_GLES20("SetShadersThreadable()");
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ DBG_LOG_GLES20("SetShaders(%s, %s)",
+ GetShaderImplTypeString(vertexProgram? vertexProgram->GetImplType():kShaderImplUndefined),
+ GetShaderImplTypeString(fragmentProgram? fragmentProgram->GetImplType():kShaderImplUndefined));
+
+ // GLSL is only supported like this:
+ // vertex shader actually is both vertex & fragment linked program
+ // fragment shader is unused
+ if (vertexProgram && vertexProgram->GetImplType() == kShaderImplBoth)
+ {
+ Assert(fragmentProgram == 0 || fragmentProgram->GetImplType() == kShaderImplBoth);
+ STATE.activeProgram = vertexProgram;
+ STATE.activeProgramParams = params[kShaderVertex];
+ DebugAssert(STATE.activeProgramParams->IsReady());
+ int bufferSize = STATE.activeProgramParams->GetValuesSize();
+ STATE.activeProgramParamsBuffer.resize_uninitialized(bufferSize);
+ memcpy(STATE.activeProgramParamsBuffer.data(), paramsBuffer[kShaderVertex], bufferSize);
+ }
+ else
+ {
+ Assert(vertexProgram == 0);
+ STATE.activeProgram = 0;
+ STATE.activeProgramParams = 0;
+ STATE.activeProgramParamsBuffer.resize_uninitialized(0);
+ }
+}
+
+void GFX_GL_IMPL::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ GlslGpuProgramGLES20& programGLES = static_cast<GlslGpuProgramGLES20&>(program->GetGpuProgram());
+ programGLES.GetGLProgram(fogMode, program->GetParams(fogMode), program->GetChannels());
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ //DBG_LOG_GLES20("IsShaderActive(%s): returns %s", GetShaderTypeString(type), STATE.shaderEnabledImpl[type] != kShaderImplUndefined?"True":"False");
+ //return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+ DBG_LOG_GLES20("IsShaderActive(%s):", GetShaderTypeString(type));
+ return (STATE.activeProgram != 0);
+}
+
+void GFX_GL_IMPL::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ //@TODO
+ //if (STATE.activeProgram == program)
+ //{
+ // STATE.activeProgram = NULL;
+ // STATE.activeProgramProps = NULL;
+ //}
+ delete subprogram;
+}
+
+
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ DBG_LOG_GLES20("DisableLights(%d)", startLight);
+ startLight = std::min (startLight, gGraphicsCaps.maxLights);
+ STATE.vertexLightCount = startLight;
+ for (int i = startLight; i < kMaxSupportedVertexLightsByGLES20; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + i), Vector4f(0.0f, 0.0f, 1.0f, 0.0f));
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), Vector4f(0.0f, 0.0f, 0.0f, 0.0f));
+ }
+}
+
+
+void GFX_GL_IMPL::SetLight( int light, const GfxVertexLight& data)
+{
+ DBG_LOG_GLES20("SetLight(%d), [{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f, %f, %f, %d]",
+ light,
+ data.position[0], data.position[1], data.position[2], data.position[3],
+ data.spotDirection[0], data.spotDirection[1], data.spotDirection[2], data.spotDirection[3],
+ data.color[0], data.color[1], data.color[2], data.color[3],
+ data.range, data.quadAtten, data.spotAngle, data.type);
+ Assert( light >= 0 && light < kMaxSupportedVertexLights );
+
+ if (light >= kMaxSupportedVertexLightsByGLES20)
+ return;
+
+ STATE.vertexLightTypes[light] = data.type;
+
+ SetupVertexLightParams (light, data);
+}
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ DBG_LOG_GLES20("SetAmbient()");
+ STATE.ambient.set (ambient[0], ambient[1], ambient[2], ambient[3]);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+}
+
+void GFX_GL_IMPL::EnableFog (const GfxFogParams& fog)
+{
+ DBG_LOG_GLES20("EnableFog()");
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ m_FogParams = fog;
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ DBG_LOG_GLES20("DisableFog()");
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new GLES2VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GFX_GL_IMPL::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GFX_GL_IMPL::GetDynamicVBO()
+{
+ if( !STATE.m_DynamicVBO ) {
+ STATE.m_DynamicVBO = new DynamicGLES2VBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+// ---------- render textures
+
+static GLuint GetFBO(GLuint* fbo)
+{
+ if (!(*fbo) && gGraphicsCaps.hasRenderToTexture)
+ glGenFramebuffers(1, fbo);
+
+ return *fbo;
+}
+static GLuint GetSharedFBO (DeviceStateGLES20& state) { return GetFBO (&state.m_SharedFBO); }
+static GLuint GetHelperFBO (DeviceStateGLES20& 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)
+{
+ RenderSurfaceHandle rs = CreateRenderColorSurfaceGLES2 (textureID, 0, width, height, dim, createFlags, format, samples);
+#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)
+{
+ RenderSurfaceHandle rs = CreateRenderDepthSurfaceGLES2 (textureID, 0, width, height, createFlags, depthFormat, samples);
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ return rs;
+}
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ // Early out if render surface is not created (don't do invalidate/flush in that case)
+ if( !rs.IsValid() )
+ return;
+
+ DestroyRenderSurfaceGLES2 (rs);
+
+ InvalidateState();
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+}
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ if (SetRenderTargetGLES2 (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::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ ResolveColorSurfaceGLES2(srcHandle, dstHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ Assert (index == 0);
+ return GetActiveRenderColorSurfaceGLES2();
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceGLES2();
+}
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+void GFX_GL_IMPL::DiscardContents (RenderSurfaceHandle& rs)
+{
+ DiscardContentsGLES2(rs);
+}
+
+
+// ---------- 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 )
+{
+ ::UploadTexture2DGLES2( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, 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 )
+{
+ ::UploadTextureSubData2DGLES2( 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 )
+{
+ ::UploadTextureCubeGLES2( 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 )
+{
+ ErrorString("3D textures are not supported by OpenGLES!");
+}
+
+void GFX_GL_IMPL::DeleteTexture(TextureID texture)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texture.m_ID);
+ GLES_CHK(glDeleteTextures(1, &targetTex));
+
+ // invalidate texture unit states that used this texture
+ for( int i = 0; i < gGraphicsCaps.maxTexUnits; ++i )
+ {
+ TextureUnitStateGLES2& currTex = STATE.textures[i];
+ if( currTex.texID == targetTex )
+ currTex.Invalidate();
+ }
+
+ TextureIdMap::RemoveTexture(texture);
+}
+
+// ---------- context
+
+GfxDevice::PresentMode GFX_GL_IMPL::GetPresentMode()
+{
+ return UNITY_IPHONE ? kPresentAfterDraw : kPresentBeforeUpdate;
+}
+void GFX_GL_IMPL::BeginFrame()
+{
+ DBG_LOG_GLES20("BeginFrame()");
+ m_InsideFrame = true;
+
+ if(gGraphicsCaps.hasTiledGPU)
+ {
+ SetBackBufferGLES2();
+
+ extern void ClearCurrentFBImpl(bool clearColor, bool clearDepth);
+ ClearCurrentFBImpl(true,true);
+ }
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ // on ios we do it in trampoline
+ // also we handle BackBuffer MSAA and Render Resolution ourselves, so discard is a bit more complicated
+#if !UNITY_IPHONE
+ if(gGraphicsCaps.gles20.hasDiscardFramebuffer)
+ {
+ SetBackBufferGLES2();
+
+ extern void DiscardCurrentFBImpl(bool discardColor, bool discardDepth, GLenum target);
+ DiscardCurrentFBImpl(false, true, GL_FRAMEBUFFER);
+ }
+#endif
+
+
+ DBG_LOG_GLES20("EndFrame()");
+ m_InsideFrame = false;
+}
+
+bool GFX_GL_IMPL::IsValidState()
+{
+#if UNITY_ANDROID
+ return ContextGLES::IsValid();
+#else
+ return true;
+#endif
+}
+
+bool GFX_GL_IMPL::HandleInvalidState()
+{
+#if UNITY_ANDROID
+ bool needReload;
+ if (!ContextGLES::HandleInvalidState(&needReload))
+ return false;
+ if (needReload)
+ ReloadResources();
+ return true;
+#else
+ return true;
+#endif
+}
+
+void GFX_GL_IMPL::PresentFrame()
+{
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("PresentFrame");
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("====================================");
+
+#if UNITY_WIN
+ PresentContextGLES20();
+#else
+ PresentContextGLES();
+#endif
+}
+
+void GFX_GL_IMPL::FinishRendering()
+{
+ GLES_CHK(glFinish());
+}
+
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 2048;
+
+
+ImmediateModeGLES20::ImmediateModeGLES20()
+: m_Mode(kPrimitiveTriangles)
+, m_QuadsIB(0)
+, m_IndexBufferQuadsID(0)
+{
+ m_QuadsIB = (UInt16*)UNITY_MALLOC(kMemGeometry, kMaxImmediateVerticesPerDraw*6*sizeof(UInt16));
+ UInt32 baseIndex = 0;
+ UInt16* ibPtr = m_QuadsIB;
+ for( int i = 0; i < kMaxImmediateVerticesPerDraw; ++i )
+ {
+ ibPtr[0] = baseIndex + 1;
+ ibPtr[1] = baseIndex + 2;
+ ibPtr[2] = baseIndex;
+ ibPtr[3] = baseIndex + 2;
+ ibPtr[4] = baseIndex + 3;
+ ibPtr[5] = baseIndex;
+ baseIndex += 4;
+ ibPtr += 6;
+ }
+}
+
+ImmediateModeGLES20::~ImmediateModeGLES20()
+{
+ Invalidate();
+ UNITY_FREE(kMemGeometry,m_QuadsIB);
+}
+
+void ImmediateModeGLES20::Invalidate()
+{
+ if( m_IndexBufferQuadsID )
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferQuadsID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferQuadsID));
+ m_IndexBufferQuadsID = 0;
+ }
+ m_Vertices.clear();
+ memset( &m_Current, 0, sizeof(m_Current) );
+}
+
+void GFX_GL_IMPL::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = STATE.m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = STATE.m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ }
+ Vector3f& vert = STATE.m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ STATE.m_Imm.m_Vertices.push_back( STATE.m_Imm.m_Current );
+}
+
+void GFX_GL_IMPL::ImmediateNormal( float x, float y, float z )
+{
+ STATE.m_Imm.m_Current.normal.x = x;
+ STATE.m_Imm.m_Current.normal.y = y;
+ STATE.m_Imm.m_Current.normal.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateColor( float r, float g, float b, float a )
+{
+ r = clamp01(r);
+ g = clamp01(g);
+ b = clamp01(b);
+ a = clamp01(a);
+
+ STATE.m_Imm.m_Current.color =
+ ((UInt32)(a * 255.0f) << 24) |
+ ((UInt32)(b * 255.0f) << 16) |
+ ((UInt32)(g * 255.0f) << 8) |
+ ((UInt32)(r * 255.0f));
+}
+
+void GFX_GL_IMPL::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < gGraphicsCaps.maxTexCoords; ++i )
+ {
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GFX_GL_IMPL::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateBegin( GfxPrimitiveType type )
+{
+ STATE.m_Imm.m_Mode = type;
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+void GFX_GL_IMPL::ImmediateEnd()
+{
+ int vertexCount = STATE.m_Imm.m_Vertices.size();
+ if( vertexCount == 0 )
+ return;
+
+ InvalidateVertexInputCacheGLES20();
+
+ const size_t stride = sizeof(ImmediateVertexGLES20);
+ const ImmediateVertexGLES20* vertices = &STATE.m_Imm.m_Vertices[0];
+
+ void* dstVertices = LockSharedBufferGLES20 (GL_ARRAY_BUFFER, stride * vertexCount);
+ DebugAssert (dstVertices);
+ memcpy (dstVertices, vertices, stride * vertexCount);
+
+ if(STATE.m_Imm.m_Mode == kPrimitiveQuads && !STATE.m_Imm.m_IndexBufferQuadsID)
+ {
+ GLES_CHK(glGenBuffers( 1, (GLuint*)&STATE.m_Imm.m_IndexBufferQuadsID));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, STATE.m_Imm.m_IndexBufferQuadsID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxImmediateVerticesPerDraw * 6, STATE.m_Imm.m_QuadsIB, GL_STATIC_DRAW));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+
+ const GLuint vbo = UnlockSharedBufferGLES20 ();
+ const GLuint ibo = (STATE.m_Imm.m_Mode == kPrimitiveQuads)? STATE.m_Imm.m_IndexBufferQuadsID: 0;
+
+ //;;printf_console("ImmediateEnd> vbo: %d ibo: %d\n", vbo, ibo);
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
+
+ size_t offset = 0;
+ if(gGraphicsCaps.gles20.slowDynamicVBO)
+ offset = (size_t)static_cast<DynamicGLES2VBO&> (GetRealGfxDevice ().GetDynamicVBO ()).GetVertexMemory(stride * vertexCount);
+
+ GLES_CHK(glEnableVertexAttribArray(GL_VERTEX_ARRAY));
+ //;;printf_console("ImmediateEnd> vertex %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ GLES_CHK(glEnableVertexAttribArray(GL_NORMAL_ARRAY));
+ //;;printf_console("ImmediateEnd> normal %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_NORMAL_ARRAY, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ GLES_CHK(glEnableVertexAttribArray(GL_COLOR_ARRAY));
+ //;;printf_console("ImmediateEnd> color %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_COLOR_ARRAY, 4, GL_UNSIGNED_BYTE, true, stride, (void*)offset)); offset += sizeof(UInt32);
+ for (size_t q = 0; q < gGraphicsCaps.maxTexUnits; ++q)
+ {
+ if (GL_TEXTURE_ARRAY0 + q < gGraphicsCaps.gles20.maxAttributes)
+ {
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_ARRAY0 + q));
+ //;;printf_console("ImmediateEnd> tex%d %d\n", q, offset);
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY0 + q, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ }
+ else
+ {
+ #pragma message ("ToDo")
+ }
+ }
+
+ if(!gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ DebugAssert (offset <= stride);
+ }
+
+ BeforeDrawCall (true);
+
+ //;;printf_console("ImmediateEnd> dip %d %d\n", STATE.m_Imm.m_Mode, vertexCount);
+ switch (STATE.m_Imm.m_Mode)
+ {
+ case kPrimitiveTriangles:
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (vertexCount/2)*3, GL_UNSIGNED_SHORT, 0));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ GLES_CHK(glDrawArrays(GL_LINES, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+
+ // reset VBO bindings
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ InvalidateVertexInputCacheGLES20();
+
+ // clear vertices
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+// ---------- readback path
+
+bool GFX_GL_IMPL::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ GLES_CHK(glReadPixels( 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 )
+{
+ return ReadbackTextureGLES2(image, left, bottom, width, height, destX, destY, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+void GFX_GL_IMPL::GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ ::GrabIntoRenderTextureGLES2(rs, rd, x, y, width, height, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+
+
+#if ENABLE_PROFILER
+
+
+void GFX_GL_IMPL::BeginProfileEvent(const char* name)
+{
+ if(gGraphicsCaps.gles20.hasDebugMarkers)
+ gGlesExtFunc.glPushGroupMarkerEXT(0, name);
+}
+
+void GFX_GL_IMPL::EndProfileEvent()
+{
+ if(gGraphicsCaps.gles20.hasDebugMarkers)
+ gGlesExtFunc.glPopGroupMarkerEXT();
+}
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ if( gGraphicsCaps.hasTimerQuery )
+ return new TimerQueryGLES;
+ return NULL;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES.EndTimerQueries();
+}
+#endif // ENABLE_PROFILER
+
+typedef std::map<FixedFunctionStateGLES20, FixedFunctionProgramGLES20*, FullStateCompareGLES20> FFProgramCacheT;
+typedef std::map<FixedFunctionStateGLES20, GLShaderID, VertexStateCompareGLES20> FFVertexProgramCacheT;
+typedef std::map<FixedFunctionStateGLES20, GLShaderID, FragmentStateCompareGLES20> FFFragmentProgramCacheT;
+
+static FFProgramCacheT g_FixedFunctionProgramCache;
+static FFVertexProgramCacheT g_FFVertexProgramCache;
+static FFFragmentProgramCacheT g_FFFragmentProgramCache;
+
+void ClearFixedFunctionPrograms()
+{
+ for (FFVertexProgramCacheT::iterator it = g_FFVertexProgramCache.begin(); it != g_FFVertexProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFVertexProgramCache.clear();
+ for (FFFragmentProgramCacheT::iterator it = g_FFFragmentProgramCache.begin(); it != g_FFFragmentProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFFragmentProgramCache.clear();
+
+ for(FFProgramCacheT::iterator i = g_FixedFunctionProgramCache.begin(); i != g_FixedFunctionProgramCache.end(); ++i)
+ {
+ delete i->second;
+ }
+ g_FixedFunctionProgramCache.clear();
+
+}
+
+static const FixedFunctionProgramGLES20* GetFixedFunctionProgram(const FixedFunctionStateGLES20& state)
+{
+ FFProgramCacheT::const_iterator cachedProgIt = g_FixedFunctionProgramCache.find(state);
+ if (cachedProgIt != g_FixedFunctionProgramCache.end())
+ return cachedProgIt->second;
+
+ // Cache miss, create fixed function program
+ // NOTE: don't worry too much about performance of vertex/fragment maps
+ // shader building/compilation is crazy expensive anyway
+ FFVertexProgramCacheT::const_iterator vertexProgIt = g_FFVertexProgramCache.find(state);
+ FFFragmentProgramCacheT::const_iterator fragmentProgIt = g_FFFragmentProgramCache.find(state);
+
+ GLShaderID vertexShader = (vertexProgIt != g_FFVertexProgramCache.end())? vertexProgIt->second: 0;
+ GLShaderID fragmentShader = (fragmentProgIt != g_FFFragmentProgramCache.end())? fragmentProgIt->second: 0;
+
+ if (vertexShader == 0)
+ {
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ std::string src = BuildVertexShaderSourceGLES20(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES20("Compiling generated vertex shader");
+ CompileGlslShader(vertexShader, kErrorCompileVertexShader, cStr);
+ GLESAssert();
+
+ g_FFVertexProgramCache[state] = vertexShader;
+ }
+
+ if (fragmentShader == 0)
+ {
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+ std::string src = BuildFragmentShaderSourceGLES20(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES20("Compiling generated fragment shader");
+ CompileGlslShader(fragmentShader, kErrorCompileFragShader, cStr);
+ GLESAssert();
+
+ g_FFFragmentProgramCache[state] = fragmentShader;
+ }
+
+ DBG_SHADER_VERBOSE_GLES20("Creating and linking GLES program");
+ FixedFunctionProgramGLES20* ffProg = new FixedFunctionProgramGLES20(vertexShader, fragmentShader);
+ g_FixedFunctionProgramCache[state] = ffProg;
+
+ return ffProg;
+}
+
+static bool ComputeTextureTransformMatrix(TextureUnitStateGLES2 const& tex, Matrix4x4f const& worldViewMatrix, Matrix4x4f const& worldMatrix,
+ Matrix4x4f& outMatrix)
+{
+ switch (tex.texGen)
+ {
+ case kTexGenDisabled:
+ // NOTE: although tex-gen can be disabled
+ // textureMatrix can contain UV scale/offset
+ // so we will set it
+ case kTexGenObject:
+ if (tex.identityMatrix)
+ {
+ outMatrix.SetIdentity();
+ return false;
+ }
+ CopyMatrix(tex.textureMatrix.GetPtr(), outMatrix.GetPtr());
+ break;
+ case kTexGenSphereMap:
+ {
+ float invScale = 1.0f / Magnitude (worldViewMatrix.GetAxisX());
+
+ Matrix4x4f scaleOffsetMatrix;
+ scaleOffsetMatrix.SetScale(Vector3f(0.5*invScale, 0.5*invScale, 0.0));
+ scaleOffsetMatrix.SetPosition(Vector3f(0.5, 0.5, 0.0));
+
+ Matrix4x4f worldViewMatrixRotation = worldViewMatrix;
+ worldViewMatrixRotation.SetPosition(Vector3f::zero);
+ Matrix4x4f combo;
+ MultiplyMatrices4x4(&scaleOffsetMatrix, &worldViewMatrixRotation, &combo);
+ MultiplyMatrices4x4(&tex.textureMatrix, &combo, &outMatrix);
+ break;
+ }
+ case kTexGenEyeLinear:
+ MultiplyMatrices4x4(&tex.textureMatrix, &worldViewMatrix, &outMatrix);
+ break;
+ case kTexGenCubeNormal:
+ case kTexGenCubeReflect:
+ {
+ float invScale = 1.0f / Magnitude (worldMatrix.GetAxisX());
+ CopyMatrix(worldViewMatrix.GetPtr(), outMatrix.GetPtr());
+ outMatrix.Scale(Vector3f(invScale, invScale, invScale));
+ outMatrix.SetPosition(Vector3f::zero);
+ break;
+ }
+ default:
+ ErrorString( Format("Unknown TexGen mode %d", tex.texGen) );
+ }
+ return true;
+}
+
+void VBOContainsColorGLES20(bool flag)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).vboContainsColor = flag;
+}
+
+//chai: glUseProgram
+void GLSLUseProgramGLES20(UInt32 programID)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ if (GetGLES20DeviceState(device).activeProgramID == programID)
+ {
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ if (gGraphicsCaps.gles20.buggyVFetchPatching)
+ {
+ // NOTE: Qualcomm driver (Android2.2) fails to re-patch vfetch instructions
+ // if shader stays the same, but vertex layout has been changed
+ // as a temporary workaround just reset the shader
+ GLES_CHK(glUseProgram (0)); // driver caches shaderid, so set to 0 first
+ GLES_CHK(glUseProgram (programID));
+ return;
+ }
+#endif
+ return;
+ }
+
+#if NV_STATE_FILTERING
+ GetGLES20DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+#endif
+
+ GLES_CHK(glUseProgram (programID));
+ GetGLES20DeviceState(device).activeProgramID = programID;
+}
+
+
+struct SetValuesFunctorES2
+{
+ SetValuesFunctorES2(GfxDevice& device, UniformCacheGLES20* targetCache) : m_Device(device), m_TargetCache(targetCache) { }
+ GfxDevice& m_Device;
+ UniformCacheGLES20* m_TargetCache;
+ void SetVectorVal (ShaderParamType type, int index, const float* ptr, int cols)
+ {
+ switch(cols) {
+ case 1: CachedUniform1(m_TargetCache, type, index, ptr); break;
+ case 2: CachedUniform2(m_TargetCache, type, index, ptr); break;
+ case 3: CachedUniform3(m_TargetCache, type, index, ptr); break;
+ case 4: CachedUniform4(m_TargetCache, type, index, ptr); break;
+ }
+ }
+ void SetMatrixVal (int index, const Matrix4x4f* ptr, int rows)
+ {
+ DebugAssert(rows == 4);
+ GLES_CHK(glUniformMatrix4fv (index, 1, GL_FALSE, ptr->GetPtr()));
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ m_Device.SetTexture (shaderType, index, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+//chai: 设置shader uniforms
+void GFX_GL_IMPL::BeforeDrawCall(bool immediateMode)
+{
+ DBG_LOG_GLES20("BeforeDrawCall(%s)", GetBoolString(immediateMode));
+
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ Assert(props);
+
+ // WorldView Matrix
+#if NV_STATE_FILTERING
+ // UpdateWorldViewMatrix will reset the dirty flags, however we want to forward any modified matrices
+ // to the shader, so we "reset" the dirty state after UpdateWorldViewMatrix has been called
+ UInt32 savedDirty = STATE.transformState.dirtyFlags;
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ STATE.transformState.dirtyFlags = savedDirty;
+#else
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+#endif
+
+ // Materials
+ if (STATE.lighting)
+ {
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(STATE.matEmissive.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(STATE.matAmbient.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(STATE.matDiffuse.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(STATE.matSpecular.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(STATE.matShininess, STATE.matShininess, STATE.matShininess, STATE.matShininess));
+ }
+
+ // Fog
+ if (m_FogParams.mode > kFogDisabled)
+ {
+ float diff = m_FogParams.mode == kFogLinear ? m_FogParams.end - m_FogParams.start : 0.0f;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, m_FogParams.color);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(m_FogParams.density * 1.2011224087f,
+ m_FogParams.density * 1.4426950408f,
+ m_FogParams.mode == kFogLinear ? -invDiff : 0.0f,
+ m_FogParams.mode == kFogLinear ? m_FogParams.end * invDiff : 0.0f
+ ));
+ }
+ // Alpha-test
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(STATE.alphaValue, STATE.alphaValue, STATE.alphaValue, STATE.alphaValue));
+
+ UniformCacheGLES20* targetCache = 0;
+ if (STATE.activeProgram)
+ {
+ //chai: glUseProgram
+ //
+ // Apply GPU program
+ //转换类型
+ GlslGpuProgramGLES20& program = static_cast<GlslGpuProgramGLES20&>(*STATE.activeProgram);
+ int fogIndex = program.ApplyGpuProgramES20 (*STATE.activeProgramParams, STATE.activeProgramParamsBuffer.data());
+ m_BuiltinParamIndices[kShaderVertex] = &STATE.activeProgramParams->GetBuiltinParams();
+
+ targetCache = &program.m_UniformCache[fogIndex];
+ }
+ else
+ {
+ //chai:模拟固定管线
+ // Emulate Fixed Function pipe
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(STATE.color.GetPtr()));
+ for (int i = 0; i < STATE.textureCount; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), STATE.textures[i].color);
+ }
+
+ // generate program from fixed function state
+ DBG_LOG_GLES20(" using fixed-function");
+ FixedFunctionStateGLES20 ffstate;
+ STATE.ComputeFixedFunctionState(ffstate, m_FogParams);
+ const FixedFunctionProgramGLES20* program = GetFixedFunctionProgram(ffstate);
+ program->ApplyFFGpuProgram(m_BuiltinParamValues);
+ m_BuiltinParamIndices[kShaderVertex] = &program->GetBuiltinParams();
+
+ targetCache = &program->m_UniformCache;
+ }
+
+ //chai: 设置Unity内置uniforms
+
+ // Set Unity built-in parameters
+ {
+ Assert(m_BuiltinParamIndices[kShaderVertex]);
+ const BuiltinShaderParamIndices& params = *m_BuiltinParamIndices[kShaderVertex];
+
+ // MVP matrix
+ if (params.mat[kShaderInstanceMatMVP].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewProjDirty)
+#endif
+ )
+ {
+ Matrix4x4f wvp;
+ MultiplyMatrices4x4(&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &STATE.transformState.worldViewMatrix, &wvp);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMVP];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, wvp.GetPtr()));
+ }
+ // MV matrix
+ if (params.mat[kShaderInstanceMatMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, STATE.transformState.worldViewMatrix.GetPtr()));
+ }
+ // Transpose of MV matrix
+ if (params.mat[kShaderInstanceMatTransMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ Matrix4x4f tWV;
+ TransposeMatrix4x4(&STATE.transformState.worldViewMatrix, &tWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, tWV.GetPtr()));
+ }
+ // Inverse transpose of MV matrix
+ if (params.mat[kShaderInstanceMatInvTransMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ Matrix4x4f mat = STATE.transformState.worldViewMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ 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 invWV, tInvWV;
+ Matrix4x4f::Invert_General3D (mat, invWV);
+ TransposeMatrix4x4(&invWV, &tInvWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, tInvWV.GetPtr()));
+ }
+ // M matrix
+ if (params.mat[kShaderInstanceMatM].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.transformState.worldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, mat.GetPtr()));
+ }
+ // Inverse M matrix
+ if (params.mat[kShaderInstanceMatInvM].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvM];
+ Matrix4x4f mat = STATE.transformState.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);
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, inverseMat.GetPtr()));
+ }
+
+ // Normal matrix
+ if (params.mat[kShaderInstanceMatNormalMatrix].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatNormalMatrix];
+
+ // @TBD: remove normalization in fixed function emulation after Normal matrix multiply.
+ Matrix4x4f rotWV;
+ rotWV = STATE.transformState.worldViewMatrix;
+ rotWV.SetPosition(Vector3f::zero); // reset translation
+
+ if (STATE.normalization == kNormalizationScale) // reset scale
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ rotWV.Get (0, 0) *= invScale;
+ rotWV.Get (1, 0) *= invScale;
+ rotWV.Get (2, 0) *= invScale;
+ rotWV.Get (0, 1) *= invScale;
+ rotWV.Get (1, 1) *= invScale;
+ rotWV.Get (2, 1) *= invScale;
+ rotWV.Get (0, 2) *= invScale;
+ rotWV.Get (1, 2) *= invScale;
+ rotWV.Get (2, 2) *= invScale;
+ }
+ Matrix3x3f rotWV33 = Matrix3x3f(rotWV);
+
+ GLES_CHK(glUniformMatrix3fv (matParam.gpuIndex, 1, GL_FALSE, rotWV33.GetPtr()));
+ }
+
+
+ //chai: 设置向量uniforms
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ int gpuIndexVS = params.vec[i].gpuIndex;
+ if (gpuIndexVS >= 0)
+ {
+ const float* val = m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr();
+ switch (params.vec[i].dim) {
+ case 1: CachedUniform1(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 2: CachedUniform2(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 3: CachedUniform3(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 4: CachedUniform4(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ }
+ GLESAssert();
+ }
+ }
+
+ // Texture Matrices
+ Matrix4x4f texM;
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; ++i)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTexture0 + i];
+ if (matParam.gpuIndex >= 0)
+ {
+ if (i < STATE.textureCount)
+ ComputeTextureTransformMatrix(STATE.textures[i], STATE.transformState.worldViewMatrix, STATE.transformState.worldMatrix, texM);
+ else
+ texM.SetIdentity();
+
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, texM.GetPtr()));
+ }
+ }
+#if NV_STATE_FILTERING
+ STATE.transformState.dirtyFlags = 0;
+#endif
+ }
+
+ // Set per-drawcall properties
+ SetValuesFunctorES2 setValuesFunc(*this, targetCache);
+ ApplyMaterialPropertyBlockValuesES(m_MaterialProperties, STATE.activeProgram, STATE.activeProgramParams, setValuesFunc);
+}
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits);
+ const TextureUnitStateGLES2& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES2::PositionRequiredForTexGen(unitState.texGen);
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ const TextureUnitStateGLES2& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES2::NormalRequiredForTexGen(unitState.texGen);
+}
+
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen() const
+{
+ return ( STATE.positionTexGen != 0 && !STATE.activeProgram );
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen() const
+{
+ return ( STATE.normalTexGen != 0 && !STATE.activeProgram );
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultRTFormat() const
+{
+#if UNITY_PEPPER
+ // desktop platfrom = noone cares
+ return kRTFormatARGB32;
+#else
+
+ RenderTextureFormat defaultFormat = GetCurrentFBColorFormatGLES20();
+ return gGraphicsCaps.supportsRenderTextureFormat[defaultFormat] ? defaultFormat : kRTFormatARGB32;
+
+#endif
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultHDRRTFormat() const
+{
+#if UNITY_PEPPER
+ // desktop platfrom = noone cares
+ return kRTFormatARGB32;
+#else
+
+ if(gGraphicsCaps.supportsRenderTextureFormat[kRTFormatARGBHalf])
+ return kRTFormatARGBHalf;
+
+ RenderTextureFormat defaultFormat = GetCurrentFBColorFormatGLES20();
+ return gGraphicsCaps.supportsRenderTextureFormat[defaultFormat] ? defaultFormat : kRTFormatARGB32;
+
+#endif
+}
+
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+
+
+void GFX_GL_IMPL::ReloadResources()
+{
+ MarkAllVBOsLost();
+ GetDynamicVBO().Recreate();
+
+ GfxDevice::CommonReloadResources(kReleaseRenderTextures | kReloadShaders | kReloadTextures);
+ ClearFixedFunctionPrograms();
+
+ extern void ClearFBMapping();
+ ClearFBMapping();
+ STATE.m_SharedFBO = STATE.m_HelperFBO = 0;
+
+ InvalidateState();
+}
+
+void GFX_GL_IMPL::InitFramebufferDepthFormat()
+{
+ m_FramebufferDepthFormat = QueryFBDepthFormatGLES2();
+}
+
+// Acquire thread ownership on the calling thread. Worker releases ownership.
+void GFX_GL_IMPL::AcquireThreadOwnership()
+{
+#if UNITY_ANDROID
+ AcquireGLES20Context();
+#endif
+}
+
+// Release thread ownership on the calling thread. Worker acquires ownership.
+void GFX_GL_IMPL::ReleaseThreadOwnership()
+{
+#if UNITY_ANDROID
+ ReleaseGLES20Context();
+#endif
+}
+
+// and now the real magic
+void GfxDeviceGLES20_InitFramebufferDepthFormat()
+{
+ ((GFX_GL_IMPL&)GetRealGfxDevice()).InitFramebufferDepthFormat();
+}
+
+// ---------- verify state
+#if GFX_DEVICE_VERIFY_ENABLE
+void GFX_GL_IMPL::VerifyState()
+{
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+
+#if NV_STATE_FILTERING
+ #ifdef glDeleteBuffers
+ #undef glDeleteBuffers
+ #endif
+ #ifdef glBindBuffer
+ #undef glBindBuffer
+ #endif
+ #ifdef glVertexAttribPointer
+ #undef glVertexAttribPointer
+ #endif
+#endif
+
+#endif // GFX_SUPPORTS_OPENGLES20