summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp')
-rw-r--r--Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp3328
1 files changed, 3328 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp b/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp
new file mode 100644
index 0000000..240cb58
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/GfxDeviceGLES30.cpp
@@ -0,0 +1,3328 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES30
+#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/Graphics/Image.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "IncludesGLES30.h"
+#include "AssertGLES30.h"
+#include "ConstantBuffersGLES30.h"
+#include "ContextGLES30.h"
+#include "VBOGLES30.h"
+#include "TexturesGLES30.h"
+#include "CombinerGLES30.h"
+#include "GpuProgramsGLES30.h"
+#include "FixedFunctionStateGLES30.h"
+#include "ShaderGeneratorGLES30.h"
+#include "RenderTextureGLES30.h"
+#include "DebugGLES30.h"
+#include "TimerQueryGLES30.h"
+#include "TextureIdMapGLES30.h"
+#include "UtilsGLES30.h"
+#include "Runtime/GfxDevice/opengles30/TransformFeedbackSkinnedMesh.h"
+#include "DataBuffersGLES30.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.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"
+
+#define UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR 0
+
+#if UNITY_ANDROID
+#undef UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR
+#define UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR 1
+#endif
+
+#if UNITY_GLES3_ENTRYPOINTS_FROM_GETPROCADDR
+
+#define STRINGIFY(x) STRINGIFY2(x)
+#define STRINGIFY2(x) #x
+
+#define DO_GLFUNC(retval, name, ...) typedef void (GL_APIENTRYP PFN##name) (__VA_ARGS__); \
+ retval GL_APIENTRY name (__VA_ARGS__) \
+{ \
+ static PFN##name addr = NULL; \
+ if(!addr) \
+ { \
+ /*DBG_LOG_GLES30("Retrieving GLES3 proc address " STRINGIFY(name) );*/ \
+ addr = (PFN##name) GetGLExtProcAddress(STRINGIFY(name)); \
+ if(!addr) \
+ { \
+ Assert("Could not find GLES 3.0 entry point" STRINGIFY(name)); \
+ return (retval)0; \
+ } \
+ DBG_LOG_GLES30("Success\n"); \
+ } \
+ __builtin_return(__builtin_apply((void (*)(...))addr, __builtin_apply_args(), 100)); \
+ /*DBG_LOG_GLES30("Called " STRINGIFY(name) " successfully\n");*/ \
+}
+
+DO_GLFUNC(void, glBindBufferBase, GLenum target, GLuint index, GLuint buffer)
+DO_GLFUNC(void, glBindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr range)
+DO_GLFUNC(void, glBlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+DO_GLFUNC(void, glDeleteTransformFeedbacks, GLsizei n, const GLuint* ids)
+DO_GLFUNC(void, glDrawBuffers, GLsizei n, const GLenum* bufs)
+DO_GLFUNC(void, glGenQueries, GLsizei n, GLuint* ids)
+DO_GLFUNC(void, glGetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params)
+DO_GLFUNC(void, glGetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName)
+DO_GLFUNC(void, glGetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params)
+DO_GLFUNC(void, glGetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary)
+DO_GLFUNC(void, glGetQueryObjectuiv, GLuint id, GLenum pname, GLuint* params)
+DO_GLFUNC(GLuint, glGetUniformBlockIndex, GLuint program, const GLchar *uniformBlockName)
+DO_GLFUNC(void, glInvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments)
+DO_GLFUNC(void *, glMapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
+DO_GLFUNC(void, glProgramBinary, GLuint program, GLenum binaryFormat, const GLvoid* binary, GLsizei length)
+DO_GLFUNC(void, glReadBuffer, GLenum buf)
+DO_GLFUNC(void, glRenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+DO_GLFUNC(void, glUniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+DO_GLFUNC(GLboolean, glUnmapBuffer, GLenum target)
+DO_GLFUNC(void, glGenTransformFeedbacks, GLsizei n, GLuint *ids)
+DO_GLFUNC(void, glVertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
+DO_GLFUNC(void, glTransformFeedbackVaryings, GLuint program, GLsizei count, const char* const* varyings, GLenum bufferMode)
+DO_GLFUNC(void, glBeginTransformFeedback, GLenum primitivemode)
+DO_GLFUNC(void, glEndTransformFeedback)
+DO_GLFUNC(void, glFlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length)
+DO_GLFUNC(void, glGenVertexArrays, GLsizei n, GLuint* arrays)
+DO_GLFUNC(void, glDeleteVertexArrays, GLsizei n, const GLuint* arrays)
+DO_GLFUNC(void, glBindVertexArray, GLuint array)
+
+#undef DO_GLFUNC
+#undef STRINGIFY
+#undef STRINGIFY2
+
+#endif
+static void ClearFixedFunctionPrograms();
+
+// \todo [2013-04-16 pyry] UtilsGLES30 or similar for these?
+
+static int queryInt (UInt32 pname)
+{
+ int value = 0;
+ GLES_CHK(glGetIntegerv(pname, &value));
+ return value;
+}
+
+// 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
+
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+// local forward declarations
+struct DeviceStateGLES30;
+static void ApplyBackfaceMode( const DeviceStateGLES30& state );
+
+static FramebufferObjectManagerGLES30& GetFBOManager (DeviceStateGLES30& deviceState);
+
+// NOTE: GLES3.0 supports only 4 lights for now
+enum { kMaxSupportedVertexLightsByGLES30 = 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, GL_MAX,
+};
+
+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 GLenum kGLES30TextureDimensionTable[kTexDimCount] = {0, -1/*GL_TEXTURE_1D*/, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, 0};
+
+static const GLint kMinFilterES2[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+// --------------------------------------------------------------------------
+
+struct DeviceDepthStateGLES30 : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGLES30 : 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, DeviceDepthStateGLES30, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGLES30, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+// --------------------------------------------------------------------------
+struct TextureUnitStateGLES3
+{
+ 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 TextureUnitStateGLES3::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 ImmediateVertexGLES30 {
+ Vector3f vertex;
+ Vector3f normal;
+ UInt32 color;
+ Vector3f texCoords[8];
+};
+
+struct ImmediateModeGLES30 {
+ std::vector<ImmediateVertexGLES30> m_Vertices;
+ ImmediateVertexGLES30 m_Current;
+ GfxPrimitiveType m_Mode;
+
+ DataBufferGLES30* m_IndexBufferQuads;
+
+ ImmediateModeGLES30();
+ ~ImmediateModeGLES30();
+ void Invalidate();
+};
+
+// --------------------------------------------------------------------------
+struct DeviceStateGLES30
+{
+ 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;
+
+ TextureUnitStateGLES3
+ 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;
+
+ ConstantBuffersGLES30
+ m_CBs;
+
+ DynamicVBO* m_DynamicVBO;
+ bool vboContainsColor;
+
+ int viewport[4];
+ int scissorRect[4];
+
+
+ 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 DeviceDepthStateGLES30* m_CurrDepthState;
+ const DeviceStencilStateGLES30* m_CurrStencilState;
+ int m_StencilRef;
+ const DeviceRasterState* m_CurrRasterState;
+
+ MaterialPropertyBlock m_MaterialProperties;
+ ImmediateModeGLES30 m_Imm;
+
+ // Framebuffer objects.
+ FramebufferObjectManagerGLES30* m_fboManager;
+ FramebufferObjectGLES30* m_activeFbo; //!< Currently bound FBO.
+ FramebufferObjectGLES30* m_defaultFbo; //!< Default render target FBO or null if using default framebuffer (0).
+
+public:
+ DeviceStateGLES30();
+ void Invalidate();
+ void ComputeFixedFunctionState(FixedFunctionStateGLES30& state, const GfxFogParams& fog) const;
+
+ inline void ApplyTexGen( UInt32 unit );
+ inline void DropTexGen( UInt32 unit );
+};
+
+DeviceStateGLES30::DeviceStateGLES30()
+: m_DynamicVBO(0)
+, m_fboManager(0)
+, m_activeFbo (0)
+, m_defaultFbo(0)
+{
+ m_TextureIDGenerator = 0;
+}
+
+void DeviceStateGLES30::Invalidate()
+{
+ DBG_LOG_GLES30("Invalidate");
+ int i;
+
+ depthFunc = -1; //unknown
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = -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();
+
+ m_activeFbo = 0;
+
+ InvalidateVertexInputCacheGLES30();
+
+ GLESAssert();
+}
+
+void DeviceStateGLES30::ComputeFixedFunctionState(FixedFunctionStateGLES30& 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); // OpenGLES3.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.gles30.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. */
+ break;
+ case kFuncUnknown:
+ case kFuncDisabled:
+ case kFuncAlways:
+ state.alphaTest = kFuncDisabled;
+ default:
+ break;
+ }
+ }
+}
+
+inline void DeviceStateGLES30::ApplyTexGen( UInt32 unit )
+{
+ const TextureUnitStateGLES3& state = textures[unit];
+
+ positionTexGen = state.posForTexGen ? positionTexGen | (1<<unit)
+ : positionTexGen & ~(1<<unit);
+
+ normalTexGen = state.nrmForTexGen ? normalTexGen | (1<<unit)
+ : normalTexGen & ~(1<<unit);
+}
+
+inline void DeviceStateGLES30::DropTexGen( UInt32 unit )
+{
+ positionTexGen &= ~(1<<unit);
+ normalTexGen &= ~(1<<unit);
+}
+
+
+#include "GfxDeviceGLES30.h"
+#include "Runtime/GfxDevice/GLESCommon.h"
+
+void GfxDeviceGLES30_MarkWorldViewProjDirty()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES30DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+}
+
+void GfxDeviceGLES30_DisableDepthTest()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES30DeviceState(device).depthFunc = GL_NEVER;
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+}
+
+void GraphicsCaps::InitGLES30()
+{
+ // \todo [2013-04-16 pyry] Requires some serious cleanaup:
+ // - This functions shouldn't be member of GraphicsCaps (why it is?)
+ // - query extensions once, split to set and check from there
+ // - nicer wrapper for int caps queries
+
+ GLES_InitCommonCaps(this);
+ gGles3ExtFunc.InitExtFunc();
+
+ shaderCaps = kShaderLevel3;
+
+ maxLights = kMaxSupportedVertexLightsByGLES30; // vertex light count
+ hasAnisoFilter = QueryExtension("GL_EXT_texture_filter_anisotropic"); // has anisotropic filtering?
+ if (hasAnisoFilter)
+ GLES_CHK(glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisoLevel));
+ 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;
+ maxMRTs = std::min<int>(FramebufferAttachmentsGLES30::kMaxColorAttachments, queryInt(GL_MAX_COLOR_ATTACHMENTS));
+
+ 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 = true;
+ hasMipMaxLevel = true;
+
+ hasMultiSampleAutoResolve = false; // ES3 requires explicit glBlitFramebuffer() to do resolve. This affects how RenderTexture behaves.
+ hasMultiSample = true;
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+ hasBlendSub = true;
+ hasBlendMinMax = true;
+
+ hasS3TCCompression = false;
+
+ hasAutoMipMapGeneration = false; // \todo [2013-04-16 pyry] glGenMipmap() does exist
+
+ has3DTexture = false; // \todo [2013-04-16 pyry] Expose 3D textures
+
+ npot = kNPOTFull;
+ npotRT = npot;
+
+ hasRenderToTexture = true;
+ hasShadowCollectorPass = false;
+
+ hasHighPrecisionTextureCombiners = false; // \todo [2013-04-16 pyry] Yep. We can use highp (fp32) in FS. Should be enough.
+
+ hasRenderToCubemap = true;
+
+ // \note supportsTextureFormat[N < kTexFormatPCCount] = true
+
+ supportsTextureFormat[kTexFormatBGRA32] = QueryExtension("GL_APPLE_texture_format_BGRA8888");
+ 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[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");
+
+ supportsTextureFormat[kTexFormatETC_RGB4] = true; // \note Mapped to ETC2.
+ supportsTextureFormat[kTexFormatEAC_R] = true;
+ supportsTextureFormat[kTexFormatEAC_R_SIGNED] = true;
+ supportsTextureFormat[kTexFormatEAC_RG] = true;
+ supportsTextureFormat[kTexFormatEAC_RG_SIGNED] = true;
+ supportsTextureFormat[kTexFormatETC2_RGB] = true;
+ supportsTextureFormat[kTexFormatETC2_RGBA1] = true;
+ supportsTextureFormat[kTexFormatETC2_RGBA8] = true;
+
+ const bool supportsASTC = QueryExtension("GL_KHR_texture_compression_astc_ldr");
+ for(int loop = kTexFormatASTC_RGB_4x4; loop <= kTexFormatASTC_RGBA_12x12; loop++)
+ supportsTextureFormat[loop] = supportsASTC;
+
+ // \todo [2013-04-16 pyry] Support means nothing until we can expose these to shaders as well.
+ // It requires changes to shader translator.
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+ supportsRenderTextureFormat[kRTFormatDepth] = true;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = true;
+ supportsRenderTextureFormat[kRTFormatRGB565] = true;
+ supportsRenderTextureFormat[kRTFormatARGB4444] = true;
+ supportsRenderTextureFormat[kRTFormatARGB1555] = true;
+ supportsRenderTextureFormat[kRTFormatDefault] = true;
+ supportsRenderTextureFormat[kRTFormatA2R10G10B10] = true;
+ supportsRenderTextureFormat[kRTFormatARGB64] = false;
+ supportsRenderTextureFormat[kRTFormatR8] = true;
+ supportsRenderTextureFormat[kRTFormatARGBInt] = true;
+ supportsRenderTextureFormat[kRTFormatRGInt] = true;
+ supportsRenderTextureFormat[kRTFormatRInt] = true;
+
+ if (QueryExtension("GL_EXT_color_buffer_float"))
+ {
+ // Support for fp render targets was stripped from spec as last minute decision. Sigh..
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = true;
+ supportsRenderTextureFormat[kRTFormatRGHalf] = true;
+ supportsRenderTextureFormat[kRTFormatRHalf] = true;
+
+ supportsRenderTextureFormat[kRTFormatARGBFloat] = true;
+ supportsRenderTextureFormat[kRTFormatRGFloat] = true;
+ supportsRenderTextureFormat[kRTFormatRFloat] = true;
+
+ supportsRenderTextureFormat[kRTFormatDefaultHDR] = true; // ES3 backend uses R11F_G11F_B10F as default HDR format
+ }
+
+ 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);
+
+ hasNativeDepthTexture = true;
+ hasNativeShadowMap = true;
+
+ hasRenderTargetStencil = true;
+ hasTwoSidedStencil = true;
+ hasStencilInDepthTexture = true;
+ hasStencil = true;
+
+ has16BitFloatVertex = true;
+ needsToSwizzleVertexColors = false;
+
+ hasSRGBReadWrite = false; // \todo [2013-06-05 pyry] Doesn't function properly
+
+ disableSoftShadows = false;
+ buggyCameraRenderToCubemap = false;
+
+ hasShadowCollectorPass = false;
+
+ // Timer queries
+ gGraphicsCaps.hasTimerQuery = QueryExtension("GL_NV_timer_query");
+
+ // GLES3-specific variables.
+ gles30.maxAttributes = std::min<int>(kGLES3MaxVertexAttribs, queryInt(GL_MAX_VERTEX_ATTRIBS));
+ gles30.maxVaryings = queryInt(GL_MAX_VARYING_VECTORS);
+ gles30.maxSamples = queryInt(GL_MAX_SAMPLES);
+
+ hasTiledGPU = isPvrGpu || isAdrenoGpu || isMaliGpu;
+
+ const bool isAdreno330 = (rendererString.find("Adreno (TM) 330") != string::npos); // MSM8974 dev device, with rather unstable drivers.
+ const bool isMali628 = (rendererString.find("Mali-T628") != string::npos); // ARM TG4 dev device, mapbuffer broken.
+ const bool isGLESEmu = (rendererString.find("Mali OpenGL ES Emulator") != string::npos); // ARM OpenGL ES 3.0 emulator, glGetProgramBinary broken
+
+ gles30.useMapBuffer = true;
+ if(isAdrenoGpu || !isAdreno330)
+ gles30.useMapBuffer = false; // Buffer mapping is dead-slow on Adreno, but using BufferData() is broken on Adreno 330
+
+ if(isMali628)
+ gles30.useMapBuffer = false; // Mapbuffer broken on TG4
+
+ gles30.useProgramBinary = true; // Set true first, check actual support in Init call below.
+ gles30.useProgramBinary = GlslGpuProgramGLES30::InitBinaryShadersSupport();
+
+
+ gles30.useTFSkinning = true; // TF skinning used to be broken on adreno, worked around now.
+
+
+ // \todo [2013-04-17 pyry] Extension queries.
+
+ // \todo [2013-04-16 pyry] Figure out which gles20 flags make sense for ES3 as well.
+
+ // \todo [2013-04-16 pyry] Why init timer queries here?
+#if ENABLE_PROFILER
+ g_TimerQueriesGLES30.Init();
+#endif
+}
+
+GfxDevice* CreateGLES30GfxDevice()
+{
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_ANDROID
+ InitializeGLES30();
+#endif
+
+ gGraphicsCaps.InitGLES30();
+
+#if UNITY_EDITOR
+ return NULL;
+#else
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLES30GfxDevice","");
+#endif
+}
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ printf_console("Creating OpenGL ES 3.0 graphics device\n");
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+
+ OnCreate();
+ InvalidateState();
+ m_Renderer = kGfxRendererOpenGLES30;
+ 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;
+
+ extern RenderSurfaceBase* CreateBackBufferColorSurfaceGLES3();
+ SetBackBufferColorSurface(CreateBackBufferColorSurfaceGLES3());
+
+ extern RenderSurfaceBase* CreateBackBufferDepthSurfaceGLES3();
+ SetBackBufferDepthSurface(CreateBackBufferDepthSurfaceGLES3());
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+ TransformFeedbackSkinningInfo::CleanupTransformFeedbackShaders();
+ ClearFixedFunctionPrograms();
+ STATE.m_CBs.Clear();
+ delete STATE.m_DynamicVBO;
+ delete STATE.m_fboManager;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+#if UNITY_WIN || UNITY_ANDROID
+ ShutdownGLES30();
+#endif
+}
+
+static void ActivateTextureUnitGLES3 (DeviceStateGLES30& state, int unit)
+{
+ if (state.activeTextureUnit == unit)
+ return;
+ GLES_CHK(glActiveTexture(GL_TEXTURE0 + unit));
+ state.activeTextureUnit = unit;
+}
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ DBG_LOG_GLES30("InvalidateState");
+ m_FogParams.Invalidate();
+ STATE.transformState.Invalidate(m_BuiltinParamValues);
+ STATE.Invalidate();
+
+ 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, DeviceDepthStateGLES30()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGLES30& 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, DeviceStencilStateGLES30()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGLES30& 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 (STATE.m_activeFbo && STATE.m_activeFbo->GetNumColorAttachments() == 0)
+ mask = 0;
+
+ 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)
+ {
+ 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;
+
+ // \todo [2013-04-16 pyry] Alpha testing should be moved to shaders
+
+ if (gGraphicsCaps.gles30.hasAlphaTestQCOM && (alphaTest != STATE.alphaTest || alphaRef != STATE.alphaValue))
+ {
+ if (alphaTest != kFuncDisabled)
+ {
+ GLES_CHK(gGles3ExtFunc.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)
+{
+ DeviceDepthStateGLES30* devstate = (DeviceDepthStateGLES30*)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 DeviceStencilStateGLES30* st = static_cast<const DeviceStencilStateGLES30*>(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)
+{
+ // \todo [2013-04-16 pyry] Implement sRGB support:
+ // - In ES3 sRGB bit is tied to format and there is no way to create views with different format
+ // - This is used rather liberally from Camera
+ // -> Use sRGB FBO for emulation, and defer necessary blits until it is known whether they are needed
+ Assert("Not implemented");
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ DBG_LOG_GLES30("Clear(%d, (%.2f, %.2f, %.2f, %.2f), %.2f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil);
+
+ // \todo [2013-04-16 pyry] Integer render targets require use of glClearBuffer()
+ // \todo [2013-04-29 pyry] Here was a call that restored FBO binding to default one. Why?
+
+ if (STATE.m_activeFbo && STATE.m_activeFbo->GetNumColorAttachments() == 0)
+ 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)
+ {
+ 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], color[3] ));
+ }
+ 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 DeviceStateGLES30& state )
+{
+ DBG_LOG_GLES30("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_GLES30("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_GLES30("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_GLES30("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_GLES30("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_GLES30("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_GLES30("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_GLES30("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_GLES30("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_GLES30("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 ));
+
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ DBG_LOG_GLES30("DisableScissor()");
+ if (STATE.scissor != 0)
+ {
+ GLES_CHK(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ DBG_LOG_GLES30("IsScissorEnabled():returns %s", STATE.scissor == 1?"True":"False");
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ DBG_LOG_GLES30("GetScissorRect()");
+ 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 true;
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ DBG_LOG_GLES30("CreateTextureCombiners()");
+ TextureCombinersGLES3* implGLES = TextureCombinersGLES3::Create (count, texEnvs);
+ return TextureCombinersHandle (implGLES);
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ DBG_LOG_GLES30("DeleteTextureCombiners()");
+ TextureCombinersGLES3* implGLES = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersGLES3);
+ delete implGLES;
+ textureCombiners.Reset();
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ DBG_LOG_GLES30("SetTextureCombiners()");
+ TextureCombinersGLES3* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES3);
+ 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::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ DBG_LOG_GLES30("SetTextureCombiners()");
+ TextureCombinersGLES3* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES3);
+ 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
+ TextureUnitStateGLES3& 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
+ ActivateTextureUnitGLES3 (STATE, 0);
+
+}
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DBG_LOG_GLES30("SetTexture(%d %d)", unit, texture.m_ID);
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+
+ GLenum texType = kGLES30TextureDimensionTable[dim];
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+
+ if (texType == (GLenum)-1)
+ {
+ Assert("Not supported");
+ return;
+ }
+
+ // \todo [2013-04-16 pyry] Shouldn't we clear state still?
+ if (targetTex == 0)
+ return;
+
+ TextureUnitStateGLES3& currTex = STATE.textures[unit];
+ if (STATE.textureCount > unit && targetTex == currTex.texID)
+ return; // Already bound.
+
+ ActivateTextureUnitGLES3 (STATE, unit);
+
+ GLES_CHK(glBindTexture(texType, targetTex));
+
+ 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();
+
+ // \todo [2013-04-16 pyry] Lod bias is given from shader.
+ if (bias != std::numeric_limits<float>::infinity())
+ currTex.bias = bias;
+}
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ DBG_LOG_GLES30("SetTextureTransform()");
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ TextureUnitStateGLES3& 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);
+}
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ DBG_LOG_GLES30("SetTextureParams()");
+
+ TextureIdMapGLES30_QueryOrCreate(texture);
+
+ GLuint target = kGLES30TextureDimensionTable[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::SetMaterialProperties( const MaterialPropertyBlock& block )
+{
+ STATE.m_MaterialProperties = block;
+}
+
+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];
+
+ DBG_LOG_GLES30("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 )
+{
+ GlslGpuProgramGLES30& programGLES = static_cast<GlslGpuProgramGLES30&>(program->GetGpuProgram());
+ programGLES.GetGLProgram(fogMode, program->GetParams(fogMode), program->GetChannels());
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ //DBG_LOG_GLES30("IsShaderActive(%s): returns %s", GetShaderTypeString(type), STATE.shaderEnabledImpl[type] != kShaderImplUndefined?"True":"False");
+ //return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+ DBG_LOG_GLES30("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::SetConstantBufferInfo( int id, int size )
+{
+ STATE.m_CBs.SetCBInfo(id, size);
+}
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ DBG_LOG_GLES30("DisableLights(%d)", startLight);
+ startLight = std::min (startLight, gGraphicsCaps.maxLights);
+ STATE.vertexLightCount = startLight;
+ for (int i = startLight; i < kMaxSupportedVertexLightsByGLES30; ++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_GLES30("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 >= kMaxSupportedVertexLightsByGLES30)
+ return;
+
+ STATE.vertexLightTypes[light] = data.type;
+
+ // Transform lighting into view space
+ const Matrix4x4f& viewMat = m_BuiltinParamValues.GetMatrixParam(kShaderMatView);
+
+ // spot direction
+ Vector4f& spotDirection = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0SpotDirection + light));
+ if( data.spotAngle > 0.0f )
+ {
+ Vector3f d = viewMat.MultiplyVector3( (const Vector3f&)data.spotDirection );
+ spotDirection.Set(-d.x, -d.y, -d.z, 0.0f);
+ }
+ else
+ {
+ spotDirection.Set(0.0f, 0.0f, 1.0f, 0.0f);
+ }
+
+ Vector4f& position = m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + light));
+ if (data.type == kLightDirectional)
+ {
+ Vector3f p = viewMat.MultiplyVector3( (const Vector3f&)data.position );
+ position.Set(-p.x, -p.y, -p.z, 0.0f);
+ }
+ else
+ {
+ Vector3f p = viewMat.MultiplyPoint3( (const Vector3f&)data.position );
+ position.Set(p.x, p.y, p.z, 1.0f);
+ }
+
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + light), data.color);
+ if (data.spotAngle > 0.0f)
+ {
+ // spot attenuation formula taken from D3D9 fixed-function emulation
+ // see: VertexPipeD3D9.cpp
+ const float cosTheta = cosf(Deg2Rad(data.spotAngle)*0.25f);
+ const float cosPhi = cosf(Deg2Rad(data.spotAngle)*0.5f);
+ const float cosDiff = cosTheta - cosPhi;
+ m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Atten + light)).Set(cosPhi, (cosDiff != 0.0f) ? 1.0f / cosDiff : 1.0f, data.quadAtten, data.range*data.range);
+ }
+ else
+ {
+ // non-spot light
+ m_BuiltinParamValues.GetWritableVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Atten + light)).Set(-1.0f, 1.0f, data.quadAtten, data.range*data.range);
+ }
+}
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ DBG_LOG_GLES30("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_GLES30("EnableFog()");
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ m_FogParams = fog;
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ DBG_LOG_GLES30("DisableFog()");
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new GLES3VBO();
+ 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 DynamicGLES3VBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+// ---------- render textures
+
+static FramebufferObjectManagerGLES30& GetFBOManager (DeviceStateGLES30& deviceState)
+{
+ if (!deviceState.m_fboManager)
+ deviceState.m_fboManager = new FramebufferObjectManagerGLES30();
+ return *deviceState.m_fboManager;
+}
+
+static RenderSurfaceGLES30* CreateRenderTexture (TextureDimension dim, TextureID texID, UInt32 internalFormat, int width, int height, int depth)
+{
+ if (dim == kTexDim2D)
+ {
+ Assert(depth == 1);
+ return new RenderTexture2DGLES30(texID, internalFormat, width, height);
+ }
+ else if (dim == kTexDimCUBE)
+ {
+ Assert(width == height && depth == 1);
+ return new RenderTextureCubeGLES30(texID, internalFormat, width, height);
+ }
+ else
+ {
+ Assert(!"Not supported");
+ return 0;
+ }
+}
+
+static RenderSurfaceGLES30* CreateShadowMapRenderTexture (TextureDimension dim, TextureID texID, UInt32 internalFormat, int width, int height, int depth)
+{
+ // \note Assumes that constructor binds texID!
+ if (dim == kTexDim2D)
+ {
+ Assert(depth == 1);
+ RenderTexture2DGLES30* tex = new RenderTexture2DGLES30(texID, internalFormat, width, height);
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL));
+ return tex;
+ }
+ else if (dim == kTexDimCUBE)
+ {
+ Assert(width == height && depth == 1);
+ RenderTextureCubeGLES30* tex = new RenderTextureCubeGLES30(texID, internalFormat, width, height);
+ GLES_CHK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL));
+ return tex;
+ }
+ else
+ {
+ Assert(!"Not supported");
+ return 0;
+ }
+}
+
+static RenderSurfaceGLES30* CreateRenderBuffer (TextureDimension dim, UInt32 internalFormat, int width, int height, int depth, int numSamples)
+{
+ if (dim == kTexDim2D)
+ {
+ Assert(depth == 1);
+ return new RenderBufferGLES30(internalFormat, width, height, numSamples);
+ }
+ else if (dim == kTexDimCUBE)
+ {
+ Assert(width == height && depth == 1);
+ return new RenderBufferCubeGLES30(internalFormat, width, height, numSamples);
+ }
+ else
+ {
+ Assert(!"Not supported");
+ return 0;
+ }
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ DBG_LOG_GLES30("CreateRenderColorSurface(id = %d, %dx%dx%d, samples = %d, dim = %d, fmt = %d, flags = 0x%04x)", textureID.m_ID, width, height, depth, samples, dim, format, createFlags);
+
+ // \note Sample count 0 and 1 both map to non-multisampled textures.
+
+ if (createFlags & kSurfaceCreateNeverUsed)
+ {
+ // Use dummy surface that is not backed by any real GL object.
+ return RenderSurfaceHandle(new DummyRenderSurfaceGLES30(width, height));
+ }
+ else
+ {
+ const bool isSRGB = (createFlags & kSurfaceCreateSRGB) != 0;
+ const UInt32 internalFormat = isSRGB ? GL_SRGB8_ALPHA8 : GetColorFormatGLES30(format);
+ const bool useRBO = textureID.m_ID == 0 || samples > 1;
+
+ Assert(internalFormat != GL_NONE);
+ Assert((createFlags & kSurfaceCreateShadowmap) == 0);
+
+ if (useRBO)
+ {
+ // \todo [2013-06-04 pyry] There is no global graphics caps for max samples.
+ const int numSamplesToUse = std::min(gGraphicsCaps.gles30.maxSamples, std::max(samples,1));
+ return RenderSurfaceHandle(CreateRenderBuffer(dim, internalFormat, width, height, depth, numSamplesToUse));
+ }
+ else
+ return RenderSurfaceHandle(CreateRenderTexture(dim, textureID, internalFormat, width, height, depth));
+ }
+
+ return RenderSurfaceHandle(0);
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ DBG_LOG_GLES30("CreateRenderDepthSurface(id = %d, %dx%d, samples = %d, dim = %d, fmt = %d, createFlags = 0x%04x)", textureID.m_ID, width, height, samples, dim, depthFormat, createFlags);
+
+ // \note Sample count 0 and 1 both map to non-multisampled textures.
+
+ if (depthFormat == kDepthFormatNone || (createFlags & kSurfaceCreateNeverUsed) != 0)
+ {
+ // Umm... Assuming that we don't want depth buffer at all, but still this is called?
+ return RenderSurfaceHandle(new DummyRenderSurfaceGLES30(width, height));
+ }
+ else
+ {
+ const bool shadowMap = (createFlags & kSurfaceCreateShadowmap) != 0;
+ const bool useStencil = !shadowMap;
+ const UInt32 internalFormat = useStencil ? GetDepthStencilFormatGLES30(depthFormat) : GetDepthOnlyFormatGLES30(depthFormat);
+ const bool useRBO = textureID.m_ID == 0 || samples > 1;
+
+ Assert(internalFormat != GL_NONE);
+ Assert(!shadowMap || !useRBO);
+
+ if (useRBO)
+ {
+ // \todo [2013-06-04 pyry] There is no global graphics caps for max samples.
+ const int numSamplesToUse = std::min(gGraphicsCaps.gles30.maxSamples, std::max(samples,1));
+ return RenderSurfaceHandle(CreateRenderBuffer(dim, internalFormat, width, height, 1, numSamplesToUse));
+ }
+ else if (shadowMap)
+ return RenderSurfaceHandle(CreateShadowMapRenderTexture(dim, textureID, internalFormat, width, height, 1));
+ else
+ return RenderSurfaceHandle(CreateRenderTexture(dim, textureID, internalFormat, width, height, 1));
+ }
+
+ return RenderSurfaceHandle(0);
+}
+
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ DBG_LOG_GLES30("DestroyRenderSurface(%p)", rs.object);
+
+ if (!rs.IsValid())
+ return;
+
+ RenderSurfaceGLES30* renderSurface = static_cast<RenderSurfaceGLES30*>(rs.object);
+
+ // Default FBO should not be managed from outside.
+ Assert(!STATE.m_defaultFbo || !IsInFramebufferAttachmentsGLES30(*STATE.m_defaultFbo->GetAttachments(), renderSurface));
+
+ // If rs was attached to current FBO, unbind it.
+ if (STATE.m_activeFbo && IsInFramebufferAttachmentsGLES30(*STATE.m_activeFbo->GetAttachments(), renderSurface))
+ {
+ if (STATE.m_defaultFbo)
+ BindFramebufferObjectGLES30(STATE.m_defaultFbo);
+ else
+ BindDefaultFramebufferGLES30();
+
+ STATE.m_activeFbo = STATE.m_defaultFbo;
+ }
+
+ // Delete FBOs where renderSurface was attached.
+ STATE.m_fboManager->InvalidateSurface(renderSurface);
+
+ delete renderSurface;
+ rs.object = 0;
+}
+
+static bool IsAnyRSHandleValid (const RenderSurfaceHandle* handles, int numHandles)
+{
+ for (int ndx = 0; ndx < numHandles; ndx++)
+ {
+ if (handles[ndx].IsValid())
+ return true;
+ }
+ return false;
+}
+
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+ bool hasColor = !colorHandles[0].object->backBuffer;
+ bool hasDepth = !depthHandle.object->backBuffer;
+
+ DBG_LOG_GLES30("SetRenderTargets(count = %d, color = %s, depth = %s, mip = %d, face = %d)", count, (hasColor ? "yes" : "no"), (hasDepth ? "yes" : "no"), mipLevel, face);
+
+ // \todo [2013-05-06 pyry] Enable SetRenderTargets() variant with flags and check if we should discard buffers.
+ // \todo [2013-05-02 pyry] It probably makes sense to do RT change deferred.
+
+ Assert(count <= FramebufferAttachmentsGLES30::kMaxColorAttachments);
+
+ Assert(colorHandles[0].IsValid() && depthHandle.IsValid());
+ Assert(colorHandles[0].object->backBuffer == depthHandle.object->backBuffer);
+
+ if(!hasColor && !hasDepth)
+ {
+ // Assuming default FB - right?
+ if (STATE.m_activeFbo != STATE.m_defaultFbo)
+ {
+ if (STATE.m_defaultFbo)
+ BindFramebufferObjectGLES30(STATE.m_defaultFbo);
+ else
+ BindDefaultFramebufferGLES30();
+
+ STATE.m_activeFbo = STATE.m_defaultFbo;
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange();
+ }
+ }
+ else
+ {
+ // Translate to FramebufferAttachments
+ FramebufferAttachmentsGLES30 attachments;
+
+ attachments.numColorAttachments = count;
+ attachments.depthStencil = static_cast<RenderSurfaceGLES30*>(depthHandle.object);
+ attachments.cubemapFace = face;
+
+ for (int ndx = 0; ndx < count; ndx++)
+ attachments.color[ndx] = static_cast<RenderSurfaceGLES30*>(colorHandles[ndx].object);
+
+ // Create (or fetch from cache) FBO
+ FramebufferObjectGLES30* fbo = GetFBOManager(STATE).GetFramebufferObject(attachments);
+
+ if (STATE.m_activeFbo != fbo)
+ {
+ BindFramebufferObjectGLES30(fbo);
+ STATE.m_activeFbo = fbo;
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange();
+ }
+ }
+}
+
+void GFX_GL_IMPL::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ DBG_LOG_GLES30("ResolveColorSurface(src = %p, dst = %p)", srcHandle.object, dstHandle.object);
+
+ // Fetch temporary FBOs for resolve - use single color attachment.
+ FramebufferObjectGLES30* srcFbo = 0;
+ FramebufferObjectGLES30* dstFbo = 0;
+
+ Assert(srcHandle.object && dstHandle.object);
+
+ for (int ndx = 0; ndx < 2; ndx++)
+ {
+ FramebufferAttachmentsGLES30 attachments;
+ attachments.numColorAttachments = 1;
+ attachments.color[0] = static_cast<RenderSurfaceGLES30*>(ndx ? srcHandle.object : dstHandle.object);
+
+ (ndx ? srcFbo : dstFbo) = GetFBOManager(STATE).GetFramebufferObject(attachments);
+ }
+
+ Assert(srcFbo && dstFbo);
+
+ int width = static_cast<RenderSurfaceGLES30*>(dstHandle.object)->GetWidth();
+ int height = static_cast<RenderSurfaceGLES30*>(dstHandle.object)->GetHeight();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo->GetFboID());
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo->GetFboID());
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
+
+ // Restore old FBO binding
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, STATE.m_activeFbo ? STATE.m_activeFbo->GetFboID() : 0));
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ Assert(0 <= index && index <= FramebufferAttachmentsGLES30::kMaxColorAttachments);
+
+ if (STATE.m_activeFbo)
+ return RenderSurfaceHandle(STATE.m_activeFbo->GetColorAttachment(index));
+ else
+ return RenderSurfaceHandle(0);
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ if (STATE.m_activeFbo)
+ return RenderSurfaceHandle(STATE.m_activeFbo->GetDepthStencilAttachment());
+ else
+ return RenderSurfaceHandle(0);
+}
+
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+ // \todo [2013-04-29 pyry] Implement handling for flags!
+}
+
+void GFX_GL_IMPL::DiscardContents (RenderSurfaceHandle& rs)
+{
+ DBG_LOG_GLES30("DiscardContents(%p)", rs.object);
+
+ if (!rs.IsValid())
+ return; // \todo [2013-06-05 pyry] Bug in threaded device code causes DiscardContents() calls to invalid handles.
+
+ FramebufferObjectGLES30* curFbo = STATE.m_activeFbo;
+ GLenum discardAttachments[FramebufferAttachmentsGLES30::kMaxColorAttachments+1];
+ int attachNdx = 0;
+
+ if (curFbo)
+ {
+ // Check if rs is attached to current fbo.
+ for (int colorNdx = 0; colorNdx < curFbo->GetNumColorAttachments(); colorNdx++)
+ {
+ if (rs.object == curFbo->GetColorAttachment(colorNdx))
+ discardAttachments[attachNdx++] = GL_COLOR_ATTACHMENT0+colorNdx;
+ }
+
+ if (rs.object == curFbo->GetDepthStencilAttachment())
+ {
+ // \todo [2013-05-02 pyry] Should we check if FBO actually has stencil attachment enabled?
+ discardAttachments[attachNdx++] = GL_DEPTH_STENCIL_ATTACHMENT;
+ }
+ }
+
+ Assert(attachNdx <= (int)(sizeof(discardAttachments)/sizeof(discardAttachments[0])));
+ if (attachNdx > 0)
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, attachNdx, &discardAttachments[attachNdx]));
+
+ DBG_LOG_GLES30(" .. discarded in current FBO = %s", (attachNdx > 0) ? "true" : "false");
+
+ // If attachment was not discarded yet, do it later when it is bound.
+ if (attachNdx == 0)
+ {
+ RenderSurfaceGLES30* renderSurface = static_cast<RenderSurfaceGLES30*>(rs.object);
+ renderSurface->SetFlags(renderSurface->GetFlags() | RenderSurfaceGLES30::kDiscardOnBind);
+ }
+}
+
+// ---------- 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 )
+{
+ ::UploadTexture2DGLES3( 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 )
+{
+ ::UploadTextureSubData2DGLES3( 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 )
+{
+ ::UploadTextureCubeGLES3( 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 )
+{
+ // \todo [2013-04-16 pyry] Add support.
+ 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 )
+ {
+ TextureUnitStateGLES3& 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;
+}
+
+static void ResetFboBindingToDefault (DeviceStateGLES30& state)
+{
+ if (state.m_activeFbo != state.m_defaultFbo)
+ {
+ if (state.m_defaultFbo)
+ BindFramebufferObjectGLES30(state.m_defaultFbo);
+ else
+ BindDefaultFramebufferGLES30();
+ state.m_activeFbo = state.m_defaultFbo;
+ }
+}
+
+void GFX_GL_IMPL::BeginFrame()
+{
+ DBG_LOG_GLES30("BeginFrame()");
+ m_InsideFrame = true;
+
+ if (gGraphicsCaps.hasTiledGPU)
+ {
+ // \todo [2013-05-02 pyry] Should we reset FBO binding here?
+ ResetFboBindingToDefault(STATE);
+ if(STATE.scissor)
+ GLES_CHK(glDisable(GL_SCISSOR_TEST));
+ GLES_CHK(glViewport(0, 0, 65536, 65536));
+ GLES_CHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
+ GLES_CHK(glClearStencil(0));
+ GLES_CHK(glClearDepthf(1.0f));
+ GLES_CHK(glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT));
+ GLES_CHK(glViewport(STATE.viewport[0], STATE.viewport[1], STATE.viewport[2], STATE.viewport[3]));
+ if(STATE.scissor)
+ GLES_CHK(glEnable(GL_SCISSOR_TEST));
+
+ }
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ // \todo [2013-05-02 pyry] Do we really want to reset FBO binding here?
+ ResetFboBindingToDefault(STATE);
+ if (STATE.m_activeFbo != 0)
+ {
+ // If rendering to FBO, discard contents.
+ static const GLenum attachments[] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT };
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, sizeof(attachments)/sizeof(attachments[0]), &attachments[0]));
+ }
+ else
+ {
+ // System "FBO", discard only depthstencil
+ static const GLenum attachments[] = { GL_DEPTH, GL_STENCIL };
+ GLES_CHK(glInvalidateFramebuffer(GL_FRAMEBUFFER, sizeof(attachments)/sizeof(attachments[0]), &attachments[0]));
+ }
+
+ GetBufferManagerGLES30()->AdvanceFrame();
+
+ DBG_LOG_GLES30("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_GLES30("====================================");
+ DBG_LOG_GLES30("====================================");
+ DBG_LOG_GLES30("PresentFrame");
+ DBG_LOG_GLES30("====================================");
+ DBG_LOG_GLES30("====================================");
+
+#if UNITY_WIN
+ PresentContextGLES30();
+#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;
+
+
+ImmediateModeGLES30::ImmediateModeGLES30()
+ : m_Mode (kPrimitiveTriangles)
+ , m_IndexBufferQuads(0)
+{
+#if 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;
+ }
+#endif
+}
+
+ImmediateModeGLES30::~ImmediateModeGLES30()
+{
+ Invalidate();
+}
+
+void ImmediateModeGLES30::Invalidate()
+{
+ if (m_IndexBufferQuads)
+ {
+ m_IndexBufferQuads->Release();
+ m_IndexBufferQuads = 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()
+{
+ if (STATE.m_Imm.m_Vertices.empty())
+ return;
+
+ const UInt32 minBufferThreshold = 2048;
+
+ const int numVertices = STATE.m_Imm.m_Vertices.size();
+ const UInt32 vertexBufStride = (UInt32)sizeof(ImmediateVertexGLES30);
+ const UInt32 vertexBufUsage = GL_STREAM_DRAW;
+ const UInt32 vertexBufSize = vertexBufStride*numVertices;
+ const bool useBuffer = vertexBufSize >= minBufferThreshold;
+ DataBufferGLES30* vertexBuffer = useBuffer ? GetBufferManagerGLES30()->AcquireBuffer(numVertices*vertexBufStride, vertexBufUsage) : 0;
+ VertexArrayInfoGLES30 vertexSetup;
+
+ // \todo [2013-05-31 pyry] Recreate or update?
+ if (vertexBuffer)
+ vertexBuffer->RecreateWithData(vertexBufSize, vertexBufUsage, &STATE.m_Imm.m_Vertices[0]);
+
+ if (STATE.m_Imm.m_Mode == kPrimitiveQuads && !STATE.m_Imm.m_IndexBufferQuads)
+ {
+ // \todo [2013-05-31 pyry] Move somewhere else.
+ const int quadCount = kMaxImmediateVerticesPerDraw/4;
+ const int quadIndexCount = quadCount * 6;
+ const int indexBufferSize = quadIndexCount * sizeof(UInt16);
+ const UInt32 indexBufferUsage = GL_STATIC_DRAW;
+ std::vector<UInt16> quadIndices (quadIndexCount);
+
+ for (int quadNdx = 0; quadNdx < quadCount; ++quadNdx)
+ {
+ const UInt16 srcBaseNdx = quadNdx*4;
+ const int dstBaseNdx = quadNdx*6;
+ quadIndices[dstBaseNdx + 0] = srcBaseNdx + 1;
+ quadIndices[dstBaseNdx + 1] = srcBaseNdx + 2;
+ quadIndices[dstBaseNdx + 2] = srcBaseNdx;
+ quadIndices[dstBaseNdx + 3] = srcBaseNdx + 2;
+ quadIndices[dstBaseNdx + 4] = srcBaseNdx + 3;
+ quadIndices[dstBaseNdx + 5] = srcBaseNdx;
+ }
+
+ STATE.m_Imm.m_IndexBufferQuads = GetBufferManagerGLES30()->AcquireBuffer(indexBufferSize, indexBufferUsage);
+ STATE.m_Imm.m_IndexBufferQuads->RecreateWithData(indexBufferSize, indexBufferUsage, &quadIndices[0]);
+ }
+
+ // Fill in vertex setup info.
+ {
+ const UInt8* basePtr = vertexBuffer ? 0 : (const UInt8*)&STATE.m_Imm.m_Vertices[0];
+ const UInt32 buffer = vertexBuffer ? vertexBuffer->GetBuffer() : 0;
+
+ // \todo [2013-05-31 pyry] Do not send unused attributes!
+ vertexSetup.enabledArrays = (1<<kGLES3AttribLocationPosition)
+ | (1<<kGLES3AttribLocationColor)
+ | (1<<kGLES3AttribLocationNormal);
+
+ // All are sourcing from same buffer
+ for (int i = 0; i < kGLES3MaxVertexAttribs; i++)
+ vertexSetup.buffers[i] = buffer;
+
+ vertexSetup.arrays[kGLES3AttribLocationPosition] = VertexInputInfoGLES30(basePtr + 0, kChannelFormatFloat, 3, vertexBufStride);
+ vertexSetup.arrays[kGLES3AttribLocationNormal] = VertexInputInfoGLES30(basePtr + 3*sizeof(float), kChannelFormatFloat, 3, vertexBufStride);
+ vertexSetup.arrays[kGLES3AttribLocationColor] = VertexInputInfoGLES30(basePtr + 6*sizeof(float), kChannelFormatColor, 4, vertexBufStride);
+ UInt32 curOffset = 6*sizeof(float) + sizeof(UInt32);
+
+ for (int texCoordNdx = 0; texCoordNdx < (kGLES3MaxVertexAttribs-kGLES3AttribLocationTexCoord0); texCoordNdx++)
+ {
+ const int attribLoc = kGLES3AttribLocationTexCoord0+texCoordNdx;
+
+ if (attribLoc < gGraphicsCaps.gles30.maxAttributes)
+ {
+ vertexSetup.enabledArrays |= (1<<attribLoc);
+ vertexSetup.arrays[kGLES3AttribLocationTexCoord0+texCoordNdx] =
+ VertexInputInfoGLES30(basePtr + curOffset, kChannelFormatFloat, 3, vertexBufStride);
+ curOffset += 3*sizeof(float);
+ }
+ }
+ }
+
+ // Setup state
+ BeforeDrawCall(true /* immediate */);
+ SetupDefaultVertexArrayStateGLES30(vertexSetup);
+
+ switch (STATE.m_Imm.m_Mode)
+ {
+ case kPrimitiveTriangles:
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, numVertices));
+ m_Stats.AddDrawCall(numVertices / 3, numVertices);
+ break;
+
+ case kPrimitiveTriangleStripDeprecated:
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));
+ m_Stats.AddDrawCall(numVertices - 2, numVertices);
+ break;
+
+ case kPrimitiveLines:
+ GLES_CHK(glDrawArrays(GL_LINES, 0, numVertices));
+ m_Stats.AddDrawCall(numVertices / 2, numVertices);
+ break;
+
+ case kPrimitiveQuads:
+ Assert(STATE.m_Imm.m_IndexBufferQuads);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, STATE.m_Imm.m_IndexBufferQuads->GetBuffer()));
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (numVertices/4)*6, GL_UNSIGNED_SHORT, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ m_Stats.AddDrawCall(numVertices / 2, numVertices);
+ break;
+
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+
+ if (vertexBuffer)
+ vertexBuffer->Release();
+}
+
+// ---------- readback path
+
+static int GetFirstValidColorAttachmentNdx (const FramebufferObjectGLES30* fbo)
+{
+ for (int colorNdx = 0; colorNdx < fbo->GetNumColorAttachments(); colorNdx++)
+ {
+ if (fbo->GetColorAttachment(colorNdx))
+ return colorNdx;
+ }
+
+ return -1;
+}
+
+static bool IsRenderSurfaceFormatCompatibleWithRGBA8Read (UInt32 format)
+{
+ switch (format)
+ {
+ case GL_RGBA8:
+ case GL_RGB8:
+ case GL_RG8:
+ case GL_R8:
+ case GL_RGB565:
+ case GL_RGB10_A2:
+ case GL_RGBA4:
+ case GL_RGB5_A1:
+ case GL_SRGB8_ALPHA8:
+ case GL_SRGB8:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool CanReadRGBA8FromFirstColorAttachment (const FramebufferObjectGLES30* fbo)
+{
+ int ndx = GetFirstValidColorAttachmentNdx(fbo);
+ if (ndx >= 0)
+ return IsRenderSurfaceFormatCompatibleWithRGBA8Read(fbo->GetColorAttachment(ndx)->GetFormat());
+ else
+ return false;
+}
+
+static void ReadPixelsFromDefaultFramebufferRGBA8 (int x, int y, int width, int height, UInt8* dstPtr)
+{
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dstPtr));
+}
+
+static void ReadPixelsFromActiveFramebufferObjectRGBA8 (DeviceStateGLES30& state, int x, int y, int width, int height, UInt8* dstPtr)
+{
+ Assert(state.m_activeFbo != 0);
+ Assert(CanReadRGBA8FromFirstColorAttachment(state.m_activeFbo));
+
+ FramebufferObjectGLES30* activeFbo = state.m_activeFbo;
+ const int colorNdx = GetFirstValidColorAttachmentNdx(state.m_activeFbo);
+ const RenderSurfaceGLES30* colorSurface = activeFbo->GetColorAttachment(colorNdx);
+ const bool requiresResolve = colorSurface->GetNumSamples() > 1;
+
+ if (requiresResolve)
+ {
+ FramebufferObjectManagerGLES30& fboManager = GetFBOManager(state);
+ FramebufferObjectGLES30* resolveBuffer = GetResolveFramebufferObjectGLES30(&fboManager, colorSurface->GetFormat(), 0,
+ colorSurface->GetWidth(), colorSurface->GetHeight());
+
+ GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
+ GLES_CHK(glDrawBuffers(1, &drawBuffer));
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveBuffer->GetFboID()));
+
+ // \note active FBO is already GL_READ_FRAMEBUFFER
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0+colorNdx));
+
+ // Resolve blit.
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
+
+ // Read from resolve buffer.
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0));
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dstPtr));
+
+ // Restore binding.
+ BindFramebufferObjectGLES30(activeFbo);
+ }
+ else
+ {
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0+colorNdx));
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dstPtr));
+ }
+}
+
+void GFX_GL_IMPL::ResolveDepthIntoTexture (RenderSurfaceHandle colorHandle, RenderSurfaceHandle depthHandle)
+{
+ FramebufferAttachmentsGLES30 att;
+ att.numColorAttachments = 1;
+ att.color[0] = static_cast<RenderSurfaceGLES30 *>(colorHandle.object);
+ att.depthStencil = static_cast<RenderSurfaceGLES30 *>(depthHandle.object);
+ att.cubemapFace = kCubeFaceUnknown;
+
+ FramebufferObjectGLES30 *helperFBO = GetFBOManager(STATE).GetFramebufferObject(att);
+ FramebufferObjectGLES30 *oldFBO = STATE.m_activeFbo;
+
+
+ // use the full size of the depth buffer, sub-rects are not needed and might not work on some hardware
+ GLint x = 0;
+ GLint y = 0;
+ GLint width = att.depthStencil->GetWidth();
+ GLint height = att.depthStencil->GetHeight();
+
+ // bind helper FBO
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, helperFBO->GetFboID()));
+
+ GLES_CHK(glReadBuffer (GL_NONE));
+
+ // blit
+ GLES_CHK(glBindFramebuffer (GL_READ_FRAMEBUFFER, oldFBO->GetFboID()));
+ GLES_CHK(glBindFramebuffer (GL_DRAW_FRAMEBUFFER, helperFBO->GetFboID()));
+ GLES_CHK(glBlitFramebuffer (x, y, x + width, y + height, x, y, x + width, y + height, GL_DEPTH_BUFFER_BIT, GL_NEAREST));
+
+ // restore the previously bound FBO
+ GLES_CHK(glBindFramebuffer (GL_FRAMEBUFFER, oldFBO->GetFboID()));
+
+
+}
+
+
+bool GFX_GL_IMPL::CaptureScreenshot (int left, int bottom, int width, int height, UInt8* rgba32)
+{
+ if (STATE.m_activeFbo)
+ {
+ if (CanReadRGBA8FromFirstColorAttachment(STATE.m_activeFbo))
+ {
+ ReadPixelsFromActiveFramebufferObjectRGBA8(STATE, left, bottom, width, height, rgba32);
+ return true;
+ }
+ else
+ {
+ ErrorString("Active FBO is not compatible with screenshots");
+ return false;
+ }
+ }
+ else
+ {
+ ReadPixelsFromDefaultFramebufferRGBA8(left, bottom, width, height, rgba32);
+ return true;
+ }
+}
+
+bool GFX_GL_IMPL::ReadbackImage (ImageReference& image, int left, int bottom, int width, int height, int destX, int destY)
+{
+ const bool coordsOk = destX == 0;
+ const bool formatOk = image.GetFormat() == kTexFormatRGBA32;
+ const bool strideOk = image.GetRowBytes() == 4*width;
+ const bool directRead = coordsOk && formatOk && strideOk;
+
+ if (directRead)
+ return CaptureScreenshot(left, bottom, width, height, image.GetRowPtr(destY));
+ else
+ {
+ std::vector<UInt8> tmpBuf(width*height*4);
+ if (!CaptureScreenshot(left, bottom, width, height, &tmpBuf[0]))
+ return false;
+
+ ImageReference blitSrc(width, height, 4*width, kTexFormatRGBA32, &tmpBuf[0]);
+ image.BlitImage(destX, destY, blitSrc);
+
+ return true;
+ }
+}
+
+void GFX_GL_IMPL::GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ if (!rs.IsValid())
+ return;
+
+ RenderSurfaceGLES30* dstColorSurface = static_cast<RenderSurfaceGLES30*>(rs.object);
+ RenderSurfaceGLES30* dstDepthSurface = static_cast<RenderSurfaceGLES30*>(rd.object);
+
+ // Grabbing to MSAA targets is not supported.
+ if (dstColorSurface->GetNumSamples() > 1 || (dstDepthSurface && dstDepthSurface->GetNumSamples() > 1))
+ {
+ ErrorString("GrabIntoRenderTexture(): Grabbing to MSAA RenderSurfaces is not supported");
+ return;
+ }
+
+ if (dstColorSurface->GetType() == RenderSurfaceGLES30::kTypeTexture2D && !dstDepthSurface && !STATE.m_activeFbo)
+ {
+ // Simple path: use glCopyTexSubImage()
+ RenderTexture2DGLES30* dstColorTex = static_cast<RenderTexture2DGLES30*>(dstColorSurface);
+
+ GetRealGfxDevice().SetTexture(kShaderFragment, 0, 0, dstColorTex->GetTextureID(), kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x, y, width, height));
+ }
+ else
+ {
+ // Temporary dst fbo.
+ FramebufferObjectManagerGLES30& fboManager = GetFBOManager(STATE);
+ FramebufferObjectGLES30* dstFbo = 0;
+ {
+ FramebufferAttachmentsGLES30 attachments;
+ attachments.numColorAttachments = 1;
+ attachments.color[0] = dstColorSurface;
+ attachments.depthStencil = dstDepthSurface;
+
+ dstFbo = fboManager.GetFramebufferObject(attachments);
+ }
+
+ bool copyColor = false;
+ bool copyDepth = false;
+ bool colorNeedsResolve = false;
+ bool depthNeedsResolve = false;
+ UInt32 srcColorFormat = 0;
+ UInt32 srcDepthFormat = 0;
+ GLenum srcColorAttachment = 0;
+
+ if (STATE.m_activeFbo)
+ {
+ const int srcColorNdx = GetFirstValidColorAttachmentNdx(STATE.m_activeFbo);
+ const RenderSurfaceGLES30* srcColorSurface = srcColorNdx >= 0 ? STATE.m_activeFbo->GetColorAttachment(srcColorNdx) : 0;
+ const RenderSurfaceGLES30* srcDepthSurface = STATE.m_activeFbo->GetDepthStencilAttachment() ? STATE.m_activeFbo->GetDepthStencilAttachment() : 0;
+
+ copyColor = srcColorSurface && dstColorSurface;
+ copyDepth = dstColorSurface && dstDepthSurface;
+ colorNeedsResolve = copyColor && srcColorSurface->GetNumSamples() > 1;
+ depthNeedsResolve = copyDepth && srcDepthSurface->GetNumSamples() > 1;
+ srcColorFormat = srcColorSurface ? srcColorSurface->GetFormat() : 0;
+ srcDepthFormat = srcDepthSurface ? srcDepthSurface->GetFormat() : 0;
+ srcColorAttachment = GL_COLOR_ATTACHMENT0 + srcColorNdx;
+ }
+ else
+ {
+ const bool isMSAA = queryInt(GL_SAMPLE_BUFFERS) > 0;
+
+ srcColorFormat = GetDefaultFramebufferColorFormatGLES30();
+ srcDepthFormat = GetDefaultFramebufferDepthFormatGLES30();
+ copyColor = srcColorFormat != 0 && dstColorSurface;
+ copyDepth = srcDepthFormat != 0 && dstDepthSurface;
+ colorNeedsResolve = isMSAA;
+ depthNeedsResolve = isMSAA;
+ srcColorAttachment = GL_BACK;
+ }
+
+ const bool colorFormatMatch = !copyColor || srcColorFormat == dstColorSurface->GetFormat();
+ const bool depthFormatMatch = !copyDepth || srcDepthFormat == dstDepthSurface->GetFormat();
+ const bool copyBoundsOk = x == 0 && y == 0;
+ const bool copyDirectly = (!colorNeedsResolve || (colorFormatMatch && copyBoundsOk)) && (!depthNeedsResolve || (depthFormatMatch && copyBoundsOk));
+ const UInt32 blitBuffers = (copyColor?GL_COLOR_BUFFER_BIT:0)|(copyDepth?GL_DEPTH_BUFFER_BIT:0);
+
+ // \note There are blits that are not supported altogether. For example blitting
+ // from unorm to integer format. Supporting them would require emulating the
+ // blit and even in such case the semantics are rather vague. So we just rely
+ // on GL giving an error if user attempts to do something strange.
+
+ if (copyDirectly)
+ {
+ GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo->GetFboID()));
+ GLES_CHK(glDrawBuffers(1, &drawBuffer));
+
+ // \note active FBO is already GL_READ_FRAMEBUFFER
+ GLES_CHK(glReadBuffer(srcColorAttachment));
+
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, blitBuffers, GL_NEAREST));
+ }
+ else
+ {
+ Assert(colorNeedsResolve || depthNeedsResolve);
+
+ GLenum drawBuffer = GL_COLOR_ATTACHMENT0;
+ GLES_CHK(glDrawBuffers(1, &drawBuffer));
+
+ FramebufferObjectGLES30* resolveBuffer = GetResolveFramebufferObjectGLES30(&fboManager,
+ copyColor ? srcColorFormat : 0,
+ copyDepth ? srcDepthFormat : 0,
+ width, height);
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveBuffer->GetFboID()));
+
+ // \note active FBO is already GL_READ_FRAMEBUFFER
+ GLES_CHK(glReadBuffer(srcColorAttachment));
+
+ // Resolve blit.
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, blitBuffers, GL_NEAREST));
+
+ // Blit from resolve buffer to destination.
+ GLES_CHK(glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveBuffer->GetFboID()));
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo->GetFboID()));
+ GLES_CHK(glReadBuffer(GL_COLOR_ATTACHMENT0));
+ GLES_CHK(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, blitBuffers, GL_NEAREST));
+ }
+
+ // Restore readbuffer state.
+ GLES_CHK(glReadBuffer(GL_BACK));
+
+ // Restore binding.
+ if (!STATE.m_activeFbo)
+ BindDefaultFramebufferGLES30();
+ else
+ BindFramebufferObjectGLES30(STATE.m_activeFbo);
+ }
+}
+
+#if ENABLE_PROFILER
+
+void GFX_GL_IMPL::BeginProfileEvent(const char* name)
+{
+ if(gGraphicsCaps.gles30.hasDebugMarkers)
+ gGles3ExtFunc.glPushGroupMarkerEXT(0, name);
+}
+
+void GFX_GL_IMPL::EndProfileEvent()
+{
+ if(gGraphicsCaps.gles30.hasDebugMarkers)
+ gGles3ExtFunc.glPopGroupMarkerEXT();
+}
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ if( gGraphicsCaps.hasTimerQuery )
+ return new TimerQueryGLES30;
+ return NULL;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES30.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES30.EndTimerQueries();
+}
+
+#endif // ENABLE_PROFILER
+
+typedef std::map<FixedFunctionStateGLES30, FixedFunctionProgramGLES30*, FullStateCompareGLES30> FFProgramCacheT;
+typedef std::map<FixedFunctionStateGLES30, GLShaderID, VertexStateCompareGLES30> FFVertexProgramCacheT;
+typedef std::map<FixedFunctionStateGLES30, GLShaderID, FragmentStateCompareGLES30> FFFragmentProgramCacheT;
+
+static FFProgramCacheT g_FixedFunctionProgramCache;
+static FFVertexProgramCacheT g_FFVertexProgramCache;
+static FFFragmentProgramCacheT g_FFFragmentProgramCache;
+
+static 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 FixedFunctionProgramGLES30* GetFixedFunctionProgram(const FixedFunctionStateGLES30& 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 = BuildVertexShaderSourceGLES30(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES30("Compiling generated vertex shader");
+ GlslGpuProgramGLES30::CompileGlslShader(vertexShader, cStr);
+ GLESAssert();
+
+ g_FFVertexProgramCache[state] = vertexShader;
+ }
+
+ if (fragmentShader == 0)
+ {
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+ std::string src = BuildFragmentShaderSourceGLES30(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES30("Compiling generated fragment shader");
+ GlslGpuProgramGLES30::CompileGlslShader(fragmentShader, cStr);
+ GLESAssert();
+
+ g_FFFragmentProgramCache[state] = fragmentShader;
+ }
+
+ DBG_SHADER_VERBOSE_GLES30("Creating and linking GLES program");
+ FixedFunctionProgramGLES30* ffProg = new FixedFunctionProgramGLES30(vertexShader, fragmentShader);
+ g_FixedFunctionProgramCache[state] = ffProg;
+
+ return ffProg;
+}
+
+static bool ComputeTextureTransformMatrix(TextureUnitStateGLES3 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 VBOContainsColorGLES30(bool flag)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES30DeviceState(device).vboContainsColor = flag;
+}
+
+void GLSLUseProgramGLES30(UInt32 programID)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ if (GetGLES30DeviceState(device).activeProgramID == programID)
+ return;
+
+ GLES_CHK(glUseProgram (programID));
+ GetGLES30DeviceState(device).activeProgramID = programID;
+}
+
+static void UploadUniformMatrix4(BuiltinShaderParamIndices::MatrixParamData& matParam, const GpuProgramParameters::ConstantBufferList* constantBuffers, const float* dataPtr, ConstantBuffersGLES30& cbs)
+{
+ Assert(matParam.cols == 4 && matParam.rows == 4);
+ if (matParam.cbID == -1)
+ {
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, dataPtr));
+ }
+ else if (constantBuffers != NULL)
+ {
+ for (int i = 0; i < constantBuffers->size(); ++i)
+ {
+ if ((*constantBuffers)[i].m_Name.index == matParam.cbID)
+ {
+ const GpuProgramParameters::ConstantBuffer& cb = (*constantBuffers)[i];
+ int idx = cbs.FindAndBindCB(cb.m_Name.index, cb.m_BindIndex, cb.m_Size);
+ cbs.SetCBConstant(idx, matParam.gpuIndex, dataPtr, sizeof(Matrix4x4f));
+ break;
+ }
+ }
+ }
+}
+
+static void UploadUniformMatrix3(BuiltinShaderParamIndices::MatrixParamData& matParam, const GpuProgramParameters::ConstantBufferList* constantBuffers, const float* dataPtr, ConstantBuffersGLES30& cbs)
+{
+ Assert(matParam.cols == 3 && matParam.rows == 3);
+ if (matParam.cbID == -1)
+ {
+ GLES_CHK(glUniformMatrix3fv (matParam.gpuIndex, 1, GL_FALSE, dataPtr));
+ }
+ else if (constantBuffers != NULL)
+ {
+ for (int i = 0; i < constantBuffers->size(); ++i)
+ {
+ if ((*constantBuffers)[i].m_Name.index == matParam.cbID)
+ {
+ const GpuProgramParameters::ConstantBuffer& cb = (*constantBuffers)[i];
+ int idx = cbs.FindAndBindCB(cb.m_Name.index, cb.m_BindIndex, cb.m_Size);
+ cbs.SetCBConstant(idx, matParam.gpuIndex, dataPtr, sizeof(Matrix3x3f));
+ break;
+ }
+ }
+ }
+}
+
+void GFX_GL_IMPL::BeforeDrawCall(bool immediateMode)
+{
+ DBG_LOG_GLES30("BeforeDrawCall(%s)", GetBoolString(immediateMode));
+
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ Assert(props);
+
+ // WorldView Matrix
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+
+ // 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));
+
+ UniformCacheGLES30* targetCache = 0;
+ const GpuProgramParameters::ConstantBufferList* constantBuffers = NULL;
+ if (STATE.activeProgram)
+ {
+ // Apply GPU program
+ GlslGpuProgramGLES30& prog = static_cast<GlslGpuProgramGLES30&>(*STATE.activeProgram);
+ int fogIndex = prog.ApplyGpuProgramES30 (*STATE.activeProgramParams, STATE.activeProgramParamsBuffer.data());
+ constantBuffers = &STATE.activeProgramParams->GetConstantBuffers();
+ m_BuiltinParamIndices[kShaderVertex] = &STATE.activeProgramParams->GetBuiltinParams();
+
+ targetCache = &prog.m_UniformCache[fogIndex];
+ }
+ else
+ {
+ // 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_GLES30(" using fixed-function");
+ FixedFunctionStateGLES30 ffstate;
+ STATE.ComputeFixedFunctionState(ffstate, m_FogParams);
+ const FixedFunctionProgramGLES30* program = GetFixedFunctionProgram(ffstate);
+ program->ApplyFFGpuProgram(m_BuiltinParamValues, STATE.m_CBs);
+ m_BuiltinParamIndices[kShaderVertex] = &program->GetBuiltinParams();
+ constantBuffers = &program->GetConstantBuffers();
+
+ targetCache = &program->m_UniformCache;
+ }
+
+ // Set Unity built-in parameters
+ {
+ Assert(m_BuiltinParamIndices[kShaderVertex]);
+ const BuiltinShaderParamIndices& params = *m_BuiltinParamIndices[kShaderVertex];
+
+ // MVP matrix
+ if (params.mat[kShaderInstanceMatMVP].gpuIndex >= 0)
+ {
+ Matrix4x4f wvp;
+ MultiplyMatrices4x4(&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &STATE.transformState.worldViewMatrix, &wvp);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMVP];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, wvp.GetPtr(), STATE.m_CBs);
+ }
+ // MV matrix
+ if (params.mat[kShaderInstanceMatMV].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, STATE.transformState.worldViewMatrix.GetPtr(), STATE.m_CBs);
+ }
+ // Transpose of MV matrix
+ if (params.mat[kShaderInstanceMatTransMV].gpuIndex >= 0)
+ {
+ Matrix4x4f tWV;
+ TransposeMatrix4x4(&STATE.transformState.worldViewMatrix, &tWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, tWV.GetPtr(), STATE.m_CBs);
+ }
+ // Inverse transpose of MV matrix
+ if (params.mat[kShaderInstanceMatInvTransMV].gpuIndex >= 0)
+ {
+ // 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);
+ UploadUniformMatrix4(matParam, constantBuffers, tInvWV.GetPtr(), STATE.m_CBs);
+ }
+ // M matrix
+ if (params.mat[kShaderInstanceMatM].gpuIndex >= 0)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.transformState.worldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ UploadUniformMatrix4(matParam, constantBuffers, mat.GetPtr(), STATE.m_CBs);
+ }
+ // Inverse M matrix
+ if (params.mat[kShaderInstanceMatInvM].gpuIndex >= 0)
+ {
+ 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);
+ UploadUniformMatrix4(matParam, constantBuffers, inverseMat.GetPtr(), STATE.m_CBs);
+ }
+
+ // Normal matrix
+ if (params.mat[kShaderInstanceMatNormalMatrix].gpuIndex >= 0)
+ {
+ 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);
+ UploadUniformMatrix3(matParam, constantBuffers, rotWV33.GetPtr(), STATE.m_CBs);
+ }
+
+ // 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, gpuIndexVS, val); break;
+ case 2: CachedUniform2(targetCache, gpuIndexVS, val); break;
+ case 3: CachedUniform3(targetCache, gpuIndexVS, val); break;
+ case 4: CachedUniform4(targetCache, 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);
+ UploadUniformMatrix4(matParam, constantBuffers, texM.GetPtr(), STATE.m_CBs);
+ }
+ }
+ }
+
+ // Set per-drawcall properties
+ GpuProgram* subprogram = STATE.activeProgram;
+ if (subprogram)
+ {
+ const MaterialPropertyBlock::Property* curProp = STATE.m_MaterialProperties.GetPropertiesBegin();
+ const MaterialPropertyBlock::Property* propEnd = STATE.m_MaterialProperties.GetPropertiesEnd();
+ const float* propBuffer = STATE.m_MaterialProperties.GetBufferBegin();
+ while (curProp != propEnd)
+ {
+ FastPropertyName name;
+ name.index = curProp->nameIndex;
+ const GpuProgramParameters::ValueParameter* param = STATE.activeProgramParams->FindParam(name);
+ if (param && curProp->rows == param->m_RowCount)
+ {
+ if (curProp->rows == 1)
+ {
+ const float* src = &propBuffer[curProp->offset];
+ switch (param->m_ColCount) {
+ case 1: CachedUniform1(targetCache, param->m_Index, src); break;
+ case 2: CachedUniform2(targetCache, param->m_Index, src); break;
+ case 3: CachedUniform3(targetCache, param->m_Index, src); break;
+ case 4: CachedUniform4(targetCache, param->m_Index, src); break;
+ }
+ GLESAssert();
+ }
+ else if (curProp->rows == 4)
+ {
+ DebugAssert(curProp->cols == 4);
+ const Matrix4x4f* mat = (const Matrix4x4f*)&propBuffer[curProp->offset];
+ GLES_CHK(glUniformMatrix4fv (param->m_Index, 1, GL_FALSE, mat->GetPtr()));
+ }
+ else
+ {
+ AssertString("Unknown property dimensions");
+ }
+ }
+ ++curProp;
+ }
+ }
+ STATE.m_MaterialProperties.Clear();
+
+ STATE.m_CBs.UpdateBuffers ();
+}
+
+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 TextureUnitStateGLES3& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES3::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 TextureUnitStateGLES3& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES3::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 );
+}
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+void GFX_GL_IMPL::ReloadResources()
+{
+ // Buffers in BufferManager must be cleared before recreating VBOs.
+ GetBufferManagerGLES30()->InvalidateAll();
+
+ RecreateAllVBOs();
+ GfxDevice::CommonReloadResources(kReleaseRenderTextures | kReloadShaders | kReloadTextures);
+ ClearFixedFunctionPrograms();
+
+ if (STATE.m_fboManager)
+ STATE.m_fboManager->InvalidateObjects();
+
+ InvalidateState();
+}
+
+// GPU skinning functionality
+GPUSkinningInfo * GFX_GL_IMPL::CreateGPUSkinningInfo()
+{
+ if (gGraphicsCaps.gles30.useTFSkinning)
+ return new TransformFeedbackSkinningInfo();
+ else
+ return 0;
+}
+
+void GFX_GL_IMPL::DeleteGPUSkinningInfo(GPUSkinningInfo *info)
+{
+ delete reinterpret_cast<TransformFeedbackSkinningInfo *>(info);
+}
+
+// All actual functionality is performed in TransformFeedbackSkinningInfo, just forward the calls
+void GFX_GL_IMPL::SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame )
+{
+ reinterpret_cast<TransformFeedbackSkinningInfo *>(info)->SkinMesh(lastThisFrame);
+}
+
+void GFX_GL_IMPL::UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty)
+{
+ reinterpret_cast<TransformFeedbackSkinningInfo *>(info)->UpdateSourceData(vertData, skinData, dirty);
+}
+
+void GFX_GL_IMPL::UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses)
+{
+ reinterpret_cast<TransformFeedbackSkinningInfo *>(info)->UpdateSourceBones(boneCount, poses);
+}
+
+// Acquire thread ownership on the calling thread. Worker releases ownership.
+void GFX_GL_IMPL::AcquireThreadOwnership()
+{
+ AcquireGLES30Context();
+}
+
+// Release thread ownership on the calling thread. Worker acquires ownership.
+void GFX_GL_IMPL::ReleaseThreadOwnership()
+{
+ ReleaseGLES30Context();
+}
+
+
+
+// ---------- verify state
+#if GFX_DEVICE_VERIFY_ENABLE
+void GFX_GL_IMPL::VerifyState()
+{
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+
+#endif // GFX_SUPPORTS_OPENGLES30