summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/opengles20
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/opengles20')
-rw-r--r--Runtime/GfxDevice/opengles20/AssertGLES20.cpp58
-rw-r--r--Runtime/GfxDevice/opengles20/AssertGLES20.h29
-rw-r--r--Runtime/GfxDevice/opengles20/CombinerGLES20.cpp21
-rw-r--r--Runtime/GfxDevice/opengles20/CombinerGLES20.h10
-rw-r--r--Runtime/GfxDevice/opengles20/ContextGLES20.cpp237
-rw-r--r--Runtime/GfxDevice/opengles20/ContextGLES20.h43
-rw-r--r--Runtime/GfxDevice/opengles20/DebugGLES20.cpp38
-rw-r--r--Runtime/GfxDevice/opengles20/DebugGLES20.h139
-rw-r--r--Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp73
-rw-r--r--Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h66
-rw-r--r--Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp2815
-rw-r--r--Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h190
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp1120
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h78
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp72
-rw-r--r--Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h51
-rw-r--r--Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp70
-rw-r--r--Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h3
-rw-r--r--Runtime/GfxDevice/opengles20/IncludesGLES20.h1
-rw-r--r--Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp958
-rw-r--r--Runtime/GfxDevice/opengles20/RenderTextureGLES20.h40
-rw-r--r--Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp749
-rw-r--r--Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h24
-rw-r--r--Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h17
-rw-r--r--Runtime/GfxDevice/opengles20/TexturesGLES20.cpp633
-rw-r--r--Runtime/GfxDevice/opengles20/TexturesGLES20.h21
-rw-r--r--Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp109
-rw-r--r--Runtime/GfxDevice/opengles20/TimerQueryGLES20.h47
-rw-r--r--Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp40
-rw-r--r--Runtime/GfxDevice/opengles20/UnityGLES20Ext.h342
-rw-r--r--Runtime/GfxDevice/opengles20/VBOGLES20.cpp1559
-rw-r--r--Runtime/GfxDevice/opengles20/VBOGLES20.h147
-rw-r--r--Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp438
33 files changed, 10238 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengles20/AssertGLES20.cpp b/Runtime/GfxDevice/opengles20/AssertGLES20.cpp
new file mode 100644
index 0000000..1b77623
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/AssertGLES20.cpp
@@ -0,0 +1,58 @@
+#include "UnityPrefix.h"
+#include "AssertGLES20.h"
+#include "IncludesGLES20.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+using namespace std;
+
+static const char* GetErrorString(GLenum glerr)
+{
+ // Error descriptions taken from OpenGLES 1.1 specification (page 13)
+ switch(glerr)
+ {
+ case GL_NO_ERROR:
+ return "GL_NO_ERROR: No error occured";
+ case GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM: enum argument out of range";
+ case GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE: Numeric argument out of range";
+ case GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION: Operation illegal in current state";
+ case GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY: Not enough memory left to execute command";
+ case 0x0506: // INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
+ return "GL_INVALID_FRAMEBUFFER_OPERATION_EXT";
+ default:
+#if UNITY_WEBGL
+ printf_console("AssertGles20::GetErrorString invoked for unknown error %d",glerr);
+#endif
+ return "Unknown error";
+ }
+}
+
+void CheckOpenGLES2Error (const char *prefix, const char* file, long line)
+{
+ const int kMaxErrors = 10;
+ int counter = 0;
+
+ GLenum glerr;
+ while( (glerr = glGetError ()) != GL_NO_ERROR )
+ {
+ string errorString(GetErrorString(glerr));
+
+ if (prefix)
+ errorString = string(prefix) + ": " + errorString;
+
+ DebugStringToFile (errorString.c_str(), 0, file, line, kAssert);
+
+ ++counter;
+ if( counter > kMaxErrors )
+ {
+ printf_console( "GLES: error count exceeds %i, stop reporting errors\n", kMaxErrors );
+ return;
+ }
+ }
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/AssertGLES20.h b/Runtime/GfxDevice/opengles20/AssertGLES20.h
new file mode 100644
index 0000000..4a9809c
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/AssertGLES20.h
@@ -0,0 +1,29 @@
+#ifndef GLES_ASSERTGLES20_H
+#define GLES_ASSERTGLES20_H
+
+#include "Runtime/Utilities/LogAssert.h"
+
+void CheckOpenGLES2Error (const char *prefix, const char* file, long line);
+
+#if !UNITY_RELEASE
+ #ifndef GLESAssert
+ /// Asserts for checking the OpenGL error state
+ #define GLESAssert() { CheckOpenGLES2Error (NULL, __FILE__, __LINE__); }
+ #endif
+ #define GLESAssertString(x) { CheckOpenGLES2Error (x, __FILE__, __LINE__); }
+ #define GLES_CHK(x) do { {x;} GLESAssert(); } while(0)
+#else
+
+ #ifndef GLESAssert
+ #define GLESAssert()
+ #endif
+ #define GLESAssertString(x)
+ #define GLES_CHK(x) x
+#endif
+
+
+//#define GLES_CHK(x) do {} while(0)
+//#define GLES_CHK(x)
+//#define GLES_CHK(x) do { printf_console("GLES: %s %d\n", __FILE__, __LINE__); } while(0)
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/CombinerGLES20.cpp b/Runtime/GfxDevice/opengles20/CombinerGLES20.cpp
new file mode 100644
index 0000000..eaff5fb
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/CombinerGLES20.cpp
@@ -0,0 +1,21 @@
+#include "UnityPrefix.h"
+#include "CombinerGLES20.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "VBOGLES20.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+TextureCombinersGLES2* TextureCombinersGLES2::Create (int count, const ShaderLab::TextureBinding* texEnvs)
+{
+ // check if we have enough vertex attributes to emulate this combiner
+ if (count + GL_TEXTURE_ARRAY0 >= gGraphicsCaps.gles20.maxAttributes)
+ return NULL;
+
+ // create struct that holds texture combiner info object
+ TextureCombinersGLES2* combiners = new TextureCombinersGLES2();
+ combiners->count = count;
+ combiners->texEnvs = texEnvs;
+ return combiners;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/CombinerGLES20.h b/Runtime/GfxDevice/opengles20/CombinerGLES20.h
new file mode 100644
index 0000000..6c144c5
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/CombinerGLES20.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace ShaderLab { struct TextureBinding; }
+
+struct TextureCombinersGLES2
+{
+ static TextureCombinersGLES2* Create (int count, const ShaderLab::TextureBinding* texEnvs);
+ int count;
+ const ShaderLab::TextureBinding* texEnvs;
+};
diff --git a/Runtime/GfxDevice/opengles20/ContextGLES20.cpp b/Runtime/GfxDevice/opengles20/ContextGLES20.cpp
new file mode 100644
index 0000000..1eaa8fd
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ContextGLES20.cpp
@@ -0,0 +1,237 @@
+#include "UnityPrefix.h"
+#include "ContextGLES20.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+#if UNITY_WIN
+
+
+struct EGLESData
+{
+ void* dsp;
+ void* cfg;
+ void* cxt;
+ void* surf;
+ EGLESData():dsp(NULL),cfg(NULL),cxt(NULL),surf(NULL){}
+};
+
+
+static EGLESData sOpenGLESData;
+
+bool InitializeGLES20 ()
+{
+ HWND hwnd = GetScreenManager().GetWindow();
+ if (hwnd)
+ {
+ CreateContextGLES20 (hwnd);
+ return true;
+ }
+ ErrorString ("gles20: Can't initialize because HWND not set up");
+ return false;
+}
+void ShutdownGLES20 ()
+{
+ DestroyContextGLES20();
+}
+bool IsContextGLES20Created()
+{
+ return sOpenGLESData.surf != NULL &&
+ sOpenGLESData.cxt != NULL &&
+ sOpenGLESData.cfg != NULL &&
+ sOpenGLESData.dsp != NULL;
+}
+
+bool CreateContextGLES20(HWND hWnd)
+{
+ //Just in case
+ DestroyContextGLES20();
+
+ EGLint numConfigs;
+ EGLint majorVersion;
+ EGLint minorVersion;
+
+#if UNITY_WIN
+
+ /// Build up the attribute list
+ const EGLint configAttribs[] =
+ {
+ EGL_LEVEL, 0,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NATIVE_RENDERABLE, EGL_FALSE,
+ EGL_DEPTH_SIZE, 16,
+ EGL_NONE
+ };
+
+ // Get Display
+ sOpenGLESData.dsp = eglGetDisplay( hWnd?GetDC(hWnd):EGL_DEFAULT_DISPLAY );
+ if ( sOpenGLESData.dsp == EGL_NO_DISPLAY )
+ {
+ printf_console("GLES20: eglGetDisplay failed\n" );
+ return false;
+ }
+ //Hack : eglInitialize invokes WM_ACTIVATE message, and gAppActive is already true, so Unity will try to call some functions which requires some initialization,
+ // and this is not done yet
+ extern bool gAlreadyClosing;
+ bool last = gAlreadyClosing;
+ gAlreadyClosing = true;
+ // Initialize EGL
+ if ( ! eglInitialize( sOpenGLESData.dsp, &majorVersion, &minorVersion) )
+ {
+ printf_console("GLES20: eglInitialize failed\n");
+ return false;
+ }
+
+
+ // Choose config
+ if ( !eglChooseConfig(sOpenGLESData.dsp, configAttribs, &sOpenGLESData.cfg, 1, &numConfigs) )
+ {
+ printf_console("GLES20: eglChooseConfig failed\n");
+ return false;
+ }
+
+
+ // Create a surface
+ sOpenGLESData.surf = eglCreateWindowSurface( sOpenGLESData.dsp, sOpenGLESData.cfg, NativeWindowType( hWnd ), NULL );
+ if ( sOpenGLESData.surf == EGL_NO_SURFACE )
+ {
+ printf_console("GLES20: eglCreateWindowSurface failed\n");
+ return false;
+ }
+
+ // Create a GL context
+ EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ sOpenGLESData.cxt = eglCreateContext( sOpenGLESData.dsp, sOpenGLESData.cfg, EGL_NO_CONTEXT, ctxAttribList );
+ if ( sOpenGLESData.cxt == EGL_NO_CONTEXT )
+ {
+ printf_console("GLES20: eglCreateContext failed\n");
+ return false;
+ }
+
+ // Make the context current
+ if ( ! eglMakeCurrent( sOpenGLESData.dsp, sOpenGLESData.surf, sOpenGLESData.surf, sOpenGLESData.cxt ) )
+ {
+ printf_console("GLES20: eglMakeCurrent failed\n");
+ return false;
+ }
+
+ gAlreadyClosing = last;
+#endif
+
+ GLESAssert();
+
+ return true;
+}
+
+
+void DestroyContextGLES20()
+{
+ if(sOpenGLESData.dsp)
+ {
+ eglMakeCurrent(sOpenGLESData.dsp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ;
+ eglDestroyContext( sOpenGLESData.dsp, sOpenGLESData.cxt );
+ eglDestroySurface( sOpenGLESData.dsp, sOpenGLESData.surf );
+ eglTerminate( sOpenGLESData.dsp);
+ }
+ sOpenGLESData.surf = NULL;
+ sOpenGLESData.cxt = NULL;
+ sOpenGLESData.cfg = NULL;
+ sOpenGLESData.dsp = NULL;
+}
+void PresentContextGLES20()
+{
+ eglSwapBuffers( sOpenGLESData.dsp, sOpenGLESData.surf );
+}
+
+#elif UNITY_LINUX
+
+static EGLDisplay gEGLDisplay = EGL_NO_DISPLAY;
+static EGLConfig gEGLConfig;
+static EGLSurface gEGLSurface = EGL_NO_SURFACE;
+static EGLContext gEGLContext = EGL_NO_CONTEXT;
+
+void SetEGLDisplay(EGLDisplay display)
+{
+ gEGLDisplay = display;
+}
+
+void SetEGLConfig(const EGLConfig &config)
+{
+ gEGLConfig = config;
+}
+
+bool InitializeGLES20 ()
+{
+ Window window = 0;
+ window = GetScreenManager().GetWindow();
+
+ if(window)
+ {
+ CreateContextGLES20(window);
+ return true;
+ }
+
+ return false;
+}
+
+void ShutdownGLES20 ()
+{
+ DestroyContextGLES20();
+}
+
+bool IsContextGLES20Created()
+{
+ return gEGLSurface != EGL_NO_SURFACE && gEGLContext != EGL_NO_CONTEXT;
+}
+
+bool CreateContextGLES20(Window window)
+{
+ DestroyContextGLES20();
+
+ gEGLSurface = eglCreateWindowSurface(gEGLDisplay, gEGLConfig, window, NULL);
+ if(gEGLSurface == EGL_NO_SURFACE)
+ {
+ printf_console("eglCreateWindowSurface failed\n");
+ return false;
+ }
+
+ // Create a context
+ printf_console("Creating context\n");
+ EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ gEGLContext = eglCreateContext(gEGLDisplay, gEGLConfig, EGL_NO_CONTEXT, ctxAttribList );
+ if ( gEGLContext == EGL_NO_CONTEXT )
+ {
+ printf_console( "eglCreateContext failed\n" );
+ return false;
+ }
+
+ if(!eglMakeCurrent(gEGLDisplay, gEGLSurface, gEGLSurface, gEGLContext))
+ {
+ printf_console("eglMakeCurrent failed\n");
+ }
+
+ return true;
+}
+
+void DestroyContextGLES20()
+{
+ if(IsContextGLES20Created())
+ {
+ eglMakeCurrent(gEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(gEGLDisplay, gEGLContext);
+ eglDestroySurface(gEGLDisplay, gEGLSurface);
+ }
+
+ gEGLSurface = EGL_NO_SURFACE;
+ gEGLContext = EGL_NO_CONTEXT;
+}
+
+void PresentContextGLES()
+{
+ eglSwapBuffers(gEGLDisplay, gEGLSurface);
+}
+#endif // UNITY_WIN
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/ContextGLES20.h b/Runtime/GfxDevice/opengles20/ContextGLES20.h
new file mode 100644
index 0000000..4fab18b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ContextGLES20.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+#if UNITY_LINUX
+#include <X11/Xlib.h>
+#include <GLES2/gl2.h>
+#endif
+
+#if UNITY_BB10
+#include <GLES2/gl2.h>
+#include <screen/screen.h>
+#endif
+
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN || UNITY_ANDROID
+bool InitializeGLES20 ();
+void ShutdownGLES20 ();
+bool IsContextGLES20Created();
+#if UNITY_WIN
+bool CreateContextGLES20(HWND hWnd);
+#elif UNITY_LINUX
+bool CreateContextGLES20(Window window);
+#elif UNITY_BB10
+bool CreateContextGLES20(screen_window_t window);
+void ResizeContextGLES20(screen_window_t window, int width, int height);
+void AdjustVsync(int val);
+#elif UNITY_TIZEN
+bool CreateContextGLES20();
+void ResizeContextGLES20(int width, int height);
+#endif
+#if !UNITY_ANDROID
+void DestroyContextGLES20();
+#endif
+#endif
+void PresentContextGLES();
+void PresentContextGLES20();
+
+#if UNITY_ANDROID
+void ReleaseGLES20Context();
+
+void AcquireGLES20Context();
+#endif
diff --git a/Runtime/GfxDevice/opengles20/DebugGLES20.cpp b/Runtime/GfxDevice/opengles20/DebugGLES20.cpp
new file mode 100644
index 0000000..d087e07
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/DebugGLES20.cpp
@@ -0,0 +1,38 @@
+#include "UnityPrefix.h"
+#include "IncludesGLES20.h"
+#include "DebugGLES20.h"
+#include "AssertGLES20.h"
+
+
+void DumpVertexArrayStateGLES20()
+{
+#if GFX_SUPPORTS_OPENGLES20
+ GLint maxVertexAttribs = 0;
+ GLES_CHK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs));
+
+ GLint vbo = 0;
+ GLint ibo = 0;
+ GLES_CHK(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo));
+ GLES_CHK(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ibo));
+
+ printf_console("---> VertexArray State: vbo:%d ibo:%d\n", vbo, ibo);
+
+ for (int q = 0; q < maxVertexAttribs; ++q)
+ {
+ int enabled, size, stride, normalized, type, vbo;
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type));
+ GLES_CHK(glGetVertexAttribiv(q, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vbo));
+
+ GLvoid* ptr;
+ GLES_CHK(glGetVertexAttribPointerv(q, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr));
+
+ printf_console(" attr[%d] --- %s type:%d size:%d stride:%d norm:%d vbo:%d, %p\n",
+ q, enabled? "On ": "Off",
+ type, size, stride, normalized, vbo, ptr);
+ }
+#endif
+}
diff --git a/Runtime/GfxDevice/opengles20/DebugGLES20.h b/Runtime/GfxDevice/opengles20/DebugGLES20.h
new file mode 100644
index 0000000..b8c0004
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/DebugGLES20.h
@@ -0,0 +1,139 @@
+#ifndef DEBUGGLES20_H
+#define DEBUGGLES20_H
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Math/Matrix4x4.h"
+
+
+void DumpVertexArrayStateGLES20();
+
+#if !UNITY_RELEASE
+ #define DBG_LOG_GLES20_ACTIVE 0
+ #define DBG_TEXTURE_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_SHADER_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_GLSL_BINDINGS_GLES20_ACTIVE 0
+#else
+ #define DBG_LOG_GLES20_ACTIVE 0
+ #define DBG_TEXTURE_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_SHADER_VERBOSE_GLES20_ACTIVE 0
+ #define DBG_GLSL_BINDINGS_GLES20_ACTIVE 0
+#endif
+
+#if DBG_LOG_GLES20_ACTIVE
+ #define DBG_LOG_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+
+ inline std::string GetMatrixString(const Matrix4x4f& mtx)
+ {
+ return Format("%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f",
+ mtx[0], mtx[1], mtx[2], mtx[3],
+ mtx[4], mtx[5], mtx[6], mtx[7],
+ mtx[8], mtx[9], mtx[10],mtx[11],
+ mtx[12],mtx[13],mtx[14],mtx[15]);
+ }
+
+ inline const char* GetBoolString(bool type)
+ {
+ return type?"True":"False";
+ }
+
+ inline const char * GetBlendModeString(BlendMode type)
+ {
+ switch(type)
+ {
+ case kBlendZero:return "kBlendZero";
+ case kBlendOne:return "kBlendOne";
+ case kBlendDstColor:return "kBlendDstColor";
+ case kBlendSrcColor:return "kBlendSrcColor";
+ case kBlendOneMinusDstColor:return "kBlendOneMinusDstColor";
+ case kBlendSrcAlpha:return "kBlendSrcAlpha";
+ case kBlendOneMinusSrcColor:return "kBlendOneMinusSrcColor";
+ case kBlendDstAlpha:return "kBlendDstAlpha";
+ case kBlendOneMinusDstAlpha:return "kBlendOneMinusDstAlpha";
+ case kBlendSrcAlphaSaturate:return "kBlendSrcAlphaSaturate";
+ case kBlendOneMinusSrcAlpha:return "kBlendOneMinusSrcAlpha";
+ default:return "GetBlendModeString<Undefined>";
+ }
+ }
+ inline const char * GetCullModeString(CullMode type)
+ {
+ switch(type)
+ {
+ case kCullUnknown:return "kCullUnknown";
+ case kCullOff:return "kCullOff:return";
+ case kCullFront:return "kCullFront";
+ case kCullBack:return "kCullBack";;
+ default:return "GetCullMode<undefined>";
+ }
+ }
+
+ inline const char * GetCompareFunctionString(CompareFunction type)
+ {
+ switch(type)
+ {
+ case kFuncUnknown:return "kFuncUnknown";
+ case kFuncDisabled:return "kFuncDisabled";
+ case kFuncNever:return "kFuncNever";
+ case kFuncLess:return "kFuncLess";
+ case kFuncEqual:return "kFuncEqual";
+ case kFuncLEqual:return "kFuncLEqual";
+ case kFuncGreater:return "kFuncGreater";
+ case kFuncNotEqual:return "kFuncNotEqual";
+ case kFuncGEqual:return "kFuncGEqual";
+ case kFuncAlways:return "kFuncAlways";
+ default:return "GetCompareFunctionString<Undefined>";
+ }
+ }
+
+ inline const char * GetShaderTypeString(ShaderType type)
+ {
+ switch(type)
+ {
+ case kShaderNone:return "kShaderNone";
+ case kShaderVertex:return "kShaderVertex";
+ case kShaderFragment:return "kShaderFragment";
+ default:return "GetShaderTypeString<undefined>";
+ }
+ }
+
+ inline const char * GetShaderImplTypeString(ShaderImplType type)
+ {
+ switch(type)
+ {
+ case kShaderImplUndefined: return "kShaderImplUndefined";
+ case kShaderImplVertex: return "kShaderImplVertex";
+ case kShaderImplFragment: return "kShaderImplFragment";
+ case kShaderImplBoth: return "kShaderImplBoth";
+ default:return "GetShaderImplTypeString<Undefined>";
+ }
+ }
+
+#else
+ #define DBG_LOG_GLES20(...)
+#endif
+
+#if DBG_TEXTURE_VERBOSE_GLES20_ACTIVE
+#define DBG_TEXTURE_VERBOSE_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#else
+#define DBG_TEXTURE_VERBOSE_GLES20(...)
+#endif
+
+#if DBG_SHADER_VERBOSE_GLES20_ACTIVE
+ #define DBG_SHADER_VERBOSE_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+ #define DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER(prefix, text) { printf_console("%s\n", prefix);DebugTextLineByLine(text);printf_console("\n---\n");}
+#else
+ #define DBG_SHADER_VERBOSE_GLES20(...)
+ #define DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER(prefix, text)
+#endif
+
+#if DBG_GLSL_BINDINGS_GLES20_ACTIVE
+ #define DBG_GLSL_BINDINGS_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#else
+ #define DBG_GLSL_BINDINGS_GLES20(...)
+#endif
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp
new file mode 100644
index 0000000..4df51e1
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.cpp
@@ -0,0 +1,73 @@
+#include "UnityPrefix.h"
+#include "FixedFunctionStateGLES20.h"
+#include <sstream>
+
+
+FixedFunctionStateGLES20::FixedFunctionStateGLES20 ()
+: texUnitCount(0),
+ lightType(0),
+ texUnitMatrix(0),
+ lightingEnabled(false),
+ specularEnabled(true),
+ onlyDirectionalLights(false),
+ lightCount(0),
+ useUniformInsteadOfVertexColor(false),
+ useVertexColorAsAmbientAndDiffuse(false),
+ useVertexColorAsEmission(false),
+ fogMode(kFogDisabled),
+ addSpecularAfterTexturing(false),
+ alphaTest(kFuncDisabled),
+ setupPointSize(false)
+{
+ for (int q = 0; q < kMaxSupportedTextureUnitsGLES; ++q)
+ {
+ texUnitCube[q] = false;
+ texUnitGen[q] = kTexGenDisabled;
+ texUnitColorCombiner[q] = ~0UL;
+ texUnitAlphaCombiner[q] = ~0UL;
+ }
+}
+
+static std::string CombinerToString (unsigned int combinerDesc)
+{
+ std::ostringstream s;
+ s << combinerDesc;
+ return s.str().c_str();
+}
+
+
+std::string FixedFunctionStateGLES20::ToString () const
+{
+ std::ostringstream s;
+
+ s << "FixedFunctionStateGLES20::ToString():\n";
+ s << " lightingEnabled = " << lightingEnabled << "\n";
+ s << " specularEnabled = " << specularEnabled << "\n";
+ s << " lights = " << lightCount << "\n";
+ for (int i = 0; i < lightCount; ++i)
+ s << " light" << i << " : " << GetLightType(i) << "\n";
+
+ s << " useUniformInsteadOfVertexColor = " << useUniformInsteadOfVertexColor << "\n";
+ s << " useVertexColorAsAmbientAndDiffuse = " << useVertexColorAsAmbientAndDiffuse << "\n";
+ s << " useVertexColorAsEmission = " << useVertexColorAsEmission << "\n";
+
+ s << " fogMode = " << fogMode << "\n";
+
+ for (int i = 0; i < texUnitCount; ++i)
+ {
+ s << " texture " << i << "\n";
+
+ s << " CUBE = " << ((texUnitCube[i])? "true": "false") << "\n";
+ s << " rgb combiner = " << CombinerToString(texUnitColorCombiner[i]) << "\n";
+ s << " alpba combiner = " << CombinerToString(texUnitAlphaCombiner[i]) << "\n";
+ s << " texGen = " << texUnitGen[i] << "\n";
+ s << " need matrix: " << (NeedTexUnitMatrix(i)?"true":"false") << "\n";
+ s << " need perspective divide: " << (IsTexUnitProjected(i)?"true":"false") << "\n";
+ }
+
+ s << " addSpecularafterTexturing = " << addSpecularAfterTexturing << "\n";
+ s << " alphaTest = " << alphaTest << "\n";
+ s << " setupPointSize = " << setupPointSize << "\n";
+
+ return s.str().c_str();
+}
diff --git a/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h
new file mode 100644
index 0000000..9fe723e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/FixedFunctionStateGLES20.h
@@ -0,0 +1,66 @@
+#ifndef FIXEDFUNCTIONSTATE_GLES20_H
+#define FIXEDFUNCTIONSTATE_GLES20_H
+
+#include "IncludesGLES20.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include <string>
+
+// we can use one var to determine both shift and mask, but let help out the compiler ;-)
+#define FFPSTATE_SET_MASK(target, idx, val, shift_mul, mask) \
+do{ \
+ target &= ~(mask << ((idx)*shift_mul)); \
+ target |= (val << ((idx)*shift_mul)); \
+}while(0) \
+
+
+
+class FixedFunctionStateGLES20
+{
+public:
+ FixedFunctionStateGLES20();
+
+ UInt32 texUnitColorCombiner[kMaxSupportedTextureUnitsGLES];
+ UInt32 texUnitAlphaCombiner[kMaxSupportedTextureUnitsGLES];
+ bool texUnitCube[kMaxSupportedTextureUnitsGLES];
+ int texUnitGen[kMaxSupportedTextureUnitsGLES];
+ int texUnitCount;
+
+ // we will use 4bits per light - this way we can handle up to 8 lights (though kMaxEmulatedVertexLights = 4)
+ UInt32 lightType;
+ // we will use 2 bits per tex unit - one for perspective divide, the other one for if we need matrix mul at all
+ // this way we can store 16 texunit info (though kMaxSupportedTextureUnitsGLES = 8)
+ UInt32 texUnitMatrix;
+
+ int lightCount : 8;
+ FogMode fogMode : 8;
+ CompareFunction alphaTest : 8;
+
+ bool lightingEnabled;
+ bool specularEnabled;
+ bool onlyDirectionalLights;
+ bool setupPointSize;
+
+ bool useUniformInsteadOfVertexColor;
+ bool useVertexColorAsAmbientAndDiffuse;
+ bool useVertexColorAsEmission;
+ bool addSpecularAfterTexturing;
+
+ unsigned GetLightType(int i) const { return (lightType >> (i*4)) & 0xF; }
+ void SetLightType(int i, unsigned type) { FFPSTATE_SET_MASK(lightType, i, type, 4, 0xF); }
+
+ bool NeedTexUnitMatrix(int i) const { return ((texUnitMatrix >> (i*2)) & 0x1) != 0; }
+ bool IsTexUnitProjected(int i) const { return ((texUnitMatrix >> (i*2)) & 0x2) != 0; }
+
+ void SetTexUnitMatrixParam(int i, bool hasMatrix, bool isProjected)
+ {
+ int mask = (hasMatrix ? 1 : 0) | (isProjected ? 2 : 0);
+ FFPSTATE_SET_MASK(texUnitMatrix, i, mask, 2, 0x3);
+ }
+
+ std::string ToString () const;
+};
+
+
+#undef FFPSTATE_SET_MASK
+
+#endif /* FIXEDFUNCTIONSTATE_GLES20_H */
diff --git a/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
new file mode 100644
index 0000000..cf59ce6
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.cpp
@@ -0,0 +1,2815 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES20
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Shaders/MaterialProperties.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Quaternion.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Threads/AtomicOps.h"
+#include "Runtime/GfxDevice/TransformState.h"
+#include "Runtime/GfxDevice/GpuProgramParamsApply.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "ContextGLES20.h"
+#include "VBOGLES20.h"
+#include "TexturesGLES20.h"
+#include "CombinerGLES20.h"
+#include "GpuProgramsGLES20.h"
+#include "FixedFunctionStateGLES20.h"
+#include "ShaderGeneratorGLES20.h"
+#include "RenderTextureGLES20.h"
+#include "DebugGLES20.h"
+#include "TimerQueryGLES20.h"
+#include "TextureIdMapGLES20.h"
+#include "UnityGLES20Ext.h"
+
+#if UNITY_IPHONE
+ #include "PlatformDependent/iPhonePlayer/iPhoneSettings.h"
+#elif UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/ContextGLES.h"
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+
+#if NV_STATE_FILTERING
+
+void filteredInitGLES20();
+void filteredBindBufferGLES20(GLenum target, GLuint buffer, bool isImmediate);
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
+void filteredDeleteBuffersGLES20(GLsizei n, const GLuint *buffers);
+
+#ifdef glBindBuffer
+#undef glBindBuffer
+#endif
+#define glBindBuffer(a, b) filteredBindBufferGLES20(a, b, true)
+#ifndef glDeleteBuffers
+#define glDeleteBuffers filteredDeleteBuffersGLES20
+#endif
+#ifndef glVertexAttribPointer
+#define glVertexAttribPointer filteredVertexAttribPointerGLES20
+#endif
+
+#endif
+
+// let's play safe here:
+// ios/glesemu works just fine
+// and shadows demands more careful eps choice - do it later
+#define WORKAROUND_POLYGON_OFFSET UNITY_ANDROID
+
+// forward declarations
+
+bool IsActiveRenderTargetWithColorGLES2(); // RenderTextureGL.cpp
+namespace ShaderLab {
+ TexEnv* GetTexEnvForBinding( const TextureBinding& binding, const PropertySheet* props ); // pass.cpp
+}
+
+// local forward declarations
+struct DeviceStateGLES20;
+static void ApplyBackfaceMode( const DeviceStateGLES20& state );
+static GLuint GetSharedFBO (DeviceStateGLES20& state);
+
+extern GLint gDefaultFBO;
+
+// NOTE: GLES2.0 supports only 4 lights for now
+enum { kMaxSupportedVertexLightsByGLES20 = 4 };
+
+// Constant tables
+static const unsigned int kBlendModeES2[] = {
+ GL_ZERO, GL_ONE, GL_DST_COLOR, GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR,
+ GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
+};
+
+static const unsigned int kBlendFuncES2[] = {
+ GL_FUNC_ADD, GL_FUNC_SUBTRACT,
+ GL_FUNC_REVERSE_SUBTRACT, GL_MIN_EXT, GL_MAX_EXT,
+};
+
+static const unsigned int kCmpFuncES2[] = {
+ GL_NEVER, GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS
+};
+
+static const unsigned int kStencilOpES2[] = {
+ GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR,
+ GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP
+};
+
+static const GLenum kWrapModeES2[kTexWrapCount] = {
+ GL_REPEAT,
+ GL_CLAMP_TO_EDGE,
+};
+
+static const GLint kMinFilterES2[kTexFilterCount] = { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
+
+// --------------------------------------------------------------------------
+
+struct DeviceDepthStateGLES20 : public DeviceDepthState
+{
+ UInt32 depthFunc;
+};
+
+struct DeviceStencilStateGLES20 : public DeviceStencilState
+{
+ GLenum stencilFuncFront;
+ GLenum stencilFailOpFront;
+ GLenum depthFailOpFront;
+ GLenum depthPassOpFront;
+ GLenum stencilFuncBack;
+ GLenum stencilFailOpBack;
+ GLenum depthFailOpBack;
+ GLenum depthPassOpBack;
+};
+
+
+typedef std::map< GfxBlendState, DeviceBlendState, memcmp_less<GfxBlendState> > CachedBlendStates;
+typedef std::map< GfxDepthState, DeviceDepthStateGLES20, memcmp_less<GfxDepthState> > CachedDepthStates;
+typedef std::map< GfxStencilState, DeviceStencilStateGLES20, memcmp_less<GfxStencilState> > CachedStencilStates;
+typedef std::map< GfxRasterState, DeviceRasterState, memcmp_less<GfxRasterState> > CachedRasterStates;
+
+// --------------------------------------------------------------------------
+struct TextureUnitStateGLES2
+{
+ GLuint texID;
+ TextureDimension texDim;
+ unsigned int combColor, combAlpha;
+ Vector4f color;
+ TexGenMode texGen;
+ Matrix4x4f textureMatrix;
+ float bias;
+
+ // right let waste space here instead of device-level int
+ bool identityMatrix;
+ bool isProjected;
+ bool posForTexGen;
+ bool nrmForTexGen;
+
+ void Invalidate();
+
+ static bool PositionRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenObject || mode == kTexGenEyeLinear);
+ }
+
+ static bool NormalRequiredForTexGen(TexGenMode mode)
+ {
+ return (mode == kTexGenSphereMap || mode == kTexGenCubeReflect || mode == kTexGenCubeNormal);
+ }
+
+ void SetTexGen( TexGenMode mode )
+ {
+ posForTexGen = PositionRequiredForTexGen(mode);
+ nrmForTexGen = NormalRequiredForTexGen(mode);
+
+ texGen = mode;
+ }
+};
+
+void TextureUnitStateGLES2::Invalidate()
+{
+ texID = -1;
+ texDim = kTexDimNone;
+ combColor = combAlpha = 0xFFFFFFFF;
+ color.Set( -1, -1, -1, -1 );
+ texGen = kTexGenUnknown;
+ posForTexGen = 0;
+ nrmForTexGen = 0;
+ textureMatrix.SetIdentity();
+ identityMatrix = true;
+ isProjected = false;
+ bias = 1.0e6f;
+}
+
+
+// --------------------------------------------------------------------------
+// TODO: optimize this. Right now we just send off whole 8 float3 UVs with each
+// immediate mode vertex. We could at least detect the number of them used from
+// ImmediateTexCoord calls.
+struct ImmediateVertexGLES20 {
+ Vector3f vertex;
+ Vector3f normal;
+ UInt32 color;
+ Vector3f texCoords[8];
+};
+
+struct ImmediateModeGLES20 {
+ std::vector<ImmediateVertexGLES20> m_Vertices;
+ ImmediateVertexGLES20 m_Current;
+ GfxPrimitiveType m_Mode;
+ UInt16* m_QuadsIB;
+
+ int m_IndexBufferQuadsID;
+
+ ImmediateModeGLES20();
+ ~ImmediateModeGLES20();
+ void Invalidate();
+};
+
+// --------------------------------------------------------------------------
+struct DeviceStateGLES20
+{
+ GLuint m_SharedFBO;
+ GLuint m_HelperFBO;
+ int m_TextureIDGenerator;
+
+ int depthFunc;
+ int depthWrite; // 0/1 or -1
+
+ int blending;
+ int srcBlend, destBlend, srcBlendAlpha, destBlendAlpha; // Blend modes
+ int blendOp, blendOpAlpha;
+ CompareFunction alphaTest;
+ float alphaValue;
+
+ CullMode culling;
+ bool appBackfaceMode, userBackfaceMode;
+ NormalizationMode
+ normalization;
+ int scissor;
+
+ bool lighting;
+ bool separateSpecular;
+ SimpleVec4 matDiffuse, matAmbient, matSpecular, matEmissive;
+ SimpleVec4 ambient;
+ float matShininess;
+ ColorMaterialMode
+ colorMaterial;
+
+ float offsetFactor, offsetUnits;
+
+ int colorWriteMask; // ColorWriteMask combinations
+ SimpleVec4 color;
+
+ TextureUnitStateGLES2
+ textures[kMaxSupportedTextureUnitsGLES];
+ int textureCount;
+ int activeTextureUnit;
+
+ // pure optimization: texGen is very special case and is used sparingly
+ UInt32 positionTexGen;
+ UInt32 normalTexGen;
+
+ int vertexLightCount;
+ LightType vertexLightTypes[kMaxSupportedVertexLights];
+
+ TransformState transformState;
+
+ DynamicVBO* m_DynamicVBO;
+ bool vboContainsColor;
+
+ int viewport[4];
+ int scissorRect[4];
+
+ // should be set before BeforeDrawCall call
+ GfxPrimitiveType drawCallTopology;
+
+
+ GpuProgram* activeProgram;
+ const GpuProgramParameters* activeProgramParams;
+ dynamic_array<UInt8> activeProgramParamsBuffer;
+ UInt32 activeProgramID;
+
+
+ CachedBlendStates m_CachedBlendStates;
+ CachedDepthStates m_CachedDepthStates;
+ CachedStencilStates m_CachedStencilStates;
+ CachedRasterStates m_CachedRasterStates;
+
+ const DeviceBlendState* m_CurrBlendState;
+ const DeviceDepthStateGLES20* m_CurrDepthState;
+ const DeviceStencilStateGLES20* m_CurrStencilState;
+ int m_StencilRef;
+ const DeviceRasterState* m_CurrRasterState;
+
+ ImmediateModeGLES20 m_Imm;
+
+public:
+ DeviceStateGLES20();
+ void Invalidate();
+ void ComputeFixedFunctionState(FixedFunctionStateGLES20& state, const GfxFogParams& fog) const;
+
+ inline void ApplyTexGen( UInt32 unit );
+ inline void DropTexGen( UInt32 unit );
+};
+
+DeviceStateGLES20::DeviceStateGLES20()
+: m_DynamicVBO(0)
+{
+ m_TextureIDGenerator = 0;
+}
+
+void DeviceStateGLES20::Invalidate()
+{
+ DBG_LOG_GLES20("Invalidate");
+ int i;
+
+ depthFunc = -1; //unknown
+ depthWrite = -1;
+
+ blending = -1; // unknown
+ srcBlend = destBlend = srcBlendAlpha = destBlendAlpha = -1; // won't match any GL mode
+ blendOp = blendOpAlpha = -1;
+ alphaTest = kFuncUnknown;
+ alphaValue = -1.0f;
+
+ culling = kCullUnknown;
+ normalization = kNormalizationUnknown;
+ scissor = -1;
+
+ lighting = false;
+ separateSpecular = false;
+
+ matDiffuse.set( -1, -1, -1, -1 );
+ matAmbient.set( -1, -1, -1, -1 );
+ matSpecular.set( -1, -1, -1, -1 );
+ matEmissive.set( -1, -1, -1, -1 );
+ ambient.set( -1, -1, -1, -1 );
+ matShininess = -1.0f;
+ colorMaterial = kColorMatUnknown;
+
+ offsetFactor = offsetUnits = -1000.0f;
+
+ colorWriteMask = -1; // TBD ?
+ m_StencilRef = -1;
+
+ color.set( -1, -1, -1, -1 );
+
+ activeTextureUnit = -1;
+ for( i = 0; i < kMaxSupportedTextureUnitsGLES; ++i )
+ textures[i].Invalidate();
+ textureCount = 0;
+
+ positionTexGen = 0;
+ normalTexGen = 0;
+
+ vertexLightCount = 0;
+ for ( i = 0; i < kMaxSupportedVertexLights; ++i)
+ vertexLightTypes[i] = kLightDirectional;
+
+ // make sure backface mode is in sync
+ appBackfaceMode = false;
+ userBackfaceMode = false;
+ ApplyBackfaceMode( *this );
+
+ vboContainsColor = true;
+
+ viewport[0] = 0;
+ viewport[1] = 0;
+ viewport[2] = 0;
+ viewport[3] = 0;
+
+ scissorRect[0] = 0;
+ scissorRect[1] = 0;
+ scissorRect[2] = 0;
+ scissorRect[3] = 0;
+
+ activeProgram = 0;
+ activeProgramParams = 0;
+ activeProgramParamsBuffer.resize_uninitialized(0);
+ activeProgramID = -1;
+
+ m_Imm.Invalidate();
+
+ InvalidateVertexInputCacheGLES20();
+
+ GLESAssert();
+}
+
+void DeviceStateGLES20::ComputeFixedFunctionState(FixedFunctionStateGLES20& state, const GfxFogParams& fog) const
+{
+ if (lighting)
+ {
+ int numLights = vertexLightCount;
+ bool onlyDir = true;
+ for(int i = 0 ; i < numLights ; ++i)
+ {
+ onlyDir = onlyDir && (vertexLightTypes[i] == kLightDirectional);
+ state.SetLightType(i,vertexLightTypes[i]);
+ }
+
+ state.lightingEnabled = true;
+ state.lightCount = numLights;
+ state.onlyDirectionalLights = onlyDir;
+ state.specularEnabled = separateSpecular;
+
+ switch (colorMaterial)
+ {
+ case kColorMatDisabled:
+ break;
+
+ case kColorMatAmbientAndDiffuse:
+ state.useVertexColorAsAmbientAndDiffuse = true;
+ break;
+
+ case kColorMatEmission:
+ state.useVertexColorAsEmission = true;
+ break;
+
+ default:
+ ErrorString("Unsupported color material mode");
+ break;
+ }
+ }
+ else
+ {
+ state.lightingEnabled = false;
+ state.lightCount = 0;
+ }
+
+ state.useUniformInsteadOfVertexColor = !vboContainsColor;
+ state.texUnitCount = textureCount;
+
+ for (int i = 0; i < textureCount; i++)
+ {
+ Assert(textures[i].texDim != kTexDimUnknown);
+ Assert(textures[i].texDim != kTexDimNone);
+ Assert(textures[i].texDim != kTexDimAny);
+ Assert(textures[i].texDim != kTexDim3D); // OpenGLES2.0 does NOT supports 3D textures
+ state.texUnitCube[i] = (textures[i].texDim == kTexDimCUBE);
+ state.texUnitColorCombiner[i] = textures[i].combColor,
+ state.texUnitAlphaCombiner[i] = textures[i].combAlpha;
+ state.texUnitGen[i] = textures[i].texGen;
+
+ bool needMatrix = !textures[i].identityMatrix || textures[i].texGen > kTexGenDisabled;
+ state.SetTexUnitMatrixParam(i, needMatrix, textures[i].isProjected);
+ }
+
+ state.fogMode = fog.mode;
+ switch (fog.mode)
+ {
+ case kFogUnknown:
+ case kFogDisabled:
+ state.fogMode = kFogDisabled;
+ default:
+ break;
+ }
+
+ if(gGraphicsCaps.gles20.hasAlphaTestQCOM)
+ {
+ // we dont want to generate special shader if we have alpha-test done gl style
+ state.alphaTest = kFuncDisabled;
+ }
+ else
+ {
+ state.alphaTest = alphaTest;
+ switch (alphaTest)
+ {
+ case kFuncNever: /* \todo Disable drawing. */
+ case kFuncUnknown:
+ case kFuncDisabled:
+ case kFuncAlways:
+ state.alphaTest = kFuncDisabled;
+ default:
+ break;
+ }
+ }
+
+ state.setupPointSize = drawCallTopology == kPrimitivePoints;
+}
+
+inline void DeviceStateGLES20::ApplyTexGen( UInt32 unit )
+{
+ const TextureUnitStateGLES2& state = textures[unit];
+
+ positionTexGen = state.posForTexGen ? positionTexGen | (1<<unit)
+ : positionTexGen & ~(1<<unit);
+
+ normalTexGen = state.nrmForTexGen ? normalTexGen | (1<<unit)
+ : normalTexGen & ~(1<<unit);
+}
+
+inline void DeviceStateGLES20::DropTexGen( UInt32 unit )
+{
+ positionTexGen &= ~(1<<unit);
+ normalTexGen &= ~(1<<unit);
+}
+
+
+#include "GfxDeviceGLES20.h"
+#include "Runtime/GfxDevice/GLESCommon.h"
+
+void GfxDeviceGLES20_MarkWorldViewProjDirty()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+}
+
+void GfxDeviceGLES20_DisableDepthTest()
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).depthFunc = GL_NEVER;
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+}
+
+void GfxDeviceGLES20_SetDrawCallTopology(GfxPrimitiveType topology)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).drawCallTopology = topology;
+}
+
+
+#define GL_RT_COMMON_GLES2 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GLES2
+
+void GraphicsCaps::InitGLES20()
+{
+ GLES_InitCommonCaps(this);
+ gGlesExtFunc.InitExtFunc();
+
+ shaderCaps = kShaderLevel3;
+
+ maxLights = kMaxSupportedVertexLightsByGLES20; // vertex light count
+ hasAnisoFilter = QueryExtension("GL_EXT_texture_filter_anisotropic"); // has anisotropic filtering?
+ if (hasAnisoFilter)
+ {
+ #if UNITY_PEPPER
+ // Google's implementation incorrectly reports a maxAnisoLevel of 1.
+ // Until they fix this, just set it here.
+ maxAnisoLevel = 16;
+ #else
+ GLES_CHK(glGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisoLevel ));
+ #endif
+ }
+ else
+ maxAnisoLevel = 1;
+
+
+ maxTexImageUnits = 8;
+ GLES_CHK(glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTexImageUnits));
+ maxTexImageUnits = std::max<int>(std::min<int>( maxTexImageUnits, kMaxSupportedTextureUnitsGLES ), 1);
+
+ maxTexUnits = maxTexImageUnits;
+ maxTexCoords = maxTexImageUnits;
+
+ GLES_CHK(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapSize));
+ GLES_CHK(glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderTextureSize));
+
+ hasMipLevelBias = QueryExtension("GL_EXT_texture_lod_bias"); // can apply bias for mips?
+ hasMipMaxLevel = QueryExtension("GL_APPLE_texture_max_level");
+
+ gles20.hasAppleMSAA = QueryExtension("GL_APPLE_framebuffer_multisample");
+ gles20.hasImgMSAA = QueryExtension("GL_IMG_multisampled_render_to_texture") && gGlesExtFunc.glRenderbufferStorageMultisampleIMG && gGlesExtFunc.glFramebufferTexture2DMultisampleIMG;
+
+ hasMultiSampleAutoResolve = gles20.hasImgMSAA;
+ hasMultiSample = gles20.hasAppleMSAA || gles20.hasImgMSAA;
+
+
+ hasBlendSquare = true;
+ hasSeparateAlphaBlend = true;
+ hasBlendSub = true;
+ hasBlendMinMax = QueryExtension("GL_EXT_blend_minmax");
+
+ hasS3TCCompression = false;
+
+ hasAutoMipMapGeneration = true;
+
+ has3DTexture = false;
+
+ npot = kNPOTRestricted;
+ if( QueryExtension("GL_OES_texture_npot") || QueryExtension("GL_ARB_texture_non_power_of_two") )
+ npot = kNPOTFull;
+
+ npotRT = npot;
+
+ hasRenderToTexture = true; // We have render-to-texture functionality.
+ hasShadowCollectorPass = false;
+
+ hasHighPrecisionTextureCombiners = false;
+
+ hasRenderToCubemap = true;
+
+ supportsTextureFormat[kTexFormatBGRA32] = QueryExtension("GL_APPLE_texture_format_BGRA8888") || QueryExtension("GL_EXT_texture_format_BGRA8888");
+ supportsTextureFormat[kTexFormatAlphaLum16] = false;
+ supportsTextureFormat[kTexFormatARGB32] = false; // OpenGL ES has no support for INT_8_8_8_8 format, so don't bother with Unity reversed formats at all
+ supportsTextureFormat[kTexFormatBGR24] = false; // OpenGL ES has no support for INT_8_8_8_8 format, so don't bother with Unity reversed formats at all
+ supportsTextureFormat[kTexFormatDXT1] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_EXT_texture_compression_dxt1");
+ supportsTextureFormat[kTexFormatDXT3] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt3");
+ supportsTextureFormat[kTexFormatDXT5] = QueryExtension("GL_EXT_texture_compression_s3tc") || QueryExtension("GL_CHROMIUM_texture_compression_dxt5");
+ supportsTextureFormat[kTexFormatPVRTC_RGB2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA2] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGB4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatPVRTC_RGBA4] = QueryExtension("GL_IMG_texture_compression_pvrtc");
+ supportsTextureFormat[kTexFormatETC_RGB4] = QueryExtension("GL_OES_compressed_ETC1_RGB8_texture");
+ supportsTextureFormat[kTexFormatATC_RGB4] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+ supportsTextureFormat[kTexFormatATC_RGBA8] = QueryExtension("GL_AMD_compressed_ATC_texture") || QueryExtension("GL_ATI_texture_compression_atitc");
+
+ supportsRenderTextureFormat[kRTFormatARGB32] = true;
+
+ {
+ const bool supportsHalfRB = QueryExtension("GL_EXT_color_buffer_half_float");
+ // if we dont have OES_texture_half_float we cant have half texture (only RB)
+ const bool supportsHalfRT = supportsHalfRB && QueryExtension("GL_OES_texture_half_float");
+ const bool supportsRG = QueryExtension("GL_EXT_texture_rg");
+
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = supportsHalfRT;
+ supportsRenderTextureFormat[kRTFormatR8] = supportsRG;
+ supportsRenderTextureFormat[kRTFormatRHalf] = supportsHalfRT && supportsRG;
+ supportsRenderTextureFormat[kRTFormatRGHalf] = supportsHalfRT && supportsRG;
+ }
+
+ {
+ FBColorFormatCheckerGLES2 checker;
+
+ supportsRenderTextureFormat[kRTFormatRGB565] = checker.CheckFormatSupported(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ supportsRenderTextureFormat[kRTFormatARGB4444] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4);
+ supportsRenderTextureFormat[kRTFormatARGB1555] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1);
+
+ // for float formats there is no ext to check rif they are renderable, so do manually
+ if(QueryExtension("OES_texture_float"))
+ {
+ supportsRenderTextureFormat[kRTFormatARGBHalf] = checker.CheckFormatSupported(GL_RGBA, GL_RGBA, GL_FLOAT);
+ if(QueryExtension("GL_EXT_texture_rg"))
+ {
+ // TODO: move all this unity gles ext header
+ supportsRenderTextureFormat[kRTFormatRFloat] = checker.CheckFormatSupported(0x1903, 0x1903, GL_FLOAT);
+ supportsRenderTextureFormat[kRTFormatRGFloat] = checker.CheckFormatSupported(0x8227, 0x8227, GL_FLOAT);
+ }
+ }
+ }
+
+ const bool isPvrGpu = (rendererString.find("PowerVR") != string::npos);
+ const bool isMaliGpu = (rendererString.find("Mali") != string::npos);
+ const bool isAdrenoGpu = (rendererString.find("Adreno") != string::npos);
+ const bool isTegraGpu = (rendererString.find("Tegra") != string::npos);
+
+ // Only support stencil when we have packed depth stencil, for simplicity
+ hasStencil = hasTwoSidedStencil = hasRenderTargetStencil = QueryExtension("GL_OES_packed_depth_stencil");
+
+#if UNITY_BB10
+ if(isAdrenoGpu)
+ hasRenderTargetStencil = false;
+#endif
+
+ // Adreno 2xx seems to not like a texture attached to color & depth & stencil at once;
+ // Adreno 3xx seems to be fine. Most 3xx devices have GL_OES_depth_texture_cube_map extension
+ // present, and 2xx do not. So detect based on that.
+ const bool isAdreno2xx = isAdrenoGpu && !QueryExtension("GL_OES_depth_texture_cube_map");
+ if (isAdreno2xx)
+ hasRenderTargetStencil = false;
+
+ hasNativeDepthTexture = QueryExtension("GL_OES_depth_texture") || QueryExtension ("GL_GOOGLE_depth_texture");
+#if UNITY_ANDROID
+ if(android::systeminfo::ApiLevel() < android::apiHoneycomb || (android::systeminfo::ApiLevel() < android::apiIceCreamSandwich && isPvrGpu))
+ hasNativeDepthTexture = false;
+#endif
+
+ hasStencilInDepthTexture = hasRenderTargetStencil && hasNativeDepthTexture;
+ gles20.has24DepthForFBO = QueryExtension("GL_OES_depth24");
+
+ hasNativeShadowMap = QueryExtension("GL_EXT_shadow_samplers");
+
+ supportsRenderTextureFormat[kRTFormatA2R10G10B10] = false;
+ supportsRenderTextureFormat[kRTFormatARGB64] = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = hasNativeDepthTexture;
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasNativeShadowMap;
+
+
+ has16BitFloatVertex = QueryExtension("GL_OES_vertex_half_float");
+ needsToSwizzleVertexColors = false;
+
+ // ---- driver bug/workaround flags
+
+#if UNITY_LINUX
+ if(isTegraGpu)
+ {
+ supportsRenderTextureFormat[kRTFormatDepth] = hasNativeDepthTexture = true;
+ hasRenderTargetStencil = true;
+ hasStencilInDepthTexture = true;
+ hasTwoSidedStencil = true;
+ }
+#endif
+
+ hasSRGBReadWrite = QueryExtension("GL_EXT_sRGB");
+ // TODO: we should check srgb+compressed on gpu-case basis, but for now it is tegra only anyway ;-)
+ hasSRGBReadWrite = hasSRGBReadWrite && QueryExtension("GL_NV_sRGB_formats");
+
+ disableSoftShadows = true;
+
+ hasShadowCollectorPass = false;
+
+ // ---- gles20 specifics
+
+ gles20.hasGLSL = true;
+ gles20.maxAttributes = 16;
+ gles20.hasNLZ = QueryExtension("GL_NV_depth_nonlinear");
+ gles20.hasAlphaTestQCOM = QueryExtension("GL_QCOM_alpha_test") && gGlesExtFunc.glAlphaFuncQCOM;
+
+ gles20.hasMapbuffer = QueryExtension("GL_OES_mapbuffer") && gGlesExtFunc.glMapBufferOES && gGlesExtFunc.glUnmapBufferOES;
+ gles20.hasMapbufferRange= QueryExtension("GL_EXT_map_buffer_range") && gGlesExtFunc.glMapBufferRangeEXT && gGlesExtFunc.glUnmapBufferOES;
+
+#if UNITY_PEPPER
+ // it was cut out in ifdef before, so lets play safe and assume we dont have map
+ gles20.hasMapbuffer = gles20.hasMapbufferRange = false;
+#endif
+
+ gles20.hasBinaryShaders = (UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN) && QueryExtension("GL_OES_get_program_binary") && GlslGpuProgramGLES20::InitBinaryShadersSupport();
+
+ gles20.hasNVMRT = QueryExtension("GL_NV_draw_buffers") && QueryExtension("GL_NV_fbo_color_attachments") && gGlesExtFunc.glDrawBuffersNV;
+ if(gles20.hasNVMRT)
+ {
+ GLES_CHK(glGetIntegerv(GL_MAX_DRAW_BUFFERS_NV, &maxMRTs));
+ maxMRTs = clamp<int> (maxMRTs, 1, kMaxSupportedRenderTargets);
+ }
+
+#if UNITY_IPHONE
+ if( iphone::GetDeviceGeneration() == iphone::kiPhoneGenerationiPad2Gen && iphone::isIOSVersionOrNewerButBefore("4.3", "5.0") )
+ gles20.buggyColorMaskBlendMSAA = true;
+#endif
+
+#if UNITY_ANDROID || UNITY_BB10
+ // Adreno (Qualcomm chipsets) have a driver bug (Android2.2)
+ // which fails to patch vfetch instructions under certain circumstances
+ gles20.buggyVFetchPatching = isAdrenoGpu;
+#endif
+
+ // Mali-400 MP? They promised to fix drivers (they know about the issue)
+ gles20.buggyDisableVAttrKeepsActive = isMaliGpu;
+
+#if UNITY_ANDROID
+ // samsung galaxy on mali + ics update ruined shader compiler
+ // most osam part: on some shaders tex sample in vprog result in crash close to kernel (no callstack in logcat)
+ gles20.buggyVprogTextures = isMaliGpu && (android::systeminfo::ApiLevel() >= android::apiIceCreamSandwich);
+#endif
+
+ // on android pvr if we enable dynamic geom drawing from memory sometimes we hit crash in the driver
+ // it seems that some internal caching goes crazy - solved by actually drawing watermark
+ // This is also needed on BB10 devices (both pvr and adreno) to fix video display issues
+ gles20.needToRenderWatermarkDueToDrawFromMemoryBuggy = (UNITY_ANDROID && isPvrGpu) || UNITY_BB10;
+
+ // PowerVR GPUs have slow discard
+ gles20.slowAlphaTest = isPvrGpu;
+
+ // on adreno devices drivers don't like if+discard combo
+ // so set slowAlphaTest here too, to remove trivial clip
+ gles20.slowAlphaTest |= isAdrenoGpu;
+
+ // orphaning path is terribly slower universally
+ // apart from some corner cases on ios where it is not faster anyway
+ gles20.hasVBOOrphaning = false;
+
+ gles20.hasDebugMarkers = QueryExtension("GL_EXT_debug_marker") && gGlesExtFunc.glPushGroupMarkerEXT && gGlesExtFunc.glPopGroupMarkerEXT;
+ gles20.hasDiscardFramebuffer = QueryExtension("GL_EXT_discard_framebuffer") && gGlesExtFunc.glDiscardFramebufferEXT;
+ gles20.hasHalfLinearFilter = QueryExtension("GL_OES_texture_half_float_linear");
+
+
+ gles20.slowDynamicVBO = isPvrGpu || isAdrenoGpu || isMaliGpu;
+ gles20.forceStaticBatchFromMem = isMaliGpu;
+#if UNITY_IPHONE
+ gles20.forceStaticBatchFromMem = true;
+#endif
+ gles20.needFlushAfterTextureUpload = isAdrenoGpu;
+
+ gles20.needFlushAfterTextureUpload = isAdrenoGpu;
+
+ hasTiledGPU = isPvrGpu || isAdrenoGpu || isMaliGpu;
+
+ // on Tegra (for now all drivers) there is a bug that sometimes results in samplers not being reported
+ // the only known workaround is to force highp default precision
+ gles20.forceHighpFSPrec = isTegraGpu;
+
+ // on Adreno (Nexus4 at least) there is a bug that sometimes results in program link crash
+ // the only known workaround is to force highp default precision
+ gles20.forceHighpFSPrec = isAdrenoGpu;
+
+
+ GLES_CHK(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gles20.maxAttributes));
+ GLES_CHK(glGetIntegerv(GL_MAX_VARYING_VECTORS, &gles20.maxVaryings));
+
+ if(gles20.hasAppleMSAA)
+ GLES_CHK(glGetIntegerv(GL_MAX_SAMPLES_APPLE, &gles20.maxSamples));
+ if(gles20.hasImgMSAA)
+ GLES_CHK(glGetIntegerv(GL_MAX_SAMPLES_IMG, &gles20.maxSamples));
+
+#if ENABLE_PROFILER
+ g_TimerQueriesGLES.Init();
+#endif
+#if NV_STATE_FILTERING
+ filteredInitGLES20();
+#endif
+}
+
+//chai: extern 函数
+GfxDevice* CreateGLES20GfxDevice()
+{
+#if UNITY_WIN || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN || UNITY_ANDROID
+ InitializeGLES20();
+#endif
+ gGraphicsCaps.InitGLES20();
+#if UNITY_EDITOR
+ return NULL;
+#else
+ return UNITY_NEW_AS_ROOT(GFX_GL_IMPL(), kMemGfxDevice, "GLES20GfxDevice","");
+#endif
+}
+
+GFX_GL_IMPL::GFX_GL_IMPL()
+{
+ ;;printf_console("Creating OpenGLES2.0 graphics device\n");
+ #if !GFX_DEVICE_VIRTUAL
+ impl = new GfxDeviceImpl();
+ #endif
+
+ // ??? ReJ: (Q for Tomas) WHY InvalidateState was commented out here? InvalidateState is necessary to enable correct back-face culling
+ OnCreate();
+ InvalidateState();
+#if UNITY_PEPPER || UNITY_WEBGL
+ m_Renderer = kGfxRendererOpenGLES20Desktop;
+#else
+ m_Renderer = kGfxRendererOpenGLES20Mobile;
+#endif
+ m_IsThreadable = true;
+
+ m_UsesOpenGLTextureCoords = true;
+ m_UsesHalfTexelOffset = false;
+
+ STATE.m_CurrBlendState = NULL;
+ STATE.m_CurrDepthState = NULL;
+ STATE.m_CurrStencilState = NULL;
+ STATE.m_CurrRasterState = NULL;
+ STATE.m_SharedFBO = STATE.m_HelperFBO = 0;
+
+ InitBackBufferGLES2(&m_BackBufferColor.object, &m_BackBufferDepth.object);
+}
+
+GFX_GL_IMPL::~GFX_GL_IMPL()
+{
+ delete STATE.m_DynamicVBO;
+
+ #if !GFX_DEVICE_VIRTUAL
+ delete impl;
+ #endif
+#if UNITY_WIN || UNITY_ANDROID
+ ShutdownGLES20();
+#endif
+}
+
+
+static void ActivateTextureUnitGLES2 (DeviceStateGLES20& state, int unit)
+{
+ if (state.activeTextureUnit == unit)
+ return;
+ GLES_CHK(glActiveTexture(GL_TEXTURE0 + unit));
+ state.activeTextureUnit = unit;
+}
+
+
+void GFX_GL_IMPL::InvalidateState()
+{
+ DBG_LOG_GLES20("InvalidateState");
+ m_FogParams.Invalidate();
+ STATE.transformState.Invalidate(m_BuiltinParamValues);
+ STATE.Invalidate();
+
+#if NV_STATE_FILTERING
+ StateFiltering_InvalidateVBOCacheGLES20();
+#endif
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(1,1,1,1));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(1,1,1,1));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, Vector4f(0,0,0,0));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(0,0,0,0));
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(1,1,1,1));
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), Vector4f(0,0,0,0));
+}
+
+
+
+DeviceBlendState* GFX_GL_IMPL::CreateBlendState(const GfxBlendState& state)
+{
+ std::pair<CachedBlendStates::iterator, bool> result = STATE.m_CachedBlendStates.insert(std::make_pair(state, DeviceBlendState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceBlendState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ DebugAssertIf(kFuncUnknown==state.alphaTest);
+
+ return &result.first->second;
+}
+
+
+DeviceDepthState* GFX_GL_IMPL::CreateDepthState(const GfxDepthState& state)
+{
+ std::pair<CachedDepthStates::iterator, bool> result = STATE.m_CachedDepthStates.insert(std::make_pair(state, DeviceDepthStateGLES20()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceDepthStateGLES20& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+ glstate.depthFunc = kCmpFuncES2[state.depthFunc];
+ return &result.first->second;
+}
+
+DeviceStencilState* GFX_GL_IMPL::CreateStencilState(const GfxStencilState& state)
+{
+ std::pair<CachedStencilStates::iterator, bool> result = STATE.m_CachedStencilStates.insert(std::make_pair(state, DeviceStencilStateGLES20()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceStencilStateGLES20& st = result.first->second;
+ memcpy (&st.sourceState, &state, sizeof(st.sourceState));
+ st.stencilFuncFront = kCmpFuncES2[state.stencilFuncFront];
+ st.stencilFailOpFront = kStencilOpES2[state.stencilFailOpFront];
+ st.depthFailOpFront = kStencilOpES2[state.stencilZFailOpFront];
+ st.depthPassOpFront = kStencilOpES2[state.stencilPassOpFront];
+ st.stencilFuncBack = kCmpFuncES2[state.stencilFuncBack];
+ st.stencilFailOpBack = kStencilOpES2[state.stencilFailOpBack];
+ st.depthFailOpBack = kStencilOpES2[state.stencilZFailOpBack];
+ st.depthPassOpBack = kStencilOpES2[state.stencilPassOpBack];
+ return &result.first->second;
+}
+
+DeviceRasterState* GFX_GL_IMPL::CreateRasterState(const GfxRasterState& state)
+{
+ std::pair<CachedRasterStates::iterator, bool> result = STATE.m_CachedRasterStates.insert(std::make_pair(state, DeviceRasterState()));
+ if (!result.second)
+ return &result.first->second;
+
+ DeviceRasterState& glstate = result.first->second;
+ memcpy(&glstate.sourceState, &state, sizeof(glstate.sourceState));
+
+ return &result.first->second;
+}
+
+void GFX_GL_IMPL::SetBlendState(const DeviceBlendState* state, float alphaRef)
+{
+ DeviceBlendState* devstate = (DeviceBlendState*)state;
+
+ if (STATE.m_CurrBlendState == devstate && alphaRef == STATE.alphaValue)
+ return;
+
+ STATE.m_CurrBlendState = devstate;
+ if (!STATE.m_CurrBlendState)
+ return;
+
+ const GfxBlendState& desc = state->sourceState;
+ const GLenum glsrc = kBlendModeES2[desc.srcBlend];
+ const GLenum gldst = kBlendModeES2[desc.dstBlend];
+ const GLenum glsrca = kBlendModeES2[desc.srcBlendAlpha];
+ const GLenum gldsta = kBlendModeES2[desc.dstBlendAlpha];
+ const GLenum glfunc = kBlendFuncES2[desc.blendOp];
+ const GLenum glfunca = kBlendFuncES2[desc.blendOpAlpha];
+ const bool blendDisabled = (glsrc == GL_ONE && gldst == GL_ZERO && glsrca == GL_ONE && gldsta == GL_ZERO);
+
+ int mask = devstate->sourceState.renderTargetWriteMask;
+
+ if (!IsActiveRenderTargetWithColorGLES2())
+ mask = 0;
+
+#if UNITY_IPHONE
+ if( (mask && mask != 15) && gGraphicsCaps.gles20.buggyColorMaskBlendMSAA && !blendDisabled && IsActiveMSAARenderTargetGLES2() )
+ mask = 15;
+#endif
+
+ if( mask != STATE.colorWriteMask )
+ {
+ GLES_CHK(glColorMask( (mask & kColorWriteR) != 0, (mask & kColorWriteG) != 0, (mask & kColorWriteB) != 0, (mask & kColorWriteA) != 0 ));
+ STATE.colorWriteMask = mask;
+ }
+
+ if( blendDisabled )
+ {
+ if( STATE.blending != 0 )
+ {
+ GLES_CHK(glDisable (GL_BLEND));
+ STATE.blending = 0;
+ }
+ }
+ else
+ {
+ if( glsrc != STATE.srcBlend || gldst != STATE.destBlend || glsrca != STATE.srcBlendAlpha || gldsta != STATE.destBlendAlpha )
+ {
+ GLES_CHK(glBlendFuncSeparate(glsrc, gldst, glsrca, gldsta));
+ STATE.srcBlend = glsrc;
+ STATE.destBlend = gldst;
+ STATE.srcBlendAlpha = glsrca;
+ STATE.destBlendAlpha = gldsta;
+ }
+ if (glfunc != STATE.blendOp || glfunca != STATE.blendOpAlpha)
+ {
+ bool supports = true;
+ if( (glfunc == GL_MIN_EXT || glfunc == GL_MAX_EXT) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+ if( (glfunca == GL_MIN_EXT || glfunca == GL_MAX_EXT) && !gGraphicsCaps.hasBlendMinMax )
+ supports = false;
+
+ if(supports)
+ {
+ GLES_CHK(glBlendEquationSeparate(glfunc, glfunca));
+ STATE.blendOp = glfunc;
+ STATE.blendOpAlpha = glfunca;
+ }
+ }
+ if( STATE.blending != 1 )
+ {
+ GLES_CHK(glEnable( GL_BLEND ));
+ STATE.blending = 1;
+ }
+ }
+ // fragment shader is expected to implement per fragment culling
+ CompareFunction alphaTest = devstate->sourceState.alphaTest;
+
+ if (gGraphicsCaps.gles20.slowAlphaTest)
+ {
+ // Alpha testing is slow on some GPUs
+ // skip trivial cases such as (alpha > 0)
+ if (alphaTest == kFuncGreater && alphaRef <= 0.01)
+ alphaTest = kFuncDisabled;
+ }
+
+ if( gGraphicsCaps.gles20.hasAlphaTestQCOM && (alphaTest != STATE.alphaTest || alphaRef != STATE.alphaValue) )
+ {
+ if( alphaTest != kFuncDisabled )
+ {
+ GLES_CHK(gGlesExtFunc.glAlphaFuncQCOM(kCmpFuncES2[alphaTest], alphaRef));
+ GLES_CHK(glEnable(GL_ALPHA_TEST_QCOM));
+ }
+ else
+ {
+ GLES_CHK(glDisable(GL_ALPHA_TEST_QCOM));
+ }
+ }
+
+ STATE.alphaTest = alphaTest;
+ STATE.alphaValue = alphaRef;
+}
+
+
+void GFX_GL_IMPL::SetRasterState(const DeviceRasterState* state)
+{
+ DeviceRasterState* devstate = (DeviceRasterState*)state;
+ if(!devstate)
+ {
+ STATE.m_CurrRasterState = NULL;
+ return;
+ }
+
+ STATE.m_CurrRasterState = devstate;
+
+ CullMode cull = devstate->sourceState.cullMode;
+ if( cull != STATE.culling )
+ {
+ switch (cull) {
+ case kCullOff:
+ GLES_CHK(glDisable (GL_CULL_FACE));
+ break;
+ case kCullFront:
+ GLES_CHK(glCullFace (GL_FRONT));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ case kCullBack:
+ GLES_CHK(glCullFace (GL_BACK));
+ GLES_CHK(glEnable (GL_CULL_FACE));
+ break;
+ default:
+ break;
+ }
+ STATE.culling = cull;
+ }
+
+ float zFactor = devstate->sourceState.slopeScaledDepthBias;
+ float zUnits = devstate->sourceState.depthBias;
+ if( zFactor != STATE.offsetFactor || zUnits != STATE.offsetUnits )
+ {
+
+#if WORKAROUND_POLYGON_OFFSET
+ // on some androids polygon offset work the other way (positive push to viewer)
+ // so we tweak projection matrix to do offset
+ // also use available depth precision better (on Adreno for example fix bugs with z-fighting)
+ // Game Programming Gems Vol1
+ // Eric Lengyel's "Tweaking a Vertex's Projected Depth Value"
+ // we use simplified formula: just smallest possible eps directly (multiplied by zUnits)
+ // we calculate eps for 16bit depth (good enough for larger depth)
+ // in projection matrix [2,2] = (f+n)/(n-f)
+ // so eps would be BitsMult * -1/proj[2,2]
+
+ static const float _BitsMult = -1.0f / (float)0xFFFF; // FFFF = 2^16-1, minus sign incorporated here
+
+ float* matrixElem = &m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).Get(2,2);
+
+ const float eps = _BitsMult / *matrixElem;
+ *matrixElem *= (1.0f + zUnits*eps);
+#else
+ GLES_CHK(glPolygonOffset( zFactor, zUnits ));
+ if( zFactor || zUnits )
+ GLES_CHK(glEnable (GL_POLYGON_OFFSET_FILL));
+ else
+ GLES_CHK(glDisable (GL_POLYGON_OFFSET_FILL));
+#endif
+ STATE.offsetFactor = zFactor;
+ STATE.offsetUnits = zUnits;
+ }
+}
+
+
+void GFX_GL_IMPL::SetDepthState(const DeviceDepthState* state)
+{
+ DeviceDepthStateGLES20* devstate = (DeviceDepthStateGLES20*)state;
+ if (STATE.m_CurrDepthState == devstate)
+ return;
+
+ STATE.m_CurrDepthState = devstate;
+
+ if (!STATE.m_CurrDepthState)
+ return;
+
+ const int depthFunc = devstate->depthFunc;
+ if( depthFunc != STATE.depthFunc )
+ {
+ if( depthFunc != GL_NEVER ) {
+ GLES_CHK(glDepthFunc (depthFunc));
+ GLES_CHK(glEnable (GL_DEPTH_TEST));
+ } else {
+ GLES_CHK(glDisable (GL_DEPTH_TEST));
+ }
+
+ STATE.depthFunc = depthFunc;
+ }
+
+ const int writeMode = devstate->sourceState.depthWrite ? GL_TRUE : GL_FALSE;
+ if( writeMode != STATE.depthWrite )
+ {
+ GLES_CHK(glDepthMask (writeMode));
+ STATE.depthWrite = writeMode;
+ }
+}
+
+void GFX_GL_IMPL::SetStencilState (const DeviceStencilState* state, int stencilRef)
+{
+ if (STATE.m_CurrStencilState == state && STATE.m_StencilRef == stencilRef)
+ return;
+ const DeviceStencilStateGLES20* st = static_cast<const DeviceStencilStateGLES20*>(state);
+ STATE.m_CurrStencilState = st;
+ if (!st)
+ return;
+
+ if (st->sourceState.stencilEnable)
+ GLES_CHK(glEnable (GL_STENCIL_TEST));
+ else
+ GLES_CHK(glDisable (GL_STENCIL_TEST));
+ if (gGraphicsCaps.hasTwoSidedStencil)
+ {
+ GLES_CHK(glStencilFuncSeparate (GL_FRONT, st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_FRONT, st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ GLES_CHK(glStencilFuncSeparate (GL_BACK, st->stencilFuncBack, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOpSeparate (GL_BACK, st->stencilFailOpBack, st->depthFailOpBack, st->depthPassOpBack));
+ }
+ else
+ {
+ GLES_CHK(glStencilFunc (st->stencilFuncFront, stencilRef, st->sourceState.readMask));
+ GLES_CHK(glStencilOp (st->stencilFailOpFront, st->depthFailOpFront, st->depthPassOpFront));
+ }
+ GLES_CHK(glStencilMask (st->sourceState.writeMask));
+
+ STATE.m_StencilRef = stencilRef;
+}
+
+void GFX_GL_IMPL::SetSRGBWrite (bool enable)
+{
+}
+
+bool GFX_GL_IMPL::GetSRGBWrite ()
+{
+ return false;
+}
+
+void GFX_GL_IMPL::Clear (UInt32 clearFlags, const float color[4], float depth, int stencil)
+{
+ DBG_LOG_GLES20("Clear(%d, (%.2f, %.2f, %.2f, %.2f), %.2f, %d", clearFlags, color[0], color[1], color[2], color[3], depth, stencil);
+
+ EnsureDefaultFBInitedGLES2();
+
+ if (!IsActiveRenderTargetWithColorGLES2())
+ clearFlags &= ~kGfxClearColor;
+
+ float clearColorAlpha = color[3];
+
+ // In OpenGL, clears are affected by color write mask and depth writing parameters.
+ // So make sure to set them!
+ GLbitfield flags = 0;
+ if (clearFlags & kGfxClearColor)
+ {
+ if (STATE.colorWriteMask != 15)
+ {
+ GLES_CHK(glColorMask( true, true, true, true ));
+ STATE.colorWriteMask = 15;
+ STATE.m_CurrBlendState = NULL;
+ }
+ flags |= GL_COLOR_BUFFER_BIT;
+ GLES_CHK(glClearColor( color[0], color[1], color[2], clearColorAlpha ));
+ }
+ if (clearFlags & kGfxClearDepth)
+ {
+ GLES_CHK(glDepthMask (GL_TRUE));
+ STATE.depthWrite = GL_TRUE;
+ STATE.m_CurrDepthState = NULL;
+ flags |= GL_DEPTH_BUFFER_BIT;
+ GLES_CHK(glClearDepthf( depth ));
+ }
+ if (clearFlags & kGfxClearStencil)
+ {
+ //@TODO: need to set stencil writes on?
+ flags |= GL_STENCIL_BUFFER_BIT;
+ GLES_CHK(glClearStencil (stencil));
+ }
+ GLES_CHK(glClear(flags));
+}
+
+static void ApplyBackfaceMode( const DeviceStateGLES20& state )
+{
+ DBG_LOG_GLES20("ApplyBackfaceMode");
+ if (state.appBackfaceMode != state.userBackfaceMode)
+ GLES_CHK(glFrontFace( GL_CCW ));
+ else
+ GLES_CHK(glFrontFace( GL_CW ));
+}
+
+
+void GFX_GL_IMPL::SetUserBackfaceMode( bool enable )
+{
+ DBG_LOG_GLES20("SetUserBackfaceMode(%s)", GetBoolString(enable));
+ if( STATE.userBackfaceMode == enable )
+ return;
+
+ STATE.userBackfaceMode = enable;
+ ApplyBackfaceMode( STATE );
+}
+
+void GFX_GL_IMPL::SetInvertProjectionMatrix( bool enable )
+{
+ Assert (!enable); // projection should never be flipped upside down on OpenGL
+}
+
+bool GFX_GL_IMPL::GetInvertProjectionMatrix() const
+{
+ return false;
+}
+
+void GFX_GL_IMPL::SetProjectionMatrix (const Matrix4x4f& matrix)
+{
+ DBG_LOG_GLES20("SetProjectionMatrix(...)");
+
+ CopyMatrix(matrix.GetPtr(), STATE.transformState.projectionMatrixOriginal.GetPtr());
+ CopyMatrix (matrix.GetPtr(), m_BuiltinParamValues.GetWritableMatrixParam(kShaderMatProj).GetPtr());
+ STATE.transformState.dirtyFlags |= TransformState::kProjDirty;
+}
+
+
+void GFX_GL_IMPL::SetWorldMatrix( const float matrix[16] )
+{
+ CopyMatrix( matrix, STATE.transformState.worldMatrix.GetPtr() );
+ STATE.transformState.dirtyFlags |= TransformState::kWorldDirty;
+}
+
+void GFX_GL_IMPL::SetViewMatrix( const float matrix[16] )
+{
+ STATE.transformState.SetViewMatrix (matrix, m_BuiltinParamValues);
+}
+
+
+void GFX_GL_IMPL::GetMatrix( float outMatrix[16] ) const
+{
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ CopyMatrix (STATE.transformState.worldViewMatrix.GetPtr(), outMatrix);
+}
+
+const float* GFX_GL_IMPL::GetWorldMatrix() const
+{
+ return STATE.transformState.worldMatrix.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetViewMatrix() const
+{
+ return m_BuiltinParamValues.GetMatrixParam(kShaderMatView).GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetProjectionMatrix() const
+{
+ return STATE.transformState.projectionMatrixOriginal.GetPtr();
+}
+
+const float* GFX_GL_IMPL::GetDeviceProjectionMatrix() const
+{
+ return GetProjectionMatrix();
+}
+
+void GFX_GL_IMPL::SetNormalizationBackface( NormalizationMode mode, bool backface )
+{
+ DBG_LOG_GLES20("SetNormalizationBackface(%d %s)", mode, backface?"back":"front");
+ STATE.normalization = mode;
+ if( STATE.appBackfaceMode != backface )
+ {
+ STATE.appBackfaceMode = backface;
+ ApplyBackfaceMode( STATE );
+ }
+}
+
+void GFX_GL_IMPL::SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial )
+{
+ DBG_LOG_GLES20("SetFFLighting(%s, %s, %d)", on?"True":"False", separateSpecular?"True":"False", colorMaterial);
+ STATE.lighting = on;
+ STATE.separateSpecular = separateSpecular;
+ STATE.colorMaterial = colorMaterial;
+}
+
+void GFX_GL_IMPL::SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess )
+{
+ DBG_LOG_GLES20("SetMaterial()");
+ STATE.matAmbient.set (ambient[0], ambient[1], ambient[2], 1.0F);
+ STATE.matDiffuse.set (diffuse);
+ STATE.matSpecular.set (specular[0], specular[1], specular[2], 1.0F);
+ STATE.matEmissive.set (emissive[0], emissive[1], emissive[2], 1.0F);
+ float glshine = std::max<float>(std::min<float>(shininess,1.0f), 0.0f) * 128.0f;
+ STATE.matShininess = glshine;
+}
+
+
+void GFX_GL_IMPL::SetColor( const float color[4] )
+{
+ DBG_LOG_GLES20("SetColor()");
+ STATE.color.set( color );
+ // Emulate OpenGL's behaviour
+ ImmediateColor( color[0], color[1], color[2], color[3] );
+}
+
+void GFX_GL_IMPL::SetViewport( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES20("SetViewport(%d, %d, %d, %d)", x, y, width, height);
+ STATE.viewport[0] = x;
+ STATE.viewport[1] = y;
+ STATE.viewport[2] = width;
+ STATE.viewport[3] = height;
+ GLES_CHK(glViewport( x, y, width, height ));
+}
+
+void GFX_GL_IMPL::GetViewport( int* port ) const
+{
+ DBG_LOG_GLES20("GetViewport()");
+ port[0] = STATE.viewport[0];
+ port[1] = STATE.viewport[1];
+ port[2] = STATE.viewport[2];
+ port[3] = STATE.viewport[3];
+}
+
+
+
+void GFX_GL_IMPL::SetScissorRect( int x, int y, int width, int height )
+{
+ DBG_LOG_GLES20("SetScissorRect(%d, %d, %d, %d)", x, y, width, height);
+ if (STATE.scissor != 1)
+ {
+ GLES_CHK(glEnable( GL_SCISSOR_TEST ));
+ STATE.scissor = 1;
+ }
+
+
+ STATE.scissorRect[0] = x;
+ STATE.scissorRect[1] = y;
+ STATE.scissorRect[2] = width;
+ STATE.scissorRect[3] = height;
+ GLES_CHK(glScissor( x, y, width, height ));
+
+}
+
+bool GFX_GL_IMPL::IsCombineModeSupported( unsigned int combiner )
+{
+ return true;
+}
+
+void GFX_GL_IMPL::DisableScissor()
+{
+ DBG_LOG_GLES20("DisableScissor()");
+ if (STATE.scissor != 0)
+ {
+ GLES_CHK(glDisable( GL_SCISSOR_TEST ));
+ STATE.scissor = 0;
+ }
+}
+
+bool GFX_GL_IMPL::IsScissorEnabled() const
+{
+ DBG_LOG_GLES20("IsScissorEnabled():returns %s", STATE.scissor == 1?"True":"False");
+ return STATE.scissor == 1;
+}
+
+void GFX_GL_IMPL::GetScissorRect( int scissor[4] ) const
+{
+ DBG_LOG_GLES20("GetScissorRect()");
+ scissor[0] = STATE.scissorRect[0];
+ scissor[1] = STATE.scissorRect[1];
+ scissor[2] = STATE.scissorRect[2];
+ scissor[3] = STATE.scissorRect[3];
+}
+
+TextureCombinersHandle GFX_GL_IMPL::CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular )
+{
+ DBG_LOG_GLES20("CreateTextureCombiners()");
+ TextureCombinersGLES2* implGLES = TextureCombinersGLES2::Create (count, texEnvs);
+ return TextureCombinersHandle (implGLES);
+}
+
+
+void GFX_GL_IMPL::SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors )
+{
+ DBG_LOG_GLES20("SetTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES2);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ STATE.textureCount = count;
+ for (int i = 0; i < count; ++i)
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+
+ // set the texture
+ ApplyTexEnvData (i, i, texEnvData[i]);
+
+ // setup texture unit state
+ TextureUnitStateGLES2& texUnitState = STATE.textures[i];
+ texUnitState.color = texColors[i];
+ texUnitState.combColor = binding.m_CombColor;
+ texUnitState.combAlpha = binding.m_CombAlpha;
+ }
+ // we can just create mask and "and" with it
+ // but consider me lazy
+ for( int i = count ; i < gGraphicsCaps.maxTexUnits ; ++i)
+ STATE.DropTexGen(i);
+
+ STATE.activeProgram = 0;
+
+ // Get us back to TU 0, so we know where we stand
+ ActivateTextureUnitGLES2 (STATE, 0);
+}
+
+void GFX_GL_IMPL::DeleteTextureCombiners( TextureCombinersHandle& textureCombiners )
+{
+ DBG_LOG_GLES20("DeleteTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners, TextureCombinersGLES2);
+ delete implGLES;
+ textureCombiners.Reset();
+}
+
+void GFX_GL_IMPL::SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props )
+{
+ DBG_LOG_GLES20("SetTextureCombiners()");
+ TextureCombinersGLES2* implGLES = OBJECT_FROM_HANDLE(textureCombiners,TextureCombinersGLES2);
+ Assert (implGLES);
+
+ const int count = std::min(gGraphicsCaps.maxTexUnits, implGLES->count);
+ // Fill in arrays
+ TexEnvData* texEnvData;
+ ALLOC_TEMP (texEnvData, TexEnvData, count);
+ for( int i = 0; i < count; ++i )
+ {
+ ShaderLab::TexEnv *te = ShaderLab::GetTexEnvForBinding( implGLES->texEnvs[i], props );
+ Assert( te != NULL );
+ te->PrepareData (implGLES->texEnvs[i].m_TextureName.index, implGLES->texEnvs[i].m_MatrixName, props, &texEnvData[i]);
+ }
+
+ Vector4f* texColors;
+ ALLOC_TEMP (texColors, Vector4f, implGLES->count);
+ for( int i = 0; i < implGLES->count; ++i )
+ {
+ const ShaderLab::TextureBinding& binding = implGLES->texEnvs[i];
+ texColors[i] = binding.GetTexColor().Get (props);
+ }
+ GFX_GL_IMPL::SetTextureCombinersThreadable(textureCombiners, texEnvData, texColors);
+}
+
+void GFX_GL_IMPL::SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias)
+{
+ DBG_LOG_GLES20("SetTexture(%d %d)", unit, texture.m_ID);
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ TextureUnitStateGLES2& currTex = STATE.textures[unit];
+ if (STATE.textureCount > unit && targetTex == currTex.texID)
+ {
+ return;
+ }
+ ActivateTextureUnitGLES2 (STATE, unit);
+
+ switch (dim)
+ {
+ case kTexDim2D: GLES_CHK(glBindTexture(GL_TEXTURE_2D, targetTex)); break;
+ case kTexDimCUBE: GLES_CHK(glBindTexture(GL_TEXTURE_CUBE_MAP, targetTex)); break;
+ default: break;
+ }
+
+ if (STATE.textureCount <= unit)
+ STATE.textureCount = unit+1;
+ currTex.texID = targetTex;
+ currTex.texDim = dim;
+ if (currTex.texGen == kTexGenUnknown)
+ currTex.texGen = kTexGenDisabled;
+
+ STATE.ApplyTexGen(unit);
+
+ GLESAssert();
+
+ #if defined(GL_EXT_texture_lod_bias) && !UNITY_LINUX
+ if (gGraphicsCaps.hasMipLevelBias && unitState.bias != bias && bias != std::numeric_limits<float>::infinity())
+ {
+ GL_CHK(glTexEnvf( GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias ));
+ unitState.bias = bias;
+ }
+ #endif
+}
+
+void GFX_GL_IMPL::SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16])
+{
+ DBG_LOG_GLES20("SetTextureTransform()");
+ DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ TextureUnitStateGLES2& unitState = STATE.textures[unit];
+
+ unitState.SetTexGen(texGen);
+ unitState.textureMatrix = *(Matrix4x4f const*)matrix;
+
+ // Since we will set texture matrix even if TexGen is disabled (since matrix can contain scale/offset information)
+ // we set textureMatrix to identity to be on a safe side
+ if (identity)
+ unitState.textureMatrix.SetIdentity();
+ unitState.identityMatrix = identity;
+
+ unitState.isProjected = false;
+ if (!identity && dim==kTexDim2D)
+ unitState.isProjected = (matrix[3] != 0.0f || matrix[7] != 0.0f || matrix[11] != 0.0f || matrix[15] != 1.0f);
+
+ STATE.ApplyTexGen(unit);
+}
+
+static const unsigned long kGLES20TextureDimensionTable[kTexDimCount] = {0, -1/*GL_TEXTURE_1D*/, GL_TEXTURE_2D, -1/*GL_TEXTURE_3D*/, GL_TEXTURE_CUBE_MAP, 0};
+
+void GFX_GL_IMPL::SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace )
+{
+ DBG_LOG_GLES20("SetTextureParams()");
+
+ TextureIdMapGLES20_QueryOrCreate(texture);
+
+ GLuint target = kGLES20TextureDimensionTable[texDim];
+ SetTexture (kShaderFragment, 0, 0, texture, texDim, std::numeric_limits<float>::infinity());
+
+ // Anisotropic texturing...
+ if( gGraphicsCaps.hasAnisoFilter )
+ {
+ anisoLevel = std::min( anisoLevel, gGraphicsCaps.maxAnisoLevel );
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel ));
+ }
+
+ GLenum wrapMode = kWrapModeES2[wrap];
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_S, wrapMode ));
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_WRAP_T, wrapMode ));
+
+ if( !hasMipMap && filter == kTexFilterTrilinear )
+ filter = kTexFilterBilinear;
+
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MAG_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST ));
+ if( hasMipMap )
+ GLES_CHK(glTexParameteri( target, GL_TEXTURE_MIN_FILTER, kMinFilterES2[filter] ));
+ else
+ GLES_CHK(glTexParameteri (target, GL_TEXTURE_MIN_FILTER, filter != kTexFilterNearest ? GL_LINEAR : GL_NEAREST));
+
+ GLESAssert();
+}
+
+void GFX_GL_IMPL::SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount])
+{
+ DBG_LOG_GLES20("SetShadersThreadable()");
+ GpuProgram* vertexProgram = programs[kShaderVertex];
+ GpuProgram* fragmentProgram = programs[kShaderFragment];
+
+ DBG_LOG_GLES20("SetShaders(%s, %s)",
+ GetShaderImplTypeString(vertexProgram? vertexProgram->GetImplType():kShaderImplUndefined),
+ GetShaderImplTypeString(fragmentProgram? fragmentProgram->GetImplType():kShaderImplUndefined));
+
+ // GLSL is only supported like this:
+ // vertex shader actually is both vertex & fragment linked program
+ // fragment shader is unused
+ if (vertexProgram && vertexProgram->GetImplType() == kShaderImplBoth)
+ {
+ Assert(fragmentProgram == 0 || fragmentProgram->GetImplType() == kShaderImplBoth);
+ STATE.activeProgram = vertexProgram;
+ STATE.activeProgramParams = params[kShaderVertex];
+ DebugAssert(STATE.activeProgramParams->IsReady());
+ int bufferSize = STATE.activeProgramParams->GetValuesSize();
+ STATE.activeProgramParamsBuffer.resize_uninitialized(bufferSize);
+ memcpy(STATE.activeProgramParamsBuffer.data(), paramsBuffer[kShaderVertex], bufferSize);
+ }
+ else
+ {
+ Assert(vertexProgram == 0);
+ STATE.activeProgram = 0;
+ STATE.activeProgramParams = 0;
+ STATE.activeProgramParamsBuffer.resize_uninitialized(0);
+ }
+}
+
+void GFX_GL_IMPL::CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode )
+{
+ GlslGpuProgramGLES20& programGLES = static_cast<GlslGpuProgramGLES20&>(program->GetGpuProgram());
+ programGLES.GetGLProgram(fogMode, program->GetParams(fogMode), program->GetChannels());
+}
+
+bool GFX_GL_IMPL::IsShaderActive( ShaderType type ) const
+{
+ //DBG_LOG_GLES20("IsShaderActive(%s): returns %s", GetShaderTypeString(type), STATE.shaderEnabledImpl[type] != kShaderImplUndefined?"True":"False");
+ //return STATE.shaderEnabledImpl[type] != kShaderImplUndefined;
+ DBG_LOG_GLES20("IsShaderActive(%s):", GetShaderTypeString(type));
+ return (STATE.activeProgram != 0);
+}
+
+void GFX_GL_IMPL::DestroySubProgram( ShaderLab::SubProgram* subprogram )
+{
+ //@TODO
+ //if (STATE.activeProgram == program)
+ //{
+ // STATE.activeProgram = NULL;
+ // STATE.activeProgramProps = NULL;
+ //}
+ delete subprogram;
+}
+
+
+
+void GFX_GL_IMPL::DisableLights( int startLight )
+{
+ DBG_LOG_GLES20("DisableLights(%d)", startLight);
+ startLight = std::min (startLight, gGraphicsCaps.maxLights);
+ STATE.vertexLightCount = startLight;
+ for (int i = startLight; i < kMaxSupportedVertexLightsByGLES20; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Position + i), Vector4f(0.0f, 0.0f, 1.0f, 0.0f));
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecLight0Diffuse + i), Vector4f(0.0f, 0.0f, 0.0f, 0.0f));
+ }
+}
+
+
+void GFX_GL_IMPL::SetLight( int light, const GfxVertexLight& data)
+{
+ DBG_LOG_GLES20("SetLight(%d), [{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, %f, %f, %f, %d]",
+ light,
+ data.position[0], data.position[1], data.position[2], data.position[3],
+ data.spotDirection[0], data.spotDirection[1], data.spotDirection[2], data.spotDirection[3],
+ data.color[0], data.color[1], data.color[2], data.color[3],
+ data.range, data.quadAtten, data.spotAngle, data.type);
+ Assert( light >= 0 && light < kMaxSupportedVertexLights );
+
+ if (light >= kMaxSupportedVertexLightsByGLES20)
+ return;
+
+ STATE.vertexLightTypes[light] = data.type;
+
+ SetupVertexLightParams (light, data);
+}
+
+void GFX_GL_IMPL::SetAmbient( const float ambient[4] )
+{
+ DBG_LOG_GLES20("SetAmbient()");
+ STATE.ambient.set (ambient[0], ambient[1], ambient[2], ambient[3]);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecLightModelAmbient, Vector4f(ambient));
+}
+
+void GFX_GL_IMPL::EnableFog (const GfxFogParams& fog)
+{
+ DBG_LOG_GLES20("EnableFog()");
+ DebugAssertIf( fog.mode <= kFogDisabled );
+ m_FogParams = fog;
+}
+
+void GFX_GL_IMPL::DisableFog()
+{
+ DBG_LOG_GLES20("DisableFog()");
+
+ if( m_FogParams.mode != kFogDisabled )
+ {
+ m_FogParams.mode = kFogDisabled;
+ m_FogParams.density = 0.0f;
+ }
+}
+
+VBO* GFX_GL_IMPL::CreateVBO()
+{
+ VBO* vbo = new GLES2VBO();
+ OnCreateVBO(vbo);
+ return vbo;
+}
+
+void GFX_GL_IMPL::DeleteVBO( VBO* vbo )
+{
+ OnDeleteVBO(vbo);
+ delete vbo;
+}
+
+DynamicVBO& GFX_GL_IMPL::GetDynamicVBO()
+{
+ if( !STATE.m_DynamicVBO ) {
+ STATE.m_DynamicVBO = new DynamicGLES2VBO();
+ }
+ return *STATE.m_DynamicVBO;
+}
+
+// ---------- render textures
+
+static GLuint GetFBO(GLuint* fbo)
+{
+ if (!(*fbo) && gGraphicsCaps.hasRenderToTexture)
+ glGenFramebuffers(1, fbo);
+
+ return *fbo;
+}
+static GLuint GetSharedFBO (DeviceStateGLES20& state) { return GetFBO (&state.m_SharedFBO); }
+static GLuint GetHelperFBO (DeviceStateGLES20& state) { return GetFBO (&state.m_HelperFBO); }
+
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags)
+{
+ RenderSurfaceHandle rs = CreateRenderColorSurfaceGLES2 (textureID, 0, width, height, dim, createFlags, format, samples);
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ return rs;
+}
+RenderSurfaceHandle GFX_GL_IMPL::CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags)
+{
+ RenderSurfaceHandle rs = CreateRenderDepthSurfaceGLES2 (textureID, 0, width, height, createFlags, depthFormat, samples);
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ return rs;
+}
+void GFX_GL_IMPL::DestroyRenderSurface (RenderSurfaceHandle& rs)
+{
+ // Early out if render surface is not created (don't do invalidate/flush in that case)
+ if( !rs.IsValid() )
+ return;
+
+ DestroyRenderSurfaceGLES2 (rs);
+
+ InvalidateState();
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+}
+void GFX_GL_IMPL::SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face)
+{
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+ if (SetRenderTargetGLES2 (count, colorHandles, depthHandle, mipLevel, face, GetSharedFBO(STATE)))
+ {
+ // changing render target might mean different color clear flags; so reset current state
+ STATE.m_CurrBlendState = NULL;
+ }
+#if GFX_DEVICE_VERIFY_ENABLE
+ VerifyState();
+#endif
+}
+
+void GFX_GL_IMPL::ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle)
+{
+ ResolveColorSurfaceGLES2(srcHandle, dstHandle, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderColorSurface(int index)
+{
+ Assert (index == 0);
+ return GetActiveRenderColorSurfaceGLES2();
+}
+RenderSurfaceHandle GFX_GL_IMPL::GetActiveRenderDepthSurface()
+{
+ return GetActiveRenderDepthSurfaceGLES2();
+}
+void GFX_GL_IMPL::SetSurfaceFlags (RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags)
+{
+}
+void GFX_GL_IMPL::DiscardContents (RenderSurfaceHandle& rs)
+{
+ DiscardContentsGLES2(rs);
+}
+
+
+// ---------- uploading textures
+
+void GFX_GL_IMPL::UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace )
+{
+ ::UploadTexture2DGLES2( texture, dimension, srcData, width, height, format, mipCount, uploadFlags, skipMipLevels, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ ::UploadTextureSubData2DGLES2( texture, srcData, mipLevel, x, y, width, height, format, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ ::UploadTextureCubeGLES2( texture, srcData, faceDataSize, size, format, mipCount, uploadFlags, colorSpace );
+}
+
+void GFX_GL_IMPL::UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags )
+{
+ ErrorString("3D textures are not supported by OpenGLES!");
+}
+
+void GFX_GL_IMPL::DeleteTexture(TextureID texture)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(texture);
+ if(targetTex == 0)
+ return;
+
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(texture.m_ID);
+ GLES_CHK(glDeleteTextures(1, &targetTex));
+
+ // invalidate texture unit states that used this texture
+ for( int i = 0; i < gGraphicsCaps.maxTexUnits; ++i )
+ {
+ TextureUnitStateGLES2& currTex = STATE.textures[i];
+ if( currTex.texID == targetTex )
+ currTex.Invalidate();
+ }
+
+ TextureIdMap::RemoveTexture(texture);
+}
+
+// ---------- context
+
+GfxDevice::PresentMode GFX_GL_IMPL::GetPresentMode()
+{
+ return UNITY_IPHONE ? kPresentAfterDraw : kPresentBeforeUpdate;
+}
+void GFX_GL_IMPL::BeginFrame()
+{
+ DBG_LOG_GLES20("BeginFrame()");
+ m_InsideFrame = true;
+
+ if(gGraphicsCaps.hasTiledGPU)
+ {
+ SetBackBufferGLES2();
+
+ extern void ClearCurrentFBImpl(bool clearColor, bool clearDepth);
+ ClearCurrentFBImpl(true,true);
+ }
+}
+void GFX_GL_IMPL::EndFrame()
+{
+ // on ios we do it in trampoline
+ // also we handle BackBuffer MSAA and Render Resolution ourselves, so discard is a bit more complicated
+#if !UNITY_IPHONE
+ if(gGraphicsCaps.gles20.hasDiscardFramebuffer)
+ {
+ SetBackBufferGLES2();
+
+ extern void DiscardCurrentFBImpl(bool discardColor, bool discardDepth, GLenum target);
+ DiscardCurrentFBImpl(false, true, GL_FRAMEBUFFER);
+ }
+#endif
+
+
+ DBG_LOG_GLES20("EndFrame()");
+ m_InsideFrame = false;
+}
+
+bool GFX_GL_IMPL::IsValidState()
+{
+#if UNITY_ANDROID
+ return ContextGLES::IsValid();
+#else
+ return true;
+#endif
+}
+
+bool GFX_GL_IMPL::HandleInvalidState()
+{
+#if UNITY_ANDROID
+ bool needReload;
+ if (!ContextGLES::HandleInvalidState(&needReload))
+ return false;
+ if (needReload)
+ ReloadResources();
+ return true;
+#else
+ return true;
+#endif
+}
+
+void GFX_GL_IMPL::PresentFrame()
+{
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("PresentFrame");
+ DBG_LOG_GLES20("====================================");
+ DBG_LOG_GLES20("====================================");
+
+#if UNITY_WIN
+ PresentContextGLES20();
+#else
+ PresentContextGLES();
+#endif
+}
+
+void GFX_GL_IMPL::FinishRendering()
+{
+ GLES_CHK(glFinish());
+}
+
+
+// ---------- immediate mode rendering
+
+// we break very large immediate mode submissions into multiple batches internally
+const int kMaxImmediateVerticesPerDraw = 2048;
+
+
+ImmediateModeGLES20::ImmediateModeGLES20()
+: m_Mode(kPrimitiveTriangles)
+, m_QuadsIB(0)
+, m_IndexBufferQuadsID(0)
+{
+ m_QuadsIB = (UInt16*)UNITY_MALLOC(kMemGeometry, kMaxImmediateVerticesPerDraw*6*sizeof(UInt16));
+ UInt32 baseIndex = 0;
+ UInt16* ibPtr = m_QuadsIB;
+ for( int i = 0; i < kMaxImmediateVerticesPerDraw; ++i )
+ {
+ ibPtr[0] = baseIndex + 1;
+ ibPtr[1] = baseIndex + 2;
+ ibPtr[2] = baseIndex;
+ ibPtr[3] = baseIndex + 2;
+ ibPtr[4] = baseIndex + 3;
+ ibPtr[5] = baseIndex;
+ baseIndex += 4;
+ ibPtr += 6;
+ }
+}
+
+ImmediateModeGLES20::~ImmediateModeGLES20()
+{
+ Invalidate();
+ UNITY_FREE(kMemGeometry,m_QuadsIB);
+}
+
+void ImmediateModeGLES20::Invalidate()
+{
+ if( m_IndexBufferQuadsID )
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferQuadsID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferQuadsID));
+ m_IndexBufferQuadsID = 0;
+ }
+ m_Vertices.clear();
+ memset( &m_Current, 0, sizeof(m_Current) );
+}
+
+void GFX_GL_IMPL::ImmediateVertex( float x, float y, float z )
+{
+ // If the current batch is becoming too large, internally end it and begin it again.
+ size_t currentSize = STATE.m_Imm.m_Vertices.size();
+ if( currentSize >= kMaxImmediateVerticesPerDraw - 4 )
+ {
+ GfxPrimitiveType mode = STATE.m_Imm.m_Mode;
+ // For triangles, break batch when multiple of 3's is reached.
+ if( mode == kPrimitiveTriangles && currentSize % 3 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ // For other primitives, break on multiple of 4's.
+ // NOTE: This won't quite work for triangle strips, but we'll just pretend
+ // that will never happen.
+ else if( mode != kPrimitiveTriangles && currentSize % 4 == 0 )
+ {
+ ImmediateEnd();
+ ImmediateBegin( mode );
+ }
+ }
+ Vector3f& vert = STATE.m_Imm.m_Current.vertex;
+ vert.x = x;
+ vert.y = y;
+ vert.z = z;
+ STATE.m_Imm.m_Vertices.push_back( STATE.m_Imm.m_Current );
+}
+
+void GFX_GL_IMPL::ImmediateNormal( float x, float y, float z )
+{
+ STATE.m_Imm.m_Current.normal.x = x;
+ STATE.m_Imm.m_Current.normal.y = y;
+ STATE.m_Imm.m_Current.normal.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateColor( float r, float g, float b, float a )
+{
+ r = clamp01(r);
+ g = clamp01(g);
+ b = clamp01(b);
+ a = clamp01(a);
+
+ STATE.m_Imm.m_Current.color =
+ ((UInt32)(a * 255.0f) << 24) |
+ ((UInt32)(b * 255.0f) << 16) |
+ ((UInt32)(g * 255.0f) << 8) |
+ ((UInt32)(r * 255.0f));
+}
+
+void GFX_GL_IMPL::ImmediateTexCoordAll( float x, float y, float z )
+{
+ for( int i = 0; i < gGraphicsCaps.maxTexCoords; ++i )
+ {
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[i];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+ }
+}
+
+void GFX_GL_IMPL::ImmediateTexCoord( int unit, float x, float y, float z )
+{
+ if( unit < 0 || unit >= 8 )
+ {
+ ErrorString( "Invalid unit for texcoord" );
+ return;
+ }
+ Vector3f& uv = STATE.m_Imm.m_Current.texCoords[unit];
+ uv.x = x;
+ uv.y = y;
+ uv.z = z;
+}
+
+void GFX_GL_IMPL::ImmediateBegin( GfxPrimitiveType type )
+{
+ STATE.m_Imm.m_Mode = type;
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+void GFX_GL_IMPL::ImmediateEnd()
+{
+ int vertexCount = STATE.m_Imm.m_Vertices.size();
+ if( vertexCount == 0 )
+ return;
+
+ InvalidateVertexInputCacheGLES20();
+
+ const size_t stride = sizeof(ImmediateVertexGLES20);
+ const ImmediateVertexGLES20* vertices = &STATE.m_Imm.m_Vertices[0];
+
+ void* dstVertices = LockSharedBufferGLES20 (GL_ARRAY_BUFFER, stride * vertexCount);
+ DebugAssert (dstVertices);
+ memcpy (dstVertices, vertices, stride * vertexCount);
+
+ if(STATE.m_Imm.m_Mode == kPrimitiveQuads && !STATE.m_Imm.m_IndexBufferQuadsID)
+ {
+ GLES_CHK(glGenBuffers( 1, (GLuint*)&STATE.m_Imm.m_IndexBufferQuadsID));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, STATE.m_Imm.m_IndexBufferQuadsID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxImmediateVerticesPerDraw * 6, STATE.m_Imm.m_QuadsIB, GL_STATIC_DRAW));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+
+ const GLuint vbo = UnlockSharedBufferGLES20 ();
+ const GLuint ibo = (STATE.m_Imm.m_Mode == kPrimitiveQuads)? STATE.m_Imm.m_IndexBufferQuadsID: 0;
+
+ //;;printf_console("ImmediateEnd> vbo: %d ibo: %d\n", vbo, ibo);
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
+
+ size_t offset = 0;
+ if(gGraphicsCaps.gles20.slowDynamicVBO)
+ offset = (size_t)static_cast<DynamicGLES2VBO&> (GetRealGfxDevice ().GetDynamicVBO ()).GetVertexMemory(stride * vertexCount);
+
+ GLES_CHK(glEnableVertexAttribArray(GL_VERTEX_ARRAY));
+ //;;printf_console("ImmediateEnd> vertex %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ GLES_CHK(glEnableVertexAttribArray(GL_NORMAL_ARRAY));
+ //;;printf_console("ImmediateEnd> normal %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_NORMAL_ARRAY, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ GLES_CHK(glEnableVertexAttribArray(GL_COLOR_ARRAY));
+ //;;printf_console("ImmediateEnd> color %d\n", offset);
+ GLES_CHK(glVertexAttribPointer(GL_COLOR_ARRAY, 4, GL_UNSIGNED_BYTE, true, stride, (void*)offset)); offset += sizeof(UInt32);
+ for (size_t q = 0; q < gGraphicsCaps.maxTexUnits; ++q)
+ {
+ if (GL_TEXTURE_ARRAY0 + q < gGraphicsCaps.gles20.maxAttributes)
+ {
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_ARRAY0 + q));
+ //;;printf_console("ImmediateEnd> tex%d %d\n", q, offset);
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY0 + q, 3, GL_FLOAT, false, stride, (void*)offset)); offset += sizeof(Vector3f);
+ }
+ else
+ {
+ #pragma message ("ToDo")
+ }
+ }
+
+ if(!gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ DebugAssert (offset <= stride);
+ }
+
+ BeforeDrawCall (true);
+
+ //;;printf_console("ImmediateEnd> dip %d %d\n", STATE.m_Imm.m_Mode, vertexCount);
+ switch (STATE.m_Imm.m_Mode)
+ {
+ case kPrimitiveTriangles:
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount / 3, vertexCount );
+ break;
+ case kPrimitiveTriangleStripDeprecated:
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount - 2, vertexCount );
+ break;
+ case kPrimitiveQuads:
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (vertexCount/2)*3, GL_UNSIGNED_SHORT, 0));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ case kPrimitiveLines:
+ GLES_CHK(glDrawArrays(GL_LINES, 0, vertexCount));
+ m_Stats.AddDrawCall( vertexCount / 2, vertexCount );
+ break;
+ default:
+ AssertString("ImmediateEnd: unknown draw mode");
+ }
+
+ // reset VBO bindings
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ InvalidateVertexInputCacheGLES20();
+
+ // clear vertices
+ STATE.m_Imm.m_Vertices.clear();
+}
+
+// ---------- readback path
+
+bool GFX_GL_IMPL::CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 )
+{
+ GLES_CHK(glReadPixels( left, bottom, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba32 ));
+ return true;
+}
+
+bool GFX_GL_IMPL::ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY )
+{
+ return ReadbackTextureGLES2(image, left, bottom, width, height, destX, destY, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+void GFX_GL_IMPL::GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height )
+{
+ ::GrabIntoRenderTextureGLES2(rs, rd, x, y, width, height, GetSharedFBO(STATE), GetHelperFBO(STATE));
+}
+
+
+#if ENABLE_PROFILER
+
+
+void GFX_GL_IMPL::BeginProfileEvent(const char* name)
+{
+ if(gGraphicsCaps.gles20.hasDebugMarkers)
+ gGlesExtFunc.glPushGroupMarkerEXT(0, name);
+}
+
+void GFX_GL_IMPL::EndProfileEvent()
+{
+ if(gGraphicsCaps.gles20.hasDebugMarkers)
+ gGlesExtFunc.glPopGroupMarkerEXT();
+}
+
+GfxTimerQuery* GFX_GL_IMPL::CreateTimerQuery()
+{
+ if( gGraphicsCaps.hasTimerQuery )
+ return new TimerQueryGLES;
+ return NULL;
+}
+
+void GFX_GL_IMPL::DeleteTimerQuery(GfxTimerQuery* query)
+{
+ delete query;
+}
+
+void GFX_GL_IMPL::BeginTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES.BeginTimerQueries();
+}
+
+void GFX_GL_IMPL::EndTimerQueries()
+{
+ if( !gGraphicsCaps.hasTimerQuery )
+ return;
+
+ g_TimerQueriesGLES.EndTimerQueries();
+}
+#endif // ENABLE_PROFILER
+
+typedef std::map<FixedFunctionStateGLES20, FixedFunctionProgramGLES20*, FullStateCompareGLES20> FFProgramCacheT;
+typedef std::map<FixedFunctionStateGLES20, GLShaderID, VertexStateCompareGLES20> FFVertexProgramCacheT;
+typedef std::map<FixedFunctionStateGLES20, GLShaderID, FragmentStateCompareGLES20> FFFragmentProgramCacheT;
+
+static FFProgramCacheT g_FixedFunctionProgramCache;
+static FFVertexProgramCacheT g_FFVertexProgramCache;
+static FFFragmentProgramCacheT g_FFFragmentProgramCache;
+
+void ClearFixedFunctionPrograms()
+{
+ for (FFVertexProgramCacheT::iterator it = g_FFVertexProgramCache.begin(); it != g_FFVertexProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFVertexProgramCache.clear();
+ for (FFFragmentProgramCacheT::iterator it = g_FFFragmentProgramCache.begin(); it != g_FFFragmentProgramCache.end(); ++it)
+ {
+ GLES_CHK(glDeleteShader(it->second));
+ }
+ g_FFFragmentProgramCache.clear();
+
+ for(FFProgramCacheT::iterator i = g_FixedFunctionProgramCache.begin(); i != g_FixedFunctionProgramCache.end(); ++i)
+ {
+ delete i->second;
+ }
+ g_FixedFunctionProgramCache.clear();
+
+}
+
+static const FixedFunctionProgramGLES20* GetFixedFunctionProgram(const FixedFunctionStateGLES20& state)
+{
+ FFProgramCacheT::const_iterator cachedProgIt = g_FixedFunctionProgramCache.find(state);
+ if (cachedProgIt != g_FixedFunctionProgramCache.end())
+ return cachedProgIt->second;
+
+ // Cache miss, create fixed function program
+ // NOTE: don't worry too much about performance of vertex/fragment maps
+ // shader building/compilation is crazy expensive anyway
+ FFVertexProgramCacheT::const_iterator vertexProgIt = g_FFVertexProgramCache.find(state);
+ FFFragmentProgramCacheT::const_iterator fragmentProgIt = g_FFFragmentProgramCache.find(state);
+
+ GLShaderID vertexShader = (vertexProgIt != g_FFVertexProgramCache.end())? vertexProgIt->second: 0;
+ GLShaderID fragmentShader = (fragmentProgIt != g_FFFragmentProgramCache.end())? fragmentProgIt->second: 0;
+
+ if (vertexShader == 0)
+ {
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ std::string src = BuildVertexShaderSourceGLES20(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES20("Compiling generated vertex shader");
+ CompileGlslShader(vertexShader, kErrorCompileVertexShader, cStr);
+ GLESAssert();
+
+ g_FFVertexProgramCache[state] = vertexShader;
+ }
+
+ if (fragmentShader == 0)
+ {
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+ std::string src = BuildFragmentShaderSourceGLES20(state);
+ const char* cStr = src.c_str();
+
+ DBG_SHADER_VERBOSE_GLES20("Compiling generated fragment shader");
+ CompileGlslShader(fragmentShader, kErrorCompileFragShader, cStr);
+ GLESAssert();
+
+ g_FFFragmentProgramCache[state] = fragmentShader;
+ }
+
+ DBG_SHADER_VERBOSE_GLES20("Creating and linking GLES program");
+ FixedFunctionProgramGLES20* ffProg = new FixedFunctionProgramGLES20(vertexShader, fragmentShader);
+ g_FixedFunctionProgramCache[state] = ffProg;
+
+ return ffProg;
+}
+
+static bool ComputeTextureTransformMatrix(TextureUnitStateGLES2 const& tex, Matrix4x4f const& worldViewMatrix, Matrix4x4f const& worldMatrix,
+ Matrix4x4f& outMatrix)
+{
+ switch (tex.texGen)
+ {
+ case kTexGenDisabled:
+ // NOTE: although tex-gen can be disabled
+ // textureMatrix can contain UV scale/offset
+ // so we will set it
+ case kTexGenObject:
+ if (tex.identityMatrix)
+ {
+ outMatrix.SetIdentity();
+ return false;
+ }
+ CopyMatrix(tex.textureMatrix.GetPtr(), outMatrix.GetPtr());
+ break;
+ case kTexGenSphereMap:
+ {
+ float invScale = 1.0f / Magnitude (worldViewMatrix.GetAxisX());
+
+ Matrix4x4f scaleOffsetMatrix;
+ scaleOffsetMatrix.SetScale(Vector3f(0.5*invScale, 0.5*invScale, 0.0));
+ scaleOffsetMatrix.SetPosition(Vector3f(0.5, 0.5, 0.0));
+
+ Matrix4x4f worldViewMatrixRotation = worldViewMatrix;
+ worldViewMatrixRotation.SetPosition(Vector3f::zero);
+ Matrix4x4f combo;
+ MultiplyMatrices4x4(&scaleOffsetMatrix, &worldViewMatrixRotation, &combo);
+ MultiplyMatrices4x4(&tex.textureMatrix, &combo, &outMatrix);
+ break;
+ }
+ case kTexGenEyeLinear:
+ MultiplyMatrices4x4(&tex.textureMatrix, &worldViewMatrix, &outMatrix);
+ break;
+ case kTexGenCubeNormal:
+ case kTexGenCubeReflect:
+ {
+ float invScale = 1.0f / Magnitude (worldMatrix.GetAxisX());
+ CopyMatrix(worldViewMatrix.GetPtr(), outMatrix.GetPtr());
+ outMatrix.Scale(Vector3f(invScale, invScale, invScale));
+ outMatrix.SetPosition(Vector3f::zero);
+ break;
+ }
+ default:
+ ErrorString( Format("Unknown TexGen mode %d", tex.texGen) );
+ }
+ return true;
+}
+
+void VBOContainsColorGLES20(bool flag)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ GetGLES20DeviceState(device).vboContainsColor = flag;
+}
+
+//chai: glUseProgram
+void GLSLUseProgramGLES20(UInt32 programID)
+{
+ GFX_GL_IMPL& device = static_cast<GFX_GL_IMPL&>(GetRealGfxDevice());
+ if (GetGLES20DeviceState(device).activeProgramID == programID)
+ {
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ if (gGraphicsCaps.gles20.buggyVFetchPatching)
+ {
+ // NOTE: Qualcomm driver (Android2.2) fails to re-patch vfetch instructions
+ // if shader stays the same, but vertex layout has been changed
+ // as a temporary workaround just reset the shader
+ GLES_CHK(glUseProgram (0)); // driver caches shaderid, so set to 0 first
+ GLES_CHK(glUseProgram (programID));
+ return;
+ }
+#endif
+ return;
+ }
+
+#if NV_STATE_FILTERING
+ GetGLES20DeviceState(device).transformState.dirtyFlags |= TransformState::kWorldViewProjDirty;
+#endif
+
+ GLES_CHK(glUseProgram (programID));
+ GetGLES20DeviceState(device).activeProgramID = programID;
+}
+
+
+struct SetValuesFunctorES2
+{
+ SetValuesFunctorES2(GfxDevice& device, UniformCacheGLES20* targetCache) : m_Device(device), m_TargetCache(targetCache) { }
+ GfxDevice& m_Device;
+ UniformCacheGLES20* m_TargetCache;
+ void SetVectorVal (ShaderParamType type, int index, const float* ptr, int cols)
+ {
+ switch(cols) {
+ case 1: CachedUniform1(m_TargetCache, type, index, ptr); break;
+ case 2: CachedUniform2(m_TargetCache, type, index, ptr); break;
+ case 3: CachedUniform3(m_TargetCache, type, index, ptr); break;
+ case 4: CachedUniform4(m_TargetCache, type, index, ptr); break;
+ }
+ }
+ void SetMatrixVal (int index, const Matrix4x4f* ptr, int rows)
+ {
+ DebugAssert(rows == 4);
+ GLES_CHK(glUniformMatrix4fv (index, 1, GL_FALSE, ptr->GetPtr()));
+ }
+ void SetTextureVal (ShaderType shaderType, int index, int samplerIndex, TextureDimension dim, TextureID texID)
+ {
+ m_Device.SetTexture (shaderType, index, samplerIndex, texID, dim, std::numeric_limits<float>::infinity());
+ }
+};
+
+
+//chai: 设置shader uniforms
+void GFX_GL_IMPL::BeforeDrawCall(bool immediateMode)
+{
+ DBG_LOG_GLES20("BeforeDrawCall(%s)", GetBoolString(immediateMode));
+
+ ShaderLab::PropertySheet *props = ShaderLab::g_GlobalProperties;
+ Assert(props);
+
+ // WorldView Matrix
+#if NV_STATE_FILTERING
+ // UpdateWorldViewMatrix will reset the dirty flags, however we want to forward any modified matrices
+ // to the shader, so we "reset" the dirty state after UpdateWorldViewMatrix has been called
+ UInt32 savedDirty = STATE.transformState.dirtyFlags;
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+ STATE.transformState.dirtyFlags = savedDirty;
+#else
+ STATE.transformState.UpdateWorldViewMatrix (m_BuiltinParamValues);
+#endif
+
+ // Materials
+ if (STATE.lighting)
+ {
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatEmission, Vector4f(STATE.matEmissive.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatAmbient, Vector4f(STATE.matAmbient.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatDiffuse, Vector4f(STATE.matDiffuse.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatSpecular, Vector4f(STATE.matSpecular.GetPtr()));
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFMatShininess, Vector4f(STATE.matShininess, STATE.matShininess, STATE.matShininess, STATE.matShininess));
+ }
+
+ // Fog
+ if (m_FogParams.mode > kFogDisabled)
+ {
+ float diff = m_FogParams.mode == kFogLinear ? m_FogParams.end - m_FogParams.start : 0.0f;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogColor, m_FogParams.color);
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFFogParams, Vector4f(m_FogParams.density * 1.2011224087f,
+ m_FogParams.density * 1.4426950408f,
+ m_FogParams.mode == kFogLinear ? -invDiff : 0.0f,
+ m_FogParams.mode == kFogLinear ? m_FogParams.end * invDiff : 0.0f
+ ));
+ }
+ // Alpha-test
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFAlphaTestRef, Vector4f(STATE.alphaValue, STATE.alphaValue, STATE.alphaValue, STATE.alphaValue));
+
+ UniformCacheGLES20* targetCache = 0;
+ if (STATE.activeProgram)
+ {
+ //chai: glUseProgram
+ //
+ // Apply GPU program
+ //转换类型
+ GlslGpuProgramGLES20& program = static_cast<GlslGpuProgramGLES20&>(*STATE.activeProgram);
+ int fogIndex = program.ApplyGpuProgramES20 (*STATE.activeProgramParams, STATE.activeProgramParamsBuffer.data());
+ m_BuiltinParamIndices[kShaderVertex] = &STATE.activeProgramParams->GetBuiltinParams();
+
+ targetCache = &program.m_UniformCache[fogIndex];
+ }
+ else
+ {
+ //chai:模拟固定管线
+ // Emulate Fixed Function pipe
+ m_BuiltinParamValues.SetVectorParam(kShaderVecFFColor, Vector4f(STATE.color.GetPtr()));
+ for (int i = 0; i < STATE.textureCount; ++i)
+ {
+ m_BuiltinParamValues.SetVectorParam(BuiltinShaderVectorParam(kShaderVecFFTextureEnvColor0+i), STATE.textures[i].color);
+ }
+
+ // generate program from fixed function state
+ DBG_LOG_GLES20(" using fixed-function");
+ FixedFunctionStateGLES20 ffstate;
+ STATE.ComputeFixedFunctionState(ffstate, m_FogParams);
+ const FixedFunctionProgramGLES20* program = GetFixedFunctionProgram(ffstate);
+ program->ApplyFFGpuProgram(m_BuiltinParamValues);
+ m_BuiltinParamIndices[kShaderVertex] = &program->GetBuiltinParams();
+
+ targetCache = &program->m_UniformCache;
+ }
+
+ //chai: 设置Unity内置uniforms
+
+ // Set Unity built-in parameters
+ {
+ Assert(m_BuiltinParamIndices[kShaderVertex]);
+ const BuiltinShaderParamIndices& params = *m_BuiltinParamIndices[kShaderVertex];
+
+ // MVP matrix
+ if (params.mat[kShaderInstanceMatMVP].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewProjDirty)
+#endif
+ )
+ {
+ Matrix4x4f wvp;
+ MultiplyMatrices4x4(&m_BuiltinParamValues.GetMatrixParam(kShaderMatProj), &STATE.transformState.worldViewMatrix, &wvp);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMVP];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, wvp.GetPtr()));
+ }
+ // MV matrix
+ if (params.mat[kShaderInstanceMatMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, STATE.transformState.worldViewMatrix.GetPtr()));
+ }
+ // Transpose of MV matrix
+ if (params.mat[kShaderInstanceMatTransMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ Matrix4x4f tWV;
+ TransposeMatrix4x4(&STATE.transformState.worldViewMatrix, &tWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, tWV.GetPtr()));
+ }
+ // Inverse transpose of MV matrix
+ if (params.mat[kShaderInstanceMatInvTransMV].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ // Inverse transpose of modelview should be scaled by uniform
+ // normal scale (this will match state.matrix.invtrans.modelview
+ // and gl_NormalMatrix in OpenGL)
+ Matrix4x4f mat = STATE.transformState.worldViewMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f invWV, tInvWV;
+ Matrix4x4f::Invert_General3D (mat, invWV);
+ TransposeMatrix4x4(&invWV, &tInvWV);
+
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvTransMV];
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, tInvWV.GetPtr()));
+ }
+ // M matrix
+ if (params.mat[kShaderInstanceMatM].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatM];
+ const Matrix4x4f& mat = STATE.transformState.worldMatrix;
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, mat.GetPtr()));
+ }
+ // Inverse M matrix
+ if (params.mat[kShaderInstanceMatInvM].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatInvM];
+ Matrix4x4f mat = STATE.transformState.worldMatrix;
+ if (STATE.normalization == kNormalizationScale)
+ {
+ // Kill scale in the world matrix before inverse
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ mat.Get (0, 0) *= invScale;
+ mat.Get (1, 0) *= invScale;
+ mat.Get (2, 0) *= invScale;
+ mat.Get (0, 1) *= invScale;
+ mat.Get (1, 1) *= invScale;
+ mat.Get (2, 1) *= invScale;
+ mat.Get (0, 2) *= invScale;
+ mat.Get (1, 2) *= invScale;
+ mat.Get (2, 2) *= invScale;
+ }
+ Matrix4x4f inverseMat;
+ Matrix4x4f::Invert_General3D (mat, inverseMat);
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, inverseMat.GetPtr()));
+ }
+
+ // Normal matrix
+ if (params.mat[kShaderInstanceMatNormalMatrix].gpuIndex >= 0
+#if NV_STATE_FILTERING
+ && (STATE.transformState.dirtyFlags & TransformState::kWorldViewDirty)
+#endif
+ )
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatNormalMatrix];
+
+ // @TBD: remove normalization in fixed function emulation after Normal matrix multiply.
+ Matrix4x4f rotWV;
+ rotWV = STATE.transformState.worldViewMatrix;
+ rotWV.SetPosition(Vector3f::zero); // reset translation
+
+ if (STATE.normalization == kNormalizationScale) // reset scale
+ {
+ float invScale = m_BuiltinParamValues.GetInstanceVectorParam(kShaderInstanceVecScale).w;
+ rotWV.Get (0, 0) *= invScale;
+ rotWV.Get (1, 0) *= invScale;
+ rotWV.Get (2, 0) *= invScale;
+ rotWV.Get (0, 1) *= invScale;
+ rotWV.Get (1, 1) *= invScale;
+ rotWV.Get (2, 1) *= invScale;
+ rotWV.Get (0, 2) *= invScale;
+ rotWV.Get (1, 2) *= invScale;
+ rotWV.Get (2, 2) *= invScale;
+ }
+ Matrix3x3f rotWV33 = Matrix3x3f(rotWV);
+
+ GLES_CHK(glUniformMatrix3fv (matParam.gpuIndex, 1, GL_FALSE, rotWV33.GetPtr()));
+ }
+
+
+ //chai: 设置向量uniforms
+ // Set instance vector parameters
+ for (int i = 0; i < kShaderInstanceVecCount; ++i)
+ {
+ int gpuIndexVS = params.vec[i].gpuIndex;
+ if (gpuIndexVS >= 0)
+ {
+ const float* val = m_BuiltinParamValues.GetInstanceVectorParam((ShaderBuiltinInstanceVectorParam)i).GetPtr();
+ switch (params.vec[i].dim) {
+ case 1: CachedUniform1(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 2: CachedUniform2(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 3: CachedUniform3(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ case 4: CachedUniform4(targetCache, kShaderParamFloat, gpuIndexVS, val); break;
+ }
+ GLESAssert();
+ }
+ }
+
+ // Texture Matrices
+ Matrix4x4f texM;
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; ++i)
+ {
+ BuiltinShaderParamIndices::MatrixParamData matParam = params.mat[kShaderInstanceMatTexture0 + i];
+ if (matParam.gpuIndex >= 0)
+ {
+ if (i < STATE.textureCount)
+ ComputeTextureTransformMatrix(STATE.textures[i], STATE.transformState.worldViewMatrix, STATE.transformState.worldMatrix, texM);
+ else
+ texM.SetIdentity();
+
+ Assert(matParam.rows == 4 && matParam.cols == 4);
+ GLES_CHK(glUniformMatrix4fv (matParam.gpuIndex, 1, GL_FALSE, texM.GetPtr()));
+ }
+ }
+#if NV_STATE_FILTERING
+ STATE.transformState.dirtyFlags = 0;
+#endif
+ }
+
+ // Set per-drawcall properties
+ SetValuesFunctorES2 setValuesFunc(*this, targetCache);
+ ApplyMaterialPropertyBlockValuesES(m_MaterialProperties, STATE.activeProgram, STATE.activeProgramParams, setValuesFunc);
+}
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits);
+ const TextureUnitStateGLES2& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES2::PositionRequiredForTexGen(unitState.texGen);
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen(int unit) const
+{
+ if (unit >= STATE.textureCount)
+ return false;
+ if (STATE.activeProgram)
+ return false;
+
+ //DebugAssertIf( unit < 0 || unit >= gGraphicsCaps.maxTexUnits );
+ const TextureUnitStateGLES2& unitState = STATE.textures[unit];
+ return TextureUnitStateGLES2::NormalRequiredForTexGen(unitState.texGen);
+}
+
+
+bool GFX_GL_IMPL::IsPositionRequiredForTexGen() const
+{
+ return ( STATE.positionTexGen != 0 && !STATE.activeProgram );
+}
+
+bool GFX_GL_IMPL::IsNormalRequiredForTexGen() const
+{
+ return ( STATE.normalTexGen != 0 && !STATE.activeProgram );
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultRTFormat() const
+{
+#if UNITY_PEPPER
+ // desktop platfrom = noone cares
+ return kRTFormatARGB32;
+#else
+
+ RenderTextureFormat defaultFormat = GetCurrentFBColorFormatGLES20();
+ return gGraphicsCaps.supportsRenderTextureFormat[defaultFormat] ? defaultFormat : kRTFormatARGB32;
+
+#endif
+}
+
+RenderTextureFormat GFX_GL_IMPL::GetDefaultHDRRTFormat() const
+{
+#if UNITY_PEPPER
+ // desktop platfrom = noone cares
+ return kRTFormatARGB32;
+#else
+
+ if(gGraphicsCaps.supportsRenderTextureFormat[kRTFormatARGBHalf])
+ return kRTFormatARGBHalf;
+
+ RenderTextureFormat defaultFormat = GetCurrentFBColorFormatGLES20();
+ return gGraphicsCaps.supportsRenderTextureFormat[defaultFormat] ? defaultFormat : kRTFormatARGB32;
+
+#endif
+}
+
+
+void* GFX_GL_IMPL::GetNativeTexturePointer(TextureID id)
+{
+ return (void*)TextureIdMap::QueryNativeTexture(id);
+}
+
+
+
+void GFX_GL_IMPL::ReloadResources()
+{
+ MarkAllVBOsLost();
+ GetDynamicVBO().Recreate();
+
+ GfxDevice::CommonReloadResources(kReleaseRenderTextures | kReloadShaders | kReloadTextures);
+ ClearFixedFunctionPrograms();
+
+ extern void ClearFBMapping();
+ ClearFBMapping();
+ STATE.m_SharedFBO = STATE.m_HelperFBO = 0;
+
+ InvalidateState();
+}
+
+void GFX_GL_IMPL::InitFramebufferDepthFormat()
+{
+ m_FramebufferDepthFormat = QueryFBDepthFormatGLES2();
+}
+
+// Acquire thread ownership on the calling thread. Worker releases ownership.
+void GFX_GL_IMPL::AcquireThreadOwnership()
+{
+#if UNITY_ANDROID
+ AcquireGLES20Context();
+#endif
+}
+
+// Release thread ownership on the calling thread. Worker acquires ownership.
+void GFX_GL_IMPL::ReleaseThreadOwnership()
+{
+#if UNITY_ANDROID
+ ReleaseGLES20Context();
+#endif
+}
+
+// and now the real magic
+void GfxDeviceGLES20_InitFramebufferDepthFormat()
+{
+ ((GFX_GL_IMPL&)GetRealGfxDevice()).InitFramebufferDepthFormat();
+}
+
+// ---------- verify state
+#if GFX_DEVICE_VERIFY_ENABLE
+void GFX_GL_IMPL::VerifyState()
+{
+}
+
+#endif // GFX_DEVICE_VERIFY_ENABLE
+
+#if NV_STATE_FILTERING
+ #ifdef glDeleteBuffers
+ #undef glDeleteBuffers
+ #endif
+ #ifdef glBindBuffer
+ #undef glBindBuffer
+ #endif
+ #ifdef glVertexAttribPointer
+ #undef glVertexAttribPointer
+ #endif
+#endif
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h
new file mode 100644
index 0000000..265477e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GfxDeviceGLES20.h
@@ -0,0 +1,190 @@
+#pragma once
+
+#if GFX_DEVICE_VIRTUAL
+
+#ifdef GFX_GL_IMPL
+#undef GFX_GL_IMPL
+#endif
+#define GFX_GL_IMPL GfxDeviceGLES20
+
+class GFX_GL_IMPL : public GfxThreadableDevice {
+public:
+ GFX_GL_IMPL();
+ GFX_API ~GFX_GL_IMPL();
+
+ GFX_API void InvalidateState();
+ #if GFX_DEVICE_VERIFY_ENABLE
+ GFX_API void VerifyState();
+ #endif
+
+ GFX_API void Clear (UInt32 clearFlags, const float color[4], float depth, int stencil);
+ GFX_API void SetUserBackfaceMode( bool enable );
+ GFX_API void SetWireframe(bool wire) { } // not possible in GLES
+ GFX_API bool GetWireframe() const { return false; } // not possible in GLES
+ GFX_API void SetInvertProjectionMatrix( bool enable );
+ GFX_API bool GetInvertProjectionMatrix() const;
+
+ GFX_API void SetWorldMatrix( const float matrix[16] );
+ GFX_API void SetViewMatrix( const float matrix[16] );
+
+ GFX_API void SetProjectionMatrix (const Matrix4x4f& matrix);
+ GFX_API void GetMatrix( float outMatrix[16] ) const;
+
+ GFX_API GPUSkinningInfo *CreateGPUSkinningInfo() { return NULL; }
+ GFX_API void DeleteGPUSkinningInfo(GPUSkinningInfo *info) { AssertBreak(false); }
+ GFX_API void SkinOnGPU( GPUSkinningInfo * info, bool lastThisFrame ) { AssertBreak(false); }
+ GFX_API void UpdateSkinSourceData(GPUSkinningInfo *info, const void *vertData, const BoneInfluence *skinData, bool dirty) { AssertBreak(false); }
+ GFX_API void UpdateSkinBonePoses(GPUSkinningInfo *info, const int boneCount, const Matrix4x4f* poses) { AssertBreak(false); }
+
+ GFX_API const float* GetWorldMatrix() const ;
+ GFX_API const float* GetViewMatrix() const;
+ GFX_API const float* GetProjectionMatrix() const;
+ GFX_API const float* GetDeviceProjectionMatrix() const;
+
+ GFX_API void SetNormalizationBackface( NormalizationMode mode, bool backface );
+ GFX_API void SetFFLighting( bool on, bool separateSpecular, ColorMaterialMode colorMaterial );
+ GFX_API void SetMaterial( const float ambient[4], const float diffuse[4], const float specular[4], const float emissive[4], const float shininess );
+ GFX_API void SetColor( const float color[4] );
+ GFX_API void SetViewport( int x, int y, int width, int height );
+ GFX_API void GetViewport( int* values ) const;
+
+ GFX_API void SetScissorRect( int x, int y, int width, int height );
+ GFX_API void DisableScissor();
+ GFX_API bool IsScissorEnabled() const;
+ GFX_API void GetScissorRect( int values[4] ) const;
+ GFX_API void SetSRGBWrite (const bool);
+ GFX_API bool GetSRGBWrite ();
+
+ GFX_API TextureCombinersHandle CreateTextureCombiners( int count, const ShaderLab::TextureBinding* texEnvs, const ShaderLab::PropertySheet* props, bool hasVertexColorOrLighting, bool usesAddSpecular );
+ GFX_API void DeleteTextureCombiners( TextureCombinersHandle& textureCombiners );
+ GFX_API void SetTextureCombiners( TextureCombinersHandle textureCombiners, const ShaderLab::PropertySheet* props );
+
+ GFX_API void SetTexture (ShaderType shaderType, int unit, int samplerUnit, TextureID texture, TextureDimension dim, float bias);
+ GFX_API void SetTextureParams( TextureID texture, TextureDimension texDim, TextureFilterMode filter, TextureWrapMode wrap, int anisoLevel, bool hasMipMap, TextureColorSpace colorSpace );
+ GFX_API void SetTextureTransform( int unit, TextureDimension dim, TexGenMode texGen, bool identity, const float matrix[16]);
+ GFX_API void SetTextureName ( TextureID texture, const char* name ) { }
+
+ GFX_API void SetShadersThreadable (GpuProgram* programs[kShaderTypeCount], const GpuProgramParameters* params[kShaderTypeCount], UInt8 const * const paramsBuffer[kShaderTypeCount]);
+ GFX_API void CreateShaderParameters( ShaderLab::SubProgram* program, FogMode fogMode );
+
+ GFX_API bool IsCombineModeSupported( unsigned int combiner );
+ GFX_API void SetTextureCombinersThreadable( TextureCombinersHandle textureCombiners, const TexEnvData* texEnvData, const Vector4f* texColors );
+
+ GFX_API bool IsShaderActive( ShaderType type ) const;
+ GFX_API void DestroySubProgram( ShaderLab::SubProgram* subprogram );
+
+ GFX_API void DisableLights( int startLight );
+ GFX_API void SetLight( int light, const GfxVertexLight& data);
+ GFX_API void SetAmbient( const float ambient[4] );
+
+ GFX_API void EnableFog (const GfxFogParams& fog);
+ GFX_API void DisableFog();
+
+ GFX_API VBO* CreateVBO();
+ GFX_API void DeleteVBO( VBO* vbo );
+ GFX_API DynamicVBO& GetDynamicVBO();
+
+ GFX_API RenderSurfaceHandle CreateRenderColorSurface (TextureID textureID, int width, int height, int samples, int depth, TextureDimension dim, RenderTextureFormat format, UInt32 createFlags);
+ GFX_API RenderSurfaceHandle CreateRenderDepthSurface (TextureID textureID, int width, int height, int samples, TextureDimension dim, DepthBufferFormat depthFormat, UInt32 createFlags);
+ GFX_API void DestroyRenderSurface (RenderSurfaceHandle& rs);
+ GFX_API void SetRenderTargets (int count, RenderSurfaceHandle* colorHandles, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face = kCubeFaceUnknown);
+ GFX_API void ResolveColorSurface (RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle);
+ GFX_API RenderSurfaceHandle GetActiveRenderColorSurface (int index);
+ GFX_API RenderSurfaceHandle GetActiveRenderDepthSurface ();
+ GFX_API void SetSurfaceFlags(RenderSurfaceHandle surf, UInt32 flags, UInt32 keepFlags);
+ GFX_API void DiscardContents (RenderSurfaceHandle& rs);
+
+ GFX_API void UploadTexture2D( TextureID texture, TextureDimension dimension, UInt8* srcData, int srcSize, int width, int height, TextureFormat format, int mipCount, UInt32 uploadFlags, int skipMipLevels, TextureUsageMode usageMode, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureSubData2D( TextureID texture, UInt8* srcData, int srcSize, int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+ GFX_API void UploadTextureCube( TextureID texture, UInt8* srcData, int srcSize, int faceDataSize, int size, TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+ GFX_API void UploadTexture3D( TextureID texture, UInt8* srcData, int srcSize, int width, int height, int depth, TextureFormat format, int mipCount, UInt32 uploadFlags );
+ GFX_API void DeleteTexture( TextureID texture );
+
+ GFX_API PresentMode GetPresentMode();
+
+ GFX_API void BeginFrame();
+ GFX_API void EndFrame();
+ GFX_API void PresentFrame();
+
+ GFX_API bool IsValidState();
+ GFX_API bool HandleInvalidState();
+
+ GFX_API void FinishRendering();
+
+ // Immediate mode rendering
+ GFX_API void ImmediateVertex( float x, float y, float z );
+ GFX_API void ImmediateNormal( float x, float y, float z );
+ GFX_API void ImmediateColor( float r, float g, float b, float a );
+ GFX_API void ImmediateTexCoordAll( float x, float y, float z );
+ GFX_API void ImmediateTexCoord( int unit, float x, float y, float z );
+ GFX_API void ImmediateBegin( GfxPrimitiveType type );
+ GFX_API void ImmediateEnd();
+
+ GFX_API void AcquireThreadOwnership();
+ GFX_API void ReleaseThreadOwnership();
+
+ protected:
+
+
+ GFX_API bool CaptureScreenshot( int left, int bottom, int width, int height, UInt8* rgba32 );
+ GFX_API bool ReadbackImage( ImageReference& image, int left, int bottom, int width, int height, int destX, int destY );
+ GFX_API void GrabIntoRenderTexture( RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height );
+
+ GFX_API void BeforeDrawCall( bool immediateMode);
+
+ GFX_API void SetBlendState(const DeviceBlendState* state, float alphaRef);
+ GFX_API void SetRasterState(const DeviceRasterState* state);
+ GFX_API void SetDepthState(const DeviceDepthState* state);
+ GFX_API void SetStencilState(const DeviceStencilState* state, int stencilRef);
+
+ GFX_API DeviceBlendState* CreateBlendState(const GfxBlendState& state);
+ GFX_API DeviceDepthState* CreateDepthState(const GfxDepthState& state);
+ GFX_API DeviceStencilState* CreateStencilState(const GfxStencilState& state);
+ GFX_API DeviceRasterState* CreateRasterState(const GfxRasterState& state);
+
+#if ENABLE_PROFILER
+ GFX_API void BeginProfileEvent (const char* name);
+ GFX_API void EndProfileEvent ();
+
+ GFX_API GfxTimerQuery* CreateTimerQuery();
+ GFX_API void DeleteTimerQuery(GfxTimerQuery* query);
+ GFX_API void BeginTimerQueries();
+ GFX_API void EndTimerQueries();
+#endif
+
+public:
+ // OpenGLES specific
+ GFX_API void ReloadResources();
+ GFX_API bool IsPositionRequiredForTexGen(int texStageIndex) const;
+ GFX_API bool IsNormalRequiredForTexGen(int texStageIndex) const;
+
+ GFX_API bool IsPositionRequiredForTexGen() const;
+ GFX_API bool IsNormalRequiredForTexGen() const;
+
+ GFX_API RenderTextureFormat GetDefaultRTFormat() const;
+ GFX_API RenderTextureFormat GetDefaultHDRRTFormat() const;
+
+ GFX_API void* GetNativeTexturePointer(TextureID id);
+
+ DeviceStateGLES20& GetState() { return state; }
+
+ void InitFramebufferDepthFormat();
+
+private:
+ DeviceStateGLES20 state;
+};
+
+#define STATE this->state
+#define GetGLES20DeviceState(device) device.GetState()
+
+#else // GFX_DEVICE_VIRTUAL
+
+
+struct GfxDeviceImpl {
+ DeviceStateGLES20 state;
+};
+
+#define STATE impl->state
+#define GetGLES20DeviceState(device) device.GetImpl()->state
+
+#endif // GFX_DEVICE_VIRTUAL
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp
new file mode 100644
index 0000000..e28fbde
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp
@@ -0,0 +1,1120 @@
+#include "UnityPrefix.h"
+#if GFX_SUPPORTS_OPENGLES20
+#include "GpuProgramsGLES20.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "External/shaderlab/Library/ShaderLabErrors.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "External/shaderlab/Library/program.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/Math/Matrix3x3.h"
+#include "Runtime/Math/Vector4.h"
+
+
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "GpuPropertiesGLES20.h"
+#include "Runtime/Utilities/GLSLUtilities.h"
+#include "VBOGLES20.h"
+#include "DebugGLES20.h"
+#include "UnityGLES20Ext.h"
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+ #include "PlatformDependent/AndroidPlayer/AndroidSystemInfo.h"
+#endif
+
+#if UNITY_BLACKBERRY
+ #include "ctype.h"
+#endif
+
+
+#include <stdio.h>
+
+#define DEBUG_GLSL_BINDINGS 0
+#define DEBUG_SHADER_CACHE 0
+
+
+// from shader_yacc.hpp
+extern std::string g_LastParsedShaderName;
+
+void GLSLUseProgramGLES20 (UInt32 programID); // defined in GfxDeviceGLES20.cpp
+
+bool CompileGLSLVertexShader (const std::string& source, ChannelAssigns& channels, GLShaderID programID, GLShaderID parentProgramID, GLShaderID* outShaderID);
+bool CompileGLSLFragmentShader (const std::string& source, GLShaderID* outShaderID);
+bool BindVProgAttrbutes(const std::string& source, ChannelAssigns& channels, GLShaderID programID);
+bool RebindVProgAttrbutes(GLShaderID programID, GLShaderID parentProgramID);
+
+
+static void GetCachedBinaryName(const std::string& vprog, const std::string& fshader, char filename[33]);
+
+
+// --------------------------------------------------------------------------
+// GLSL
+
+// AttributeConversionTable
+const static UInt32 kAttribLookupTableSize = 12;
+
+const static char* s_GLSLESAttributes[kAttribLookupTableSize] = {
+ "_glesVertex", "_glesColor", "_glesNormal",
+ "_gles_unused__", // unused
+ "_glesMultiTexCoord0", "_glesMultiTexCoord1", "_glesMultiTexCoord2", "_glesMultiTexCoord3",
+ "_glesMultiTexCoord4", "_glesMultiTexCoord5", "_glesMultiTexCoord6", "_glesMultiTexCoord7"
+};
+const static char* s_UnityAttributes[kAttribLookupTableSize] = {
+ "Vertex", "Color", "Normal",
+ "", // unused
+ "TexCoord", "TexCoord1", "TexCoord2", "TexCoord3",
+ "TexCoord4", "TexCoord5", "TexCoord6", "TexCoord7"
+};
+
+
+const VertexComponent s_UnityVertexComponents[kAttribLookupTableSize] = {
+ kVertexCompVertex,
+ kVertexCompColor,
+ kVertexCompNormal,
+ kVertexCompTexCoord,
+ kVertexCompTexCoord0, kVertexCompTexCoord1, kVertexCompTexCoord2, kVertexCompTexCoord3,
+ kVertexCompTexCoord4, kVertexCompTexCoord5, kVertexCompTexCoord6, kVertexCompTexCoord7
+};
+
+const GLuint s_GLESVertexComponents[kAttribLookupTableSize] = {
+ GL_VERTEX_ARRAY,
+ GL_COLOR_ARRAY,
+ GL_NORMAL_ARRAY,
+ ~0 /*kVertexCompTexCoord*/,
+ GL_TEXTURE_ARRAY0, GL_TEXTURE_ARRAY1, GL_TEXTURE_ARRAY2, GL_TEXTURE_ARRAY3,
+ GL_TEXTURE_ARRAY4, GL_TEXTURE_ARRAY5, GL_TEXTURE_ARRAY6, GL_TEXTURE_ARRAY7
+};
+
+GlslGpuProgramGLES20::GlslGpuProgramGLES20 (const std::string& source, CreateGpuProgramOutput& output)
+{
+ output.SetPerFogModeParamsEnabled(true);
+ m_ImplType = kShaderImplBoth;
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ m_GLSLVertexShader[i] = 0;
+ m_GLSLFragmentShader[i] = 0;
+ m_FogColorIndex[i] = -1;
+ m_FogParamsIndex[i] = -1;
+ m_FogFailed[i] = false;
+ }
+
+ // Fragment shaders come out as dummy GLSL text. Just ignore them; the real shader was part of
+ // the vertex shader text anyway.
+ if (source.empty())
+ return;
+
+ if (Create (source, output.CreateChannelAssigns()))
+ {
+ GpuProgramParameters& params = output.CreateParams();
+ FillParams (m_Programs[kFogDisabled], params, output.GetOutNames());
+ if (params.GetTextureParams().size() > gGraphicsCaps.maxTexImageUnits)
+ m_NotSupported = true;
+
+ m_UniformCache[kFogDisabled].Create(&params, -1, -1);
+ }
+ else
+ {
+ m_NotSupported = true;
+ }
+}
+
+GlslGpuProgramGLES20::~GlslGpuProgramGLES20 ()
+{
+ Assert (m_ImplType == kShaderImplBoth);
+ for (int i = 0; i < kFogModeCount; ++i)
+ {
+ if (m_GLSLVertexShader[i]) { GLES_CHK(glDeleteShader(m_GLSLVertexShader[i])); }
+ if (m_GLSLFragmentShader[i]) { GLES_CHK(glDeleteShader(m_GLSLFragmentShader[i])); }
+ if (m_Programs[i]) { GLES_CHK(glDeleteProgram(m_Programs[i])); }
+ m_UniformCache[i].Destroy();
+ }
+}
+
+static bool ParseGlslErrors (GLuint type, GLSLErrorType errorType, const char* source = 0)
+{
+ bool hadErrors = false;
+ char compileInfoLog[4096];
+ GLsizei infoLogLength = 0;
+ switch(errorType)
+ {
+ case kErrorCompileVertexShader:
+ case kErrorCompileFragShader:
+ glGetShaderInfoLog(type, 4096, &infoLogLength, compileInfoLog);
+ break;
+ case kErrorLinkProgram:
+ glGetProgramInfoLog(type, 4096, &infoLogLength, compileInfoLog);
+ break;
+ default:
+ FatalErrorMsg("Unknown error type");
+ break;
+ }
+
+ if(infoLogLength)
+ {
+ hadErrors = true;
+ if(source)
+ {
+ ErrorStringMsg("-------- failed compiling %s:\n", errorType==kErrorCompileVertexShader?"vertex program":"fragment shader");
+ DebugTextLineByLine(source);
+ }
+
+ ErrorStringMsg("-------- GLSL error: %s\n\n", compileInfoLog);
+ }
+
+ return hadErrors;
+}
+
+static std::string ExtractDefineBock(const std::string& defineName, const std::string& str, std::string* remainderStr)
+{
+ const std::string beginsWith = "#ifdef " + defineName;
+ const std::string endsWith = "#endif";
+
+ size_t b;
+ if ((b = str.find(beginsWith))==std::string::npos)
+ return "";
+
+ b += beginsWith.size();
+
+ size_t e = b;
+ size_t n = 1;
+ do
+ {
+ size_t nextEnd = str.find(endsWith, e);
+ size_t nextIf = str.find("#if", e);
+
+ if (nextEnd == std::string::npos)
+ return "";
+
+ if (nextIf != std::string::npos && nextIf < nextEnd)
+ {
+ ++n;
+ e = nextIf + 1;
+ }
+ else
+ {
+ --n;
+ e = nextEnd + 1;
+ }
+ }
+ while (n > 0);
+
+ std::string retVal = str.substr(b, e-b-1);
+ if (remainderStr)
+ {
+ *remainderStr = str.substr(0, b - beginsWith.size());
+ if (e + endsWith.size() < str.length()) *remainderStr += str.substr(e + endsWith.size());
+ }
+
+ return retVal;
+}
+
+static void FindProgramStart(const char* source, std::string* header, std::string* prog)
+{
+ const char* line_start = source;
+ while(*line_start)
+ {
+ while(isspace(*line_start))
+ ++line_start;
+ if(*line_start == '#')
+ {
+ while(*line_start != '\n' && *line_start != '\r')
+ ++line_start;
+ }
+ else
+ {
+ header->assign(source, line_start - source);
+ prog->assign(line_start);
+ break;
+ }
+ }
+}
+
+bool CompileGlslShader(GLShaderID shader, GLSLErrorType type, const char* source)
+{
+ GLES_CHK(glShaderSource(shader, 1, &source, NULL));
+ GLES_CHK(glCompileShader(shader));
+
+ int compiled = 10;
+ GLES_CHK(glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled));
+
+ if (compiled==0)
+ {
+ ParseGlslErrors(shader, type, source);
+ return false;
+ }
+
+ return true;
+}
+
+bool GlslGpuProgramGLES20::Create (const std::string &shaderString, ChannelAssigns& channels)
+{
+ if( !gGraphicsCaps.gles20.hasGLSL )
+ return false;
+
+ GLESAssert(); // Clear any GL errors
+
+ GLES_CHK(m_Programs[0] = glCreateProgram());
+
+ m_ImplType = kShaderImplBoth;
+
+ // NOTE: pre-pending shader with VERTEX/FRAGMENT defines doesn't work with ES/GLSL compilers for some reason
+ // therefore we extract VERTEX/FRAGMENT sections from the shaderString
+
+ std::string remainder = shaderString;
+ std::string vertexShaderSource = ExtractDefineBock("VERTEX", shaderString, &remainder);
+ std::string fragmentShaderSource = ExtractDefineBock("FRAGMENT", remainder, &remainder);
+
+ vertexShaderSource = remainder + vertexShaderSource;
+ fragmentShaderSource = remainder + fragmentShaderSource;
+
+ if(!CompileProgram(0, vertexShaderSource, fragmentShaderSource, channels))
+ {
+ ParseGlslErrors(m_Programs[0], kErrorLinkProgram);
+
+ // TODO: cleanup
+ return false;
+ }
+
+ m_VertexShaderSourceForFog = vertexShaderSource;
+ m_SourceForFog = fragmentShaderSource;
+
+ return true;
+}
+
+std::string GlslGpuProgramGLES20::_CachePath;
+
+bool GlslGpuProgramGLES20::InitBinaryShadersSupport()
+{
+#if UNITY_ANDROID
+ // the implementation is broken on pre-HC on pvr at least, but we'd rather play safe with this dino stuff
+ if(android::systeminfo::ApiLevel() < android::apiHoneycomb)
+ return false;
+
+ // Huawei is osam in general
+ if(gGraphicsCaps.rendererString.find("Immersion") != std::string::npos)
+ return false;
+#endif
+
+ if(QueryExtension("GL_OES_get_program_binary") && gGlesExtFunc.glGetProgramBinaryOES && gGlesExtFunc.glProgramBinaryOES)
+ {
+ int binFormatCount = 0;
+ GLES_CHK(glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &binFormatCount));
+
+ if(binFormatCount > 0)
+ {
+ _CachePath = GetTemporaryCachePathApplicationSpecific() + "/UnityShaderCache/";
+ if(!IsDirectoryCreated(_CachePath))
+ CreateDirectory(_CachePath);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Internal_ClearShaderCache()
+{
+ std::string shaderCache = GetTemporaryCachePathApplicationSpecific() + "/UnityShaderCache/";
+ DeleteFileOrDirectory(shaderCache);
+ CreateDirectory(shaderCache);
+}
+
+bool GlslGpuProgramGLES20::CompileProgram(unsigned index, const std::string& vprog, const std::string& fshader, ChannelAssigns& channels)
+{
+ std::string binaryPath;
+ if(gGraphicsCaps.gles20.hasBinaryShaders)
+ {
+ char filename[33] = {0};
+ GetCachedBinaryName(vprog, fshader, filename);
+
+ binaryPath = _CachePath + filename;
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Starting compilation \"%s\" variation with md5:%s\n", g_LastParsedShaderName.c_str(), filename);
+ #endif
+ }
+
+ // first 4 bytes are for binary format
+ char* binaryData = 0;
+ char* binaryProgram = 0;
+ unsigned binaryLength = 0;
+
+ bool loadedBinary = false;
+ if(gGraphicsCaps.gles20.hasBinaryShaders)
+ {
+ FILE* binaryFile = ::fopen(binaryPath.c_str(), "rb");
+ if(binaryFile)
+ {
+ ::fseek(binaryFile, 0, SEEK_END);
+ unsigned datasz = (unsigned)::ftell(binaryFile);
+ ::fseek(binaryFile, 0, SEEK_SET);
+
+ binaryData = (char*)::malloc(datasz);
+ binaryProgram = binaryData + sizeof(GLenum);
+ binaryLength = datasz - sizeof(GLenum);
+ ::fread(binaryData, datasz, 1, binaryFile);
+ ::fclose(binaryFile);
+
+ loadedBinary = true;
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Loaded from cache");
+ #endif
+ }
+ }
+
+ if(loadedBinary)
+ {
+ loadedBinary = false;
+
+ bool attrBound = index==0 ? BindVProgAttrbutes(vprog, channels, m_Programs[0])
+ : RebindVProgAttrbutes(m_Programs[index], m_Programs[0]);
+ if(attrBound)
+ {
+ GLES_CHK(gGlesExtFunc.glProgramBinaryOES(m_Programs[index], *((GLenum*)binaryData), binaryProgram, binaryLength));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_LINK_STATUS, &linked));
+ loadedBinary = linked != 0;
+ }
+
+ if(!loadedBinary)
+ {
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Bad cached version\n");
+ #endif
+
+ ::free(binaryData);
+ binaryProgram = binaryData = 0;
+ }
+ }
+
+ // fallback to compiling shaders at runtime
+ if(!loadedBinary)
+ {
+ DBG_SHADER_VERBOSE_GLES20("Compiling shader: %s\n", g_LastParsedShaderName.c_str());
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Actually compiling");
+ #endif
+
+ if(!CompileGLSLVertexShader(vprog, channels, m_Programs[index], m_Programs[0], &m_GLSLVertexShader[index]))
+ return false;
+ if(!CompileGLSLFragmentShader(fshader, &m_GLSLFragmentShader[index]))
+ return false;
+
+ GLES_CHK(glAttachShader(m_Programs[index], m_GLSLVertexShader[index]));
+ GLES_CHK(glAttachShader(m_Programs[index], m_GLSLFragmentShader[index]));
+ GLES_CHK(glLinkProgram(m_Programs[index]));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_LINK_STATUS, &linked));
+ if(linked == 0)
+ return false;
+
+ if(gGraphicsCaps.gles20.hasBinaryShaders)
+ {
+ Assert(binaryData == 0 && binaryProgram == 0);
+
+ GLES_CHK(glGetProgramiv(m_Programs[index], GL_PROGRAM_BINARY_LENGTH_OES, (GLint*)&binaryLength));
+ binaryData = (char*)::malloc(binaryLength + sizeof(GLenum));
+ binaryProgram = binaryData + sizeof(GLenum);
+ GLES_CHK(gGlesExtFunc.glGetProgramBinaryOES(m_Programs[index], binaryLength, 0, (GLenum*)binaryData, binaryProgram));
+
+ FILE* binaryFile = ::fopen(binaryPath.c_str(), "wb");
+ if(binaryFile)
+ {
+ ::fwrite(binaryData, binaryLength + sizeof(GLenum), 1, binaryFile);
+ ::fclose(binaryFile);
+
+ #if DEBUG_SHADER_CACHE
+ ::printf_console("Saved to cache\n");
+ #endif
+ }
+ }
+ }
+
+ if(binaryData)
+ ::free(binaryData);
+
+ return true;
+}
+
+
+
+static void PrintNumber (char* s, int i, bool brackets)
+{
+ DebugAssert (i >= 0 && i < 100);
+ if (brackets)
+ *s++ = '[';
+
+ if (i < 10) {
+ *s++ = '0' + i;
+ } else {
+ *s++ = '0' + i/10;
+ *s++ = '0' + i%10;
+ }
+
+ if (brackets)
+ *s++ = ']';
+ *s++ = 0;
+}
+
+static void AddSizedVectorParam (GpuProgramParameters& params, ShaderParamType type, GLuint program, int vectorSize, int uniformNumber, int arraySize, const char* unityName, char* glName, int glNameIndexOffset, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddVectorParam (uniformNumber, type, vectorSize, unityName, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j)
+ {
+ PrintNumber (glName+glNameIndexOffset, j, true);
+ uniformNumber = glGetUniformLocation (program, glName);
+ PrintNumber (glName+glNameIndexOffset, j, false);
+ params.AddVectorParam (uniformNumber, type, vectorSize, glName, -1, outNames);
+ }
+ }
+}
+
+static void AddSizedMatrixParam (GpuProgramParameters& params, GLuint program, int rows, int cols, int uniformNumber, int arraySize, const char* unityName, char* glName, int glNameIndexOffset, PropertyNamesSet* outNames)
+{
+ if (arraySize <= 1)
+ {
+ params.AddMatrixParam (uniformNumber, unityName, rows, cols, -1, outNames);
+ }
+ else
+ {
+ for (int j = 0; j < arraySize; ++j)
+ {
+ PrintNumber (glName+glNameIndexOffset, j, true);
+ uniformNumber = glGetUniformLocation (program, glName);
+ PrintNumber (glName+glNameIndexOffset, j, false);
+ params.AddMatrixParam (uniformNumber, glName, rows, cols, -1, outNames);
+ }
+ }
+}
+
+
+void GlslGpuProgramGLES20::FillParams (unsigned int programID, GpuProgramParameters& params, PropertyNamesSet* outNames)
+{
+ if (!programID)
+ return;
+
+ int activeUniforms;
+
+ char name[1024];
+ GLenum type;
+ int arraySize = 0,
+ nameLength = 0,
+ bufSize = sizeof(name);
+
+ DBG_LOG_GLES20("GLSL: apply params to program id=%i\n", programID);
+ GLSLUseProgramGLES20 (programID);
+
+ // Figure out the uniforms
+ GLES_CHK(glGetProgramiv (programID, GL_ACTIVE_UNIFORMS, &activeUniforms));
+ for(int i=0; i < activeUniforms; i++)
+ {
+ //chai: 获得Uniform变量名
+ GLES_CHK(glGetActiveUniform (programID, i, bufSize, &nameLength, &arraySize, &type, name));
+
+ if (!strcmp (name, "_unity_FogParams") || !strcmp(name, "_unity_FogColor"))
+ continue;
+
+ // some Unity builtin properties are mapped to GLSL structure fields
+ // hijack them here
+ const char* glslName = GetGLSLESPropertyNameRemap(name);
+ const char* unityName = glslName ? glslName : name;
+
+ if (!strncmp (name, "gl_", 3)) // skip "gl_" names
+ continue;
+
+ int uniformNumber = glGetUniformLocation (programID, name);
+ Assert(uniformNumber != -1);
+
+ char* glName = name;
+ int glNameIndexOffset = 0;
+
+ bool isElemZero = false;
+ bool isArray = IsShaderParameterArray(name, nameLength, arraySize, &isElemZero);
+ if (isArray)
+ {
+ // for array parameters, transform name a bit: Foo[0] becomes Foo0
+ if (arraySize >= 100) {
+ ErrorString( "GLSL: array sizes larger than 99 not supported" ); // TODO: SL error
+ arraySize = 99;
+ }
+ // TODO: wrong? what if we use only array[1] for example
+ if (isElemZero)
+ {
+ glNameIndexOffset = nameLength-3;
+ glName[glNameIndexOffset] = '0';
+ glName[glNameIndexOffset+1] = 0;
+ }
+ else
+ {
+ glNameIndexOffset = nameLength;
+ }
+ }
+
+ if (type == GL_FLOAT)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_VEC2)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_VEC3)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_VEC4)
+ AddSizedVectorParam (params, kShaderParamFloat, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT_VEC2)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT_VEC3)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_INT_VEC4)
+ AddSizedVectorParam (params, kShaderParamInt, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 1, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL_VEC2)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 2, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL_VEC3)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_BOOL_VEC4)
+ AddSizedVectorParam (params, kShaderParamBool, programID, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_MAT4)
+ AddSizedMatrixParam (params, programID, 4, 4, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+ else if (type == GL_FLOAT_MAT3)
+ AddSizedMatrixParam (params, programID, 3, 3, uniformNumber, arraySize, unityName, glName, glNameIndexOffset, outNames);
+
+ else if (type == GL_SAMPLER_2D || type == GL_SAMPLER_2D_SHADOW_EXT) {
+ const int texIndex = params.GetTextureParams().size(); // statically bind this uniform to sequential texture index
+ GLES_CHK(glUniform1i (uniformNumber, texIndex));
+ params.AddTextureParam (texIndex, -1, unityName, kTexDim2D, outNames);
+ }
+ else if (type == GL_SAMPLER_CUBE) {
+ const int texIndex = params.GetTextureParams().size(); // statically bind this uniform to sequential texture index
+ GLES_CHK(glUniform1i (uniformNumber, texIndex));
+ params.AddTextureParam (texIndex, -1, unityName, kTexDimCUBE, outNames);
+ }
+ /*
+ else if(type == GL_SAMPLER_3D) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam( name, kTexDim3D, uniformNumber );
+ }
+ else if(type == GL_SAMPLER_2D_SHADOW) {
+ GLES_CHK(glUniform1i (uniformNumber, params.GetTextureParams().size()));
+ params.AddTextureParam( name, kTexDim2D, uniformNumber );
+ }
+
+ */
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ }
+
+ GLESAssert();
+}
+
+bool RebindVProgAttrbutes(GLShaderID programID, GLShaderID parentProgramID)
+{
+ int attribCount = 0;
+ GLES_CHK(glGetProgramiv(parentProgramID, GL_ACTIVE_ATTRIBUTES, &attribCount));
+
+ const int kBufSize = 256;
+ char name[kBufSize];
+ for(int i = 0 ; i < attribCount ; ++i)
+ {
+ int nameLength = 0, arraySize = 0;
+ GLenum type;
+ GLES_CHK(glGetActiveAttrib (parentProgramID, i, kBufSize, &nameLength, &arraySize, &type, name));
+ int location = glGetAttribLocation (parentProgramID, name);
+ if (location != -1)
+ GLES_CHK(glBindAttribLocation(programID, location, name));
+ }
+
+ return true;
+}
+
+bool BindVProgAttrbutes(const std::string& source, ChannelAssigns& channels, GLShaderID programID)
+{
+ // Add necessary attribute tags
+ for (UInt32 j = 0; j < kAttribLookupTableSize; j++)
+ {
+ if (source.find(s_GLSLESAttributes[j]) != std::string::npos)
+ {
+ if (s_GLESVertexComponents[j] >= gGraphicsCaps.gles20.maxAttributes)
+ {
+ ErrorString("Shader uses too many vertex attributes for this platform");
+ return false;
+ }
+ GLES_CHK(glBindAttribLocation(programID, s_GLESVertexComponents[j], s_GLSLESAttributes[j]));
+ ShaderChannel shaderChannel = GetShaderChannelFromName(s_UnityAttributes[j]);
+ if( shaderChannel != kShaderChannelNone )
+ channels.Bind (shaderChannel, s_UnityVertexComponents[j]);
+ }
+ }
+
+ // UGLY HACK:
+ // somewhere deep inside shader generation we just put attribute TANGENT
+ // without ever using it after
+ // look for TANGENT twice to be sure we use it
+ size_t firstTangentUsage = source.find("TANGENT");
+ if( firstTangentUsage != std::string::npos && source.find("TANGENT", firstTangentUsage+1) != std::string::npos )
+ {
+ // Find first free slot for tangents and use it.
+ // Unity normally supports 2 UV slots (0&1), so start looking from
+ // kVertexTexCoord2
+ for (int i = kVertexCompTexCoord2; i < kVertexCompTexCoord7; i++)
+ {
+ if (channels.GetSourceForTarget((VertexComponent)i) == kShaderChannelNone)
+ {
+ channels.Bind (kShaderChannelTangent, (VertexComponent)i);
+ GLES_CHK(glBindAttribLocation(programID, GL_TEXTURE_ARRAY0 + i - kVertexCompTexCoord0, "_glesTANGENT"));
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool CompileGLSLVertexShader (const std::string& source, ChannelAssigns& channels, GLShaderID programID, GLShaderID parentProgramID, GLShaderID* outShaderID)
+{
+ GLES_CHK(*outShaderID = glCreateShader(GL_VERTEX_SHADER));
+
+ if(parentProgramID == programID)
+ BindVProgAttrbutes(source, channels, programID);
+ else
+ RebindVProgAttrbutes(programID, parentProgramID);
+
+
+#if UNITY_ANDROID
+ if(gGraphicsCaps.gles20.buggyVprogTextures)
+ {
+ if( source.find("texture2D") != std::string::npos || source.find("tex2D") != std::string::npos )
+ {
+ ErrorString("GLES20: Running on platform with buggy vprog textures.\n");
+ ErrorString("GLES20: Compiling this shader may result in crash.\n");
+ ErrorString("GLES20: Shader in question:\n");
+ DebugTextLineByLine(source.c_str());
+ ErrorString("\n---------------\n");
+ }
+ }
+#endif
+
+ const char* text = source.c_str();
+ DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER("Compiling VERTEX program:", text);
+
+ return CompileGlslShader(*outShaderID, kErrorCompileVertexShader, text);
+}
+
+
+bool CompileGLSLFragmentShader (const std::string& source, GLShaderID* outShaderID)
+{
+ *outShaderID = glCreateShader(GL_FRAGMENT_SHADER);
+
+ ///@TODO: find any existing precision statement
+
+ std::string modSourceHeader, modSourceProg;
+ FindProgramStart(source.c_str(), &modSourceHeader, &modSourceProg);
+
+ std::string prec_modifier = gGraphicsCaps.gles20.forceHighpFSPrec ? "precision highp float;\n" : "precision mediump float;\n";
+
+ std::string modSource = modSourceHeader + prec_modifier + modSourceProg;
+
+ const char* text = modSource.c_str();
+ DBG_SHADER_VERBOSE_GLES20_DUMP_SHADER("Compiling FRAGMENT program:", text);
+
+ return CompileGlslShader(*outShaderID, kErrorCompileFragShader, text);
+}
+
+int GlslGpuProgramGLES20::GetGLProgram (FogMode fog, GpuProgramParameters& outParams, ChannelAssigns &channels)
+{
+ int index = 0;
+ if (fog > kFogDisabled && !m_FogFailed[fog] && !m_SourceForFog.empty())
+ {
+ index = fog;
+ Assert (index >= 0 && index < kFogModeCount);
+
+ // create patched fog program if needed
+ if(!m_Programs[index])
+ {
+ std::string srcVS = m_VertexShaderSourceForFog;
+ std::string srcPS = m_SourceForFog;
+
+ if(PatchShaderFogGLES (srcVS, srcPS, fog, CanUseOptimizedFogCodeGLES(srcVS)))
+ {
+ // create program, shaders, link
+ GLES_CHK(m_Programs[index] = glCreateProgram());
+ ShaderErrors errors;
+ if(CompileProgram(index, srcVS, srcPS, channels))
+ {
+ FillParams (m_Programs[index], outParams, NULL);
+ m_FogParamsIndex[index] = glGetUniformLocation(m_Programs[index], "_unity_FogParams");
+ m_FogColorIndex[index] = glGetUniformLocation(m_Programs[index], "_unity_FogColor");
+
+ m_UniformCache[index].Create(&outParams, m_FogParamsIndex[index], m_FogColorIndex[index]);
+ }
+ else
+ {
+ if(m_GLSLVertexShader[index])
+ glDeleteShader(m_GLSLVertexShader[index]);
+
+ if(m_GLSLFragmentShader[index])
+ glDeleteShader(m_GLSLFragmentShader[index]);
+
+ m_GLSLVertexShader[index] = 0;
+ m_GLSLFragmentShader[index] = 0;
+
+ glDeleteProgram(m_Programs[index]);
+ m_Programs[index] = 0;
+
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ else
+ {
+ m_FogFailed[index] = true;
+ index = 0;
+ }
+ }
+ }
+ return index;
+}
+
+// chai: 在BeforDrawCall调用
+int GlslGpuProgramGLES20::ApplyGpuProgramES20 (const GpuProgramParameters& params, const UInt8 *buffer)
+{
+ DBG_LOG_GLES20("GlslGpuProgramGLES20::ApplyGpuProgramES20()");
+
+ // m_Programs[0] == 0, is when Unity tries to build a dummy shader with empty source for fragment shaders, do nothing in this case
+ if (m_Programs[0] == 0)
+ return 0;
+
+ GfxDevice& device = GetRealGfxDevice();
+
+ const GfxFogParams& fog = device.GetFogParams();
+ const int index = (int)fog.mode;
+
+ DBG_LOG_GLES20("GLSL: apply program id=%i\n", m_Programs[index]);
+
+ //chai: glUseProgram
+ GLSLUseProgramGLES20 (m_Programs[index]);
+
+ // Apply value parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if (i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ UniformCacheGLES20* cache = m_UniformCache+index;
+ const float * val = reinterpret_cast<const float*>(buffer);
+
+ switch (i->m_ColCount)
+ {
+ case 1: CachedUniform1(cache, i->m_Type, i->m_Index, val); break;
+ case 2: CachedUniform2(cache, i->m_Type, i->m_Index, val); break;
+ case 3: CachedUniform3(cache, i->m_Type, i->m_Index, val); break;
+ case 4: CachedUniform4(cache, i->m_Type, i->m_Index, val); break;
+ default: break;
+ }
+
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" vector %i dim=%i\n", i->m_Index, i->m_Dim );
+ #endif
+ buffer += 4*sizeof(float);
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert (i->m_ArraySize == 1);
+ int size = *reinterpret_cast<const int*>(buffer); buffer += sizeof(int);
+ Assert (size == 16);
+ const Matrix4x4f* mat = reinterpret_cast<const Matrix4x4f*>(buffer);
+ if (i->m_RowCount == 3 && i->m_ColCount == 3)
+ {
+ Matrix3x3f m33 = Matrix3x3f(*mat);
+ GLES_CHK(glUniformMatrix3fv (i->m_Index, 1, GL_FALSE, m33.GetPtr()));
+ }
+ else
+ {
+ const float *ptr = mat->GetPtr ();
+ GLES_CHK(glUniformMatrix4fv (i->m_Index, 1, GL_FALSE, ptr));
+ }
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" matrix %i (%s)\n", i->m_Index, i->m_Name.GetName() );
+ #endif
+ buffer += size * sizeof(float);
+ }
+ }
+
+ // Apply textures
+ const GpuProgramParameters::TextureParameterList& textureParams = params.GetTextureParams();
+ GpuProgramParameters::TextureParameterList::const_iterator textureParamsEnd = textureParams.end();
+ for( GpuProgramParameters::TextureParameterList::const_iterator i = textureParams.begin(); i != textureParamsEnd; ++i )
+ {
+ const GpuProgramParameters::TextureParameter& t = *i;
+ const TexEnvData* texdata = reinterpret_cast<const TexEnvData*>(buffer);
+ #if DEBUG_GLSL_BINDINGS
+ ;;printf_console(" sampler %i (%s) id=%i dim=%i\n", t.m_Index, t.m_Name.GetName(), tex->GetActualTextureID().m_ID, tex->GetTexDim() );
+ #endif
+ ApplyTexEnvData (t.m_Index, t.m_Index, *texdata);
+ buffer += sizeof(TexEnvData);
+ }
+
+ // Fog parameters if needed
+ if (index > 0)
+ {
+ if (m_FogColorIndex[fog.mode] >= 0)
+ CachedUniform4(m_UniformCache+index, kShaderParamFloat, m_FogColorIndex[fog.mode], fog.color.GetPtr());
+
+ Vector4f params(
+ fog.density * 1.2011224087f,// density / sqrt(ln(2))
+ fog.density * 1.4426950408f, // density / ln(2)
+ 0.0f,
+ 0.0f
+ );
+
+ if (fog.mode == kFogLinear)
+ {
+ float diff = fog.end - fog.start;
+ float invDiff = Abs(diff) > 0.0001f ? 1.0f/diff : 0.0f;
+ params[2] = -invDiff;
+ params[3] = fog.end * invDiff;
+ }
+
+ if (m_FogParamsIndex[fog.mode] >= 0)
+ CachedUniform4(m_UniformCache+index, kShaderParamFloat, m_FogParamsIndex[fog.mode], params.GetPtr());
+ }
+
+ GLESAssert();
+
+ return index;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+FixedFunctionProgramGLES20::FixedFunctionProgramGLES20(GLShaderID vertexShader, GLShaderID fragmentShader)
+: m_GLSLProgram(0)
+, m_GLSLVertexShader(vertexShader)
+, m_GLSLFragmentShader(fragmentShader)
+{
+ m_GLSLProgram = Create(m_GLSLVertexShader, m_GLSLFragmentShader);
+}
+
+FixedFunctionProgramGLES20::~FixedFunctionProgramGLES20 ()
+{
+ // NOTE: do not delete vertex/fragment shaders; they can be shared between multiple programs
+ // and are deleted in ClearFixedFunctionPrograms
+ if (m_GLSLProgram != 0 ) // only delete valid programs
+ GLES_CHK(glDeleteProgram(m_GLSLProgram));
+
+ m_UniformCache.Destroy();
+}
+
+GLShaderID FixedFunctionProgramGLES20::Create(GLShaderID vertexShader, GLShaderID fragmentShader)
+{
+ if( !gGraphicsCaps.gles20.hasGLSL )
+ return false;
+
+ GLESAssert(); // Clear any GL errors
+
+ GLuint program;
+ GLES_CHK(program = glCreateProgram());
+
+ for (int i = 0; i < kAttribLookupTableSize; i++)
+ {
+ if(s_GLESVertexComponents[i] < gGraphicsCaps.gles20.maxAttributes)
+ GLES_CHK(glBindAttribLocation(program, s_GLESVertexComponents[i], s_GLSLESAttributes[i]));
+ }
+
+ GLES_CHK(glAttachShader(program, m_GLSLVertexShader));
+ GLES_CHK(glAttachShader(program, m_GLSLFragmentShader));
+
+ //We must link only after binding the attributes
+ GLES_CHK(glLinkProgram(program));
+
+ int linked = 0;
+ GLES_CHK(glGetProgramiv(program, GL_LINK_STATUS, &linked));
+ if (linked == 0)
+ {
+ ParseGlslErrors(program, kErrorLinkProgram);
+ GLES_CHK(glDeleteProgram(program));
+ return 0;
+ }
+
+ // Figure out the uniforms
+ int activeUniforms;
+
+ char name[1024];
+ GLenum type;
+ int size = 0,
+ length = 0,
+ bufSize = sizeof(name);
+
+ // Fetch texture stage samplers. Bind GLSL uniform to the OpenGL texture unit
+ // just once, cause it never changes after that for this program; and could cause
+ // internal shader recompiles when changing them.
+ DBG_LOG_GLES20("GLSL: apply fixed-function program id=%i\n", program);
+ GLSLUseProgramGLES20 (program);
+
+ for (int i = 0; i < kMaxSupportedTextureUnitsGLES; i++)
+ {
+ std::string samplerName = Format("u_sampler%d", i);
+ GLint uniformNumber = glGetUniformLocation(program, samplerName.c_str());
+
+ if (uniformNumber != -1)
+ {
+ GLES_CHK(glUniform1i (uniformNumber, i));
+ DBG_GLSL_BINDINGS_GLES20(" FFsampler: %s nr=%d", samplerName.c_str(), uniformNumber);
+ }
+ }
+
+ // fetch generic params
+ GLES_CHK(glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms));
+ for(int i=0; i < activeUniforms; i++)
+ {
+ GLES_CHK(glGetActiveUniform(program, i, bufSize, &length, &size, &type, name));
+ int uniformNumber = glGetUniformLocation(program, name);
+
+ const char* glslName = GetGLSLESPropertyNameRemap(name);
+ const char* unityName = glslName ? glslName : name;
+
+ if(type == GL_FLOAT) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 1, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_VEC2) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 2, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_VEC3) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 3, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_VEC4) {
+ m_Params.AddVectorParam(uniformNumber, kShaderParamFloat, 4, unityName, -1, NULL);
+ }
+ else if(type == GL_FLOAT_MAT4) {
+ m_Params.AddMatrixParam(uniformNumber, unityName, 4, 4, -1, NULL);
+ }
+ else if(type == GL_FLOAT_MAT3) {
+ m_Params.AddMatrixParam(uniformNumber, unityName, 3, 3, -1, NULL);
+ }
+ else if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE) {
+ }
+ else {
+ AssertString( "Unrecognized GLSL uniform type" );
+ }
+ DBG_GLSL_BINDINGS_GLES20(" FFuniform: %s nr=%d type=%d", unityName, uniformNumber, type);
+ }
+
+ m_UniformCache.Create(&m_Params, -1,-1);
+
+ GLESAssert();
+ return program;
+}
+
+void FixedFunctionProgramGLES20::ApplyFFGpuProgram(const BuiltinShaderParamValues& values) const
+{
+ DBG_LOG_GLES20("FixedFunctionProgramGLES20::ApplyFFGpuProgram()");
+
+ DBG_LOG_GLES20("GLSL: apply fixed-function program id=%i\n", m_GLSLProgram);
+ GLSLUseProgramGLES20 (m_GLSLProgram);
+
+ // Apply float/vector parameters
+ const GpuProgramParameters::ValueParameterArray& valueParams = m_Params.GetValueParams();
+ GpuProgramParameters::ValueParameterArray::const_iterator valueParamsEnd = valueParams.end();
+ for( GpuProgramParameters::ValueParameterArray::const_iterator i = valueParams.begin(); i != valueParamsEnd; ++i )
+ {
+ if(i->m_RowCount == 1 && i->m_ArraySize == 1)
+ {
+ UniformCacheGLES20* cache = &m_UniformCache;
+ const float * val = values.GetVectorParam((BuiltinShaderVectorParam)i->m_Name.BuiltinIndex()).GetPtr();
+ switch (i->m_ColCount)
+ {
+ case 1: CachedUniform1(cache, kShaderParamFloat, i->m_Index, val); break;
+ case 2: CachedUniform2(cache, kShaderParamFloat, i->m_Index, val); break;
+ case 3: CachedUniform3(cache, kShaderParamFloat, i->m_Index, val); break;
+ case 4: CachedUniform4(cache, kShaderParamFloat, i->m_Index, val); break;
+ default: break;
+ }
+ }
+ else
+ {
+ // Apply matrix parameters
+ DebugAssert (i->m_ArraySize == 1);
+ const Matrix4x4f& mat = values.GetMatrixParam((BuiltinShaderMatrixParam)i->m_Name.BuiltinIndex());
+ if (i->m_RowCount == 3 && i->m_ColCount == 3)
+ {
+ Matrix3x3f m33 = Matrix3x3f(mat);
+ GLES_CHK(glUniformMatrix3fv (i->m_Index, 1, GL_FALSE, m33.GetPtr()));
+ }
+ else
+ {
+ const float *ptr = mat.GetPtr ();
+ GLES_CHK(glUniformMatrix4fv (i->m_Index, 1, GL_FALSE, ptr));
+ }
+ DBG_GLSL_BINDINGS_GLES20(" FFmatrix %i (%s)", i->m_Index, i->m_Name.GetName() );
+ }
+ }
+
+ GLESAssert();
+}
+
+
+#if UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN
+//-----------------------------------------------------------------------------
+// md5 internals are extracted from External/MurmurHash/md5.cpp
+ struct
+ md5_context
+ {
+ unsigned long total[2];
+ unsigned long state[4];
+ unsigned char buffer[64];
+ unsigned char ipad[64];
+ unsigned char opad[64];
+ };
+
+ extern void md5_starts(md5_context* ctx);
+ extern void md5_update(md5_context* ctx, unsigned char* input, int ilen);
+ extern void md5_finish(md5_context* ctx, unsigned char output[16]);
+#endif
+
+
+static void GetCachedBinaryName(const std::string& vprog, const std::string& fshader, char filename[33])
+{
+// we have caching only on android, so no need to worry (at least for now) about md5
+#if UNITY_ANDROID || UNITY_BLACKBERRY || UNITY_TIZEN
+
+ unsigned char hash[16] = {0};
+
+ md5_context ctx;
+ md5_starts(&ctx);
+ md5_update(&ctx, (unsigned char*)vprog.c_str(), vprog.length());
+ md5_update(&ctx, (unsigned char*)fshader.c_str(), fshader.length());
+ md5_finish(&ctx, hash);
+
+ BytesToHexString(hash, 16, filename);
+
+#else
+
+ (void)vprog;
+ (void)fshader;
+ ::memset(filename, '1', 33);
+
+#endif
+}
+
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h
new file mode 100644
index 0000000..a9a14d2
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20.h
@@ -0,0 +1,78 @@
+#ifndef GPUPROGRAMSGLES20_H
+#define GPUPROGRAMSGLES20_H
+
+
+#if !GFX_SUPPORTS_OPENGLES20
+#error "Should not include GpuProgramsGLES20 on this platform"
+#endif
+
+
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Utilities/GLSLUtilities.h"
+#include "IncludesGLES20.h"
+#include "GpuProgramsGLES20_UniformCache.h"
+
+class GlslGpuProgramGLES20 : public GpuProgramGL
+{
+public:
+ GlslGpuProgramGLES20 (const std::string& source, CreateGpuProgramOutput& output);
+ ~GlslGpuProgramGLES20();
+
+ virtual void ApplyGpuProgram (const GpuProgramParameters& params, const UInt8* buffer) { Assert(!"Should not be used"); }
+
+ // Returns the permutation index used
+ int ApplyGpuProgramES20 (const GpuProgramParameters& params, const UInt8 *buffer);
+ int GetGLProgram (FogMode fog, GpuProgramParameters& outParams, ChannelAssigns& channels);
+
+ static bool InitBinaryShadersSupport();
+
+
+ UniformCacheGLES20 m_UniformCache[kFogModeCount];
+
+private:
+ static void FillParams (unsigned int programID, GpuProgramParameters& params, PropertyNamesSet* outNames);
+
+ bool Create (const std::string& source, ChannelAssigns& channels);
+
+ bool CompileProgram(unsigned index, const std::string& vprog, const std::string& fshader, ChannelAssigns& channels);
+
+private:
+ std::string m_VertexShaderSourceForFog;
+ GLShaderID m_GLSLVertexShader[kFogModeCount];
+ GLShaderID m_GLSLFragmentShader[kFogModeCount];
+ int m_FogColorIndex[kFogModeCount];
+ int m_FogParamsIndex[kFogModeCount];
+ bool m_FogFailed[kFogModeCount];
+
+ static std::string _CachePath;
+ static glGetProgramBinaryOESFunc _glGetProgramBinaryOES;
+ static glProgramBinaryOESFunc _glProgramBinaryOES;
+};
+
+
+class FixedFunctionProgramGLES20
+{
+public:
+ FixedFunctionProgramGLES20(GLShaderID vertexShader, GLShaderID fragmentShader);
+ ~FixedFunctionProgramGLES20();
+
+ void ApplyFFGpuProgram(const BuiltinShaderParamValues& values) const;
+ const BuiltinShaderParamIndices& GetBuiltinParams() const { return m_Params.GetBuiltinParams(); }
+
+ mutable UniformCacheGLES20 m_UniformCache;
+
+protected:
+ GLShaderID Create(GLShaderID vertexShader, GLShaderID fragmentShader);
+
+private:
+ GLShaderID m_GLSLProgram;
+ GLShaderID m_GLSLVertexShader, m_GLSLFragmentShader;
+
+ GpuProgramParameters m_Params;
+};
+
+bool CompileGlslShader(GLShaderID shader, GLSLErrorType type, const char* source);
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp
new file mode 100644
index 0000000..db725d2
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.cpp
@@ -0,0 +1,72 @@
+#include "UnityPrefix.h"
+#if !GFX_SUPPORTS_OPENGLES20
+#error "Should not include GpuProgramsGLES20 on this platform"
+#endif
+
+#include "GpuProgramsGLES20_UniformCache.h"
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/GfxDevice/GpuProgram.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+
+
+void UniformCacheGLES20::Create(const GpuProgramParameters* params, int fogParamsIndex, int fogColorIndex)
+{
+ int lastUsedUniform = -1;
+
+ // we will track only float/vector uniforms
+ GpuProgramParameters::ValueParameterArray::const_iterator paramI = params->GetValueParams().begin();
+ GpuProgramParameters::ValueParameterArray::const_iterator paramEnd = params->GetValueParams().end();
+ while(paramI != paramEnd)
+ {
+ if(paramI->m_RowCount == 1 && paramI->m_ArraySize == 1 && paramI->m_Index > lastUsedUniform)
+ lastUsedUniform = paramI->m_Index;
+
+ ++paramI;
+ }
+
+ const BuiltinShaderParamIndices& builtinParam = params->GetBuiltinParams();
+ for(unsigned i = 0 ; i < kShaderInstanceVecCount ; ++i)
+ {
+ if(builtinParam.vec[i].gpuIndex > lastUsedUniform)
+ lastUsedUniform = builtinParam.vec[i].gpuIndex;
+ }
+
+ if(fogParamsIndex > lastUsedUniform) lastUsedUniform = fogParamsIndex;
+ if(fogColorIndex > lastUsedUniform) lastUsedUniform = fogColorIndex;
+
+ count = lastUsedUniform + 1;
+ uniform = (float*)UNITY_MALLOC_ALIGNED(kMemShader, count*4 * sizeof(float), 16);
+ memset(uniform, 0xff /* NaN */, count*4 * sizeof(float));
+}
+
+void UniformCacheGLES20::Destroy()
+{
+ count = 0;
+
+ UNITY_FREE(kMemShader, uniform);
+ uniform = 0;
+}
+
+
+// In theory Uniform*f can also be used to load bool uniforms, in practice
+// some drivers don't like that (e.g. PVR GLES2.0 Emu wants bools to be loaded
+// via Uniform*i). So load both integers and bools via *i functions.
+#define CACHED_UNIFORM_IMPL(Count) \
+void CachedUniform##Count(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val) \
+{ \
+ if (cache->UpdateUniform(index, val, Count)) { \
+ if (type == kShaderParamFloat) \
+ GLES_CHK(glUniform##Count##fv(index, 1, val)); \
+ else { \
+ int ival[4] = {val[0],val[1],val[2],val[3]}; \
+ GLES_CHK(glUniform##Count##iv(index, 1, ival)); \
+ } \
+ } \
+} \
+
+CACHED_UNIFORM_IMPL(1);
+CACHED_UNIFORM_IMPL(2);
+CACHED_UNIFORM_IMPL(3);
+CACHED_UNIFORM_IMPL(4);
diff --git a/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h
new file mode 100644
index 0000000..6f01fc4
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuProgramsGLES20_UniformCache.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#if !GFX_SUPPORTS_OPENGLES20
+#error "Should not include GpuProgramsGLES20 on this platform"
+#endif
+
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include <string.h>
+
+class GpuProgramParameters;
+
+struct
+UniformCacheGLES20
+{
+ // for gles we must set values per-uniform (not per-registers like in dx)
+ // so there is no real need for dirty tracking.
+ // TODO: do unified impl with dirty tracking if/when we do "everything is an array" in gles
+ float* uniform;
+ unsigned count;
+
+ UniformCacheGLES20() : uniform(0), count(0) {}
+
+ // we will pre-alloc memory. Fog params are handled differently (not added to gpu params).
+ // TODO: make it more general, int* perhaps, or some struct
+ void Create(const GpuProgramParameters* params, int fogParamsIndex, int fogColorIndex);
+ void Destroy();
+
+ // returns true if you need to update for real
+ bool UpdateUniform(int index, const float* val, unsigned floatCount);
+};
+
+void CachedUniform1(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+void CachedUniform2(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+void CachedUniform3(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+void CachedUniform4(UniformCacheGLES20* cache, ShaderParamType type, int index, const float* val);
+
+
+inline bool UniformCacheGLES20::UpdateUniform(int index, const float* val, unsigned floatCount)
+{
+ Assert(index < count);
+ const unsigned mem_sz = floatCount*sizeof(float);
+
+ float* target = uniform + 4*index;
+ if(::memcmp(target, val, mem_sz))
+ {
+ ::memcpy(target, val, mem_sz);
+ return true;
+ }
+ return false;
+}
diff --git a/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp
new file mode 100644
index 0000000..0d808a1
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.cpp
@@ -0,0 +1,70 @@
+#include "UnityPrefix.h"
+#include "GpuPropertiesGLES20.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/BuiltinShaderParamsNames.h"
+
+#if GFX_SUPPORTS_OPENGLES20
+
+struct GLSLESProperty
+{
+ GLSLESProperty(const char* _glName, const char* _glesName) : glName(_glName), unityName(_glesName) { }
+ const char* glName;
+ const char* unityName;
+};
+
+#define DEF_MAT_INTERNAL(name, builtin) GLSLESProperty(name, GetShaderInstanceMatrixParamName(builtin))
+#define DEF_MAT_BUILTIN(name, builtin) GLSLESProperty(name, GetBuiltinMatrixParamName(builtin))
+#define BIND_VEC_BUILTIN(name, builtin) GLSLESProperty(name, GetBuiltinVectorParamName(builtin))
+
+
+static const GLSLESProperty kglslesProperties[] =
+{
+ DEF_MAT_BUILTIN("gl_ProjectionMatrix", kShaderMatProj),
+ DEF_MAT_INTERNAL("gl_NormalMatrix", kShaderInstanceMatNormalMatrix),
+ DEF_MAT_INTERNAL("gl_ModelViewProjectionMatrix", kShaderInstanceMatMVP),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrixTranspose", kShaderInstanceMatTransMV),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrixInverseTranspose", kShaderInstanceMatInvTransMV),
+ DEF_MAT_INTERNAL("gl_ModelViewMatrix", kShaderInstanceMatMV),
+
+ DEF_MAT_INTERNAL("gl_TextureMatrix0", kShaderInstanceMatTexture0),
+ DEF_MAT_INTERNAL("gl_TextureMatrix1", kShaderInstanceMatTexture1),
+ DEF_MAT_INTERNAL("gl_TextureMatrix2", kShaderInstanceMatTexture2),
+ DEF_MAT_INTERNAL("gl_TextureMatrix3", kShaderInstanceMatTexture3),
+ DEF_MAT_INTERNAL("gl_TextureMatrix4", kShaderInstanceMatTexture4),
+ DEF_MAT_INTERNAL("gl_TextureMatrix5", kShaderInstanceMatTexture5),
+ DEF_MAT_INTERNAL("gl_TextureMatrix6", kShaderInstanceMatTexture6),
+ DEF_MAT_INTERNAL("gl_TextureMatrix7", kShaderInstanceMatTexture7),
+
+ BIND_VEC_BUILTIN("_glesLightSource[0].diffuse", kShaderVecLight0Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[1].diffuse", kShaderVecLight1Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[2].diffuse", kShaderVecLight2Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[3].diffuse", kShaderVecLight3Diffuse),
+ BIND_VEC_BUILTIN("_glesLightSource[0].position", kShaderVecLight0Position),
+ BIND_VEC_BUILTIN("_glesLightSource[1].position", kShaderVecLight1Position),
+ BIND_VEC_BUILTIN("_glesLightSource[2].position", kShaderVecLight2Position),
+ BIND_VEC_BUILTIN("_glesLightSource[3].position", kShaderVecLight3Position),
+ BIND_VEC_BUILTIN("_glesLightSource[0].spotDirection", kShaderVecLight0SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[1].spotDirection", kShaderVecLight1SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[2].spotDirection", kShaderVecLight2SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[3].spotDirection", kShaderVecLight3SpotDirection),
+ BIND_VEC_BUILTIN("_glesLightSource[0].atten", kShaderVecLight0Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[1].atten", kShaderVecLight1Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[2].atten", kShaderVecLight2Atten),
+ BIND_VEC_BUILTIN("_glesLightSource[3].atten", kShaderVecLight3Atten),
+ BIND_VEC_BUILTIN("_glesLightModel.ambient", kShaderVecLightModelAmbient),
+};
+
+
+const char* GetGLSLESPropertyNameRemap (const char* name)
+{
+ for (int i = 0; i < ARRAY_SIZE(kglslesProperties); i++)
+ {
+ const GLSLESProperty& prop = kglslesProperties[i];
+ if (strcmp(name, prop.glName) == 0)
+ return prop.unityName;
+ }
+ return NULL;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h
new file mode 100644
index 0000000..9caae9a
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/GpuPropertiesGLES20.h
@@ -0,0 +1,3 @@
+#pragma once
+
+const char* GetGLSLESPropertyNameRemap (const char* name);
diff --git a/Runtime/GfxDevice/opengles20/IncludesGLES20.h b/Runtime/GfxDevice/opengles20/IncludesGLES20.h
new file mode 100644
index 0000000..260bea5
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/IncludesGLES20.h
@@ -0,0 +1 @@
+#include "Runtime/GfxDevice/opengles/IncludesGLES.h" \ No newline at end of file
diff --git a/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
new file mode 100644
index 0000000..d397c6e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.cpp
@@ -0,0 +1,958 @@
+#include "UnityPrefix.h"
+#include "RenderTextureGLES20.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/RenderSurface.h"
+#include "Runtime/Graphics/RenderTexture.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "DebugGLES20.h"
+#include "TextureIdMapGLES20.h"
+#include "UnityGLES20Ext.h"
+
+// seems like code is the best place to leave TODO:
+// GetCurrentFBImpl - it is called in random places - should just put somehwere (begin frame?)
+// discard logic - both color/depth have flags, but only color is used, also - mrt case
+
+
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+
+#if 1
+ #define DBG_LOG_RT_GLES20(...) {}
+#else
+ #define DBG_LOG_RT_GLES20(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+
+#if GFX_SUPPORTS_OPENGLES20
+
+#define GL_RT_COMMON_GLES2 1
+#include "Runtime/GfxDevice/GLRTCommon.h"
+#undef GL_RT_COMMON_GLES2
+
+#if UNITY_IPHONE
+ extern "C" bool UnityDefaultFBOHasMSAA();
+ extern "C" void* UnityDefaultFBOColorBuffer();
+#endif
+
+
+struct RenderColorSurfaceGLES2 : public RenderSurfaceBase
+{
+ GLuint m_ColorBuffer;
+ RenderTextureFormat format;
+ TextureDimension dim;
+};
+
+struct RenderDepthSurfaceGLES2 : public RenderSurfaceBase
+{
+ GLuint m_DepthBuffer;
+ DepthBufferFormat depthFormat;
+ bool depthWithStencil;
+};
+
+extern GLint gDefaultFBO;
+static bool gDefaultFboInited = false;
+
+static const unsigned long kOpenGLESTextureDimensionTable[kTexDimCount] = {0, 0, GL_TEXTURE_2D, 0, GL_TEXTURE_CUBE_MAP, 0};
+
+static RenderColorSurfaceGLES2* s_ActiveColorTarget[kMaxSupportedRenderTargets] = {0};
+static int s_ActiveColorTargetCount = 0;
+static int s_ActiveMip = 0;
+static CubemapFace s_ActiveFace = kCubeFaceUnknown;
+static RenderDepthSurfaceGLES2* s_ActiveDepthTarget = 0;
+
+static RenderColorSurfaceGLES2 s_BackBufferColor;
+static RenderDepthSurfaceGLES2 s_BackBufferDepth;
+
+
+// at least on ios when changing ext of attachments there is huge perf penalty
+// so do fbo per rt-ext
+
+typedef std::pair<unsigned, unsigned> FBKey;
+typedef std::map<FBKey, GLuint> FBMap;
+
+static FBMap _FBMap;
+static GLuint GetFBFromAttachments(RenderColorSurfaceGLES2* color, RenderDepthSurfaceGLES2* depth)
+{
+ // while it may seems bad that we freely mix rb/texture - we do the distinction per-gpu/per-platform mostly
+ // so it is actually ok
+ unsigned ckey = color->textureID.m_ID ? color->textureID.m_ID : color->m_ColorBuffer;
+ unsigned dkey = depth->textureID.m_ID ? depth->textureID.m_ID : depth->m_DepthBuffer;
+ FBKey key = std::make_pair(ckey, dkey);
+
+
+ FBMap::iterator fbi = _FBMap.find(key);
+ if(fbi == _FBMap.end())
+ {
+ GLuint fb=0;
+ GLES_CHK(glGenFramebuffers(1, &fb));
+
+ fbi = _FBMap.insert(std::make_pair(key, fb)).first;
+ }
+
+ Assert(fbi != _FBMap.end());
+ return fbi->second;
+}
+
+// this should be called on context loss
+// TODO: need we care about the case of actually deleting FBOs
+void ClearFBMapping()
+{
+ _FBMap.clear();
+}
+
+// this is not the best way, but unless we have proper rt->fbo mapping let's live with that
+// the reason is to avoid leaks when we destroy rb/tex that is still attached to some FBO
+static void OnFBOAttachmentDelete(RenderSurfaceBase* rs)
+{
+ unsigned key = rs->textureID.m_ID;
+ if(key == 0)
+ key = rs->colorSurface ? ((RenderColorSurfaceGLES2*)rs)->m_ColorBuffer : ((RenderDepthSurfaceGLES2*)rs)->m_DepthBuffer;
+
+ int curFB = GetCurrentFBGLES2();
+
+ for(FBMap::iterator i = _FBMap.begin() ; i != _FBMap.end() ; )
+ {
+ if(i->first.first == key || i->first.second == key)
+ {
+ // make sure we also drop attachments before killing FBO (some drivers may leak otherwise)
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, i->second));
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, curFB));
+
+ GLES_CHK(glDeleteFramebuffers(1, &i->second));
+ FBMap::iterator rem = i;
+ ++i;
+ _FBMap.erase(rem);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+}
+
+static const char* GetFBOStatusError( GLenum status )
+{
+ Assert( status != GL_FRAMEBUFFER_COMPLETE ); // should not be called when everything is ok...
+ switch( status )
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return "INCOMPLETE_DIMENSIONS";
+ case GL_FRAMEBUFFER_UNSUPPORTED: return "UNSUPPORTED";
+ default: return "unknown error";
+ }
+}
+static const char* GetFBOAttachementType(GLint type)
+{
+ switch (type)
+ {
+ case GL_RENDERBUFFER: return "GL_RENDERBUFFER";
+ case GL_TEXTURE: return "GL_TEXTURE";
+ default: return "GL_NONE";
+ }
+}
+
+static int GetCurrentFBImpl()
+{
+ GLint curFB;
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curFB));
+
+ return (int)curFB;
+}
+
+int GetCurrentFBGLES2()
+{
+ return GetCurrentFBImpl();
+}
+
+void EnsureDefaultFBInitedGLES2()
+{
+ if(!gDefaultFboInited)
+ {
+ gDefaultFBO = GetCurrentFBImpl();
+ // this will be called from correct thread, so no need to sync or whatever
+ extern void GfxDeviceGLES20_InitFramebufferDepthFormat();
+ GfxDeviceGLES20_InitFramebufferDepthFormat();
+
+ gDefaultFboInited = true;
+ }
+}
+
+void DiscardCurrentFBImpl(bool discardColor, bool discardDepth, GLenum target=GL_FRAMEBUFFER)
+{
+ if(gGraphicsCaps.gles20.hasDiscardFramebuffer)
+ {
+ // TODO: mrt case?
+ GLenum discardUserAttach[] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
+ GLenum discardSystemAttach[] = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT};
+
+ const GLenum* discardTarget = discardUserAttach;
+ if(s_ActiveColorTarget[0]->backBuffer && GetCurrentFBGLES2() == 0)
+ discardTarget = discardSystemAttach;
+
+
+ if(!discardColor) discardTarget += 1;
+
+ unsigned discardCount = 0;
+ if(discardColor) discardCount += 1;
+ if(discardDepth) discardCount += 2;
+
+ if(discardCount)
+ GLES_CHK(gGlesExtFunc.glDiscardFramebufferEXT(GL_FRAMEBUFFER, discardCount, discardTarget));
+ }
+}
+
+void ClearCurrentFBImpl(bool clearColor, bool clearDepth)
+{
+ if(gGraphicsCaps.hasTiledGPU)
+ {
+ UInt32 clearFlags = (clearColor ? kGfxClearColor : 0) | (clearDepth ? kGfxClearDepthStencil : 0);
+ float clearColor[] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ GetRealGfxDevice().Clear(clearFlags, clearColor, 1.0f, 0);
+ }
+}
+
+enum
+{
+ discardDiscardFB = 0,
+ discardClearFB = 1
+};
+
+static void DiscardContentsImpl(int discardPhase)
+{
+ // TODO: mrt?
+ if(s_ActiveColorTarget[0] || s_ActiveDepthTarget)
+ {
+ bool* colorVar = discardPhase == discardDiscardFB ? &s_ActiveColorTarget[0]->shouldDiscard : &s_ActiveColorTarget[0]->shouldClear;
+ bool* depthVar = discardPhase == discardDiscardFB ? &s_ActiveDepthTarget->shouldDiscard : &s_ActiveDepthTarget->shouldClear;
+
+ bool color = *colorVar, depth = *depthVar;
+
+ *colorVar = *depthVar = false;
+
+ if(color || depth)
+ {
+ if(discardPhase == discardDiscardFB) DiscardCurrentFBImpl(color, depth);
+ else ClearCurrentFBImpl(color, depth);
+ }
+ }
+}
+
+
+void DiscardContentsGLES2(RenderSurfaceHandle rs)
+{
+ // TODO: handle bb
+ if(rs.IsValid())
+ {
+ RenderColorSurfaceGLES2* rsgles = reinterpret_cast<RenderColorSurfaceGLES2*>(rs.object);
+ // discard only makes sense for current active rt
+ rsgles->shouldDiscard = gGraphicsCaps.gles20.hasDiscardFramebuffer && rsgles == s_ActiveColorTarget[0];
+ rsgles->shouldClear = gGraphicsCaps.hasTiledGPU;
+ }
+}
+
+bool SetRenderTargetGLES2 (int count, RenderSurfaceHandle* colorHandle, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, GLuint globalSharedFBO)
+{
+ RenderColorSurfaceGLES2* rcolor0 = reinterpret_cast<RenderColorSurfaceGLES2*>( colorHandle[0].object );
+ RenderDepthSurfaceGLES2* rdepth = reinterpret_cast<RenderDepthSurfaceGLES2*>( depthHandle.object );
+
+ // Exit if nothing to do
+ if( s_ActiveColorTargetCount == count && s_ActiveDepthTarget == rdepth && s_ActiveFace == face && s_ActiveMip == mipLevel )
+ {
+ bool colorSame = true;
+ for(int i = 0 ; i < count && colorSame ; ++i)
+ {
+ if(s_ActiveColorTarget[i] != reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object))
+ colorSame = false;
+ }
+ if (colorSame)
+ return false;
+ }
+
+ EnsureDefaultFBInitedGLES2();
+ DiscardContentsImpl(discardDiscardFB);
+
+ AssertIf (!gGraphicsCaps.hasRenderToTexture);
+ GetRealGfxDevice().GetFrameStats().AddRenderTextureChange(); // stats
+
+
+ Assert(colorHandle[0].IsValid() && depthHandle.IsValid());
+ Assert(colorHandle[0].object->backBuffer == depthHandle.object->backBuffer);
+
+
+ GLenum colorAttachStart = GL_COLOR_ATTACHMENT0;
+ if(gGraphicsCaps.gles20.hasNVMRT)
+ colorAttachStart = GL_COLOR_ATTACHMENT0_NV;
+
+ Assert(gGraphicsCaps.gles20.hasNVMRT || count==1);
+
+ if(!rcolor0->backBuffer)
+ {
+ GLuint fb = GetFBFromAttachments(rcolor0, rdepth);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, fb));
+
+ GLenum drawBuffers[kMaxSupportedRenderTargets] = {0};
+ for(int i = 0 ; i < count ; ++i)
+ {
+ // when we start to support EXT variant - add check here
+ GLenum colorAttach = colorAttachStart + i;
+ RenderColorSurfaceGLES2* rcolor = reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object);
+ Assert(rcolor->colorSurface);
+
+ GLuint targetColorTex = (GLuint)TextureIdMap::QueryNativeTexture(rcolor->textureID);
+
+ drawBuffers[i] = colorAttach;
+ if(!IsDepthRTFormat(rcolor->format) && targetColorTex)
+ {
+ if (rcolor->dim == kTexDimCUBE)
+ {
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_CUBE_MAP_POSITIVE_X + clamp<int>(face,0,5), targetColorTex, mipLevel));
+ }
+ else
+ {
+ if(rcolor->samples > 1 && gGraphicsCaps.gles20.hasImgMSAA)
+ GLES_CHK(gGlesExtFunc.glFramebufferTexture2DMultisampleIMG(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_2D, targetColorTex, mipLevel, rcolor->samples));
+ else
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttach, GL_TEXTURE_2D, targetColorTex, mipLevel));
+ }
+ }
+ else
+ {
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, colorAttach, GL_RENDERBUFFER, rcolor->m_ColorBuffer));
+ if(rcolor->m_ColorBuffer == 0)
+ drawBuffers[i] = GL_NONE;
+ }
+ }
+
+ if(gGraphicsCaps.gles20.hasNVMRT)
+ gGlesExtFunc.glDrawBuffersNV(count, drawBuffers);
+
+ GLuint targetDepthTex = (GLuint)TextureIdMap::QueryNativeTexture(rdepth->textureID);
+ bool needAttachStencil = gGraphicsCaps.hasStencil && rdepth->depthWithStencil;
+
+ // depth surface
+ AssertIf (rdepth->colorSurface);
+ if (targetDepthTex
+#if UNITY_PEPPER
+// Workaround for http://code.google.com/p/angleproject/issues/detail?id=211
+ || rdepth->m_DepthBuffer == 0
+#endif
+ )
+ {
+ DBG_LOG_RT_GLES20("glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, %d, 0);", rdepth->textureID.m_ID);
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, targetDepthTex, 0));
+ // A driver might not support attaching depth & stencil as textures here.
+ if (!gGraphicsCaps.hasRenderTargetStencil)
+ needAttachStencil = false;
+ if (needAttachStencil)
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, targetDepthTex, 0));
+ else
+ GLES_CHK(glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0));
+ }
+ else
+ {
+ DBG_LOG_RT_GLES20("glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, %d, 0);", rdepth->m_DepthBuffer);
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rdepth->m_DepthBuffer));
+ if(needAttachStencil)
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rdepth->m_DepthBuffer));
+ else
+ GLES_CHK(glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ }
+
+ GLenum status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ int colorParam, depthParam, stencilParam;
+ int colorValue, depthValue, stencilValue;
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &colorParam);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthParam);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilParam);
+
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &colorValue);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthValue);
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &stencilValue);
+
+ AssertString (Format(
+ "FBO fail: %s\nDetailed description:\n"
+ "GL_COLOR_ATTACHMENT0 Type:%s Value:%d\n"
+ "GL_DEPTH_ATTACHMENT Type:%s Value:%d\n"
+ "GL_STENCIL_ATTACHEMENT Type:%s Value:%d\n",
+ GetFBOStatusError(status),
+ GetFBOAttachementType(colorParam), colorValue,
+ GetFBOAttachementType(depthParam), depthValue,
+ GetFBOAttachementType(stencilParam), stencilValue));
+ }
+ Assert(status == GL_FRAMEBUFFER_COMPLETE);
+ }
+ else
+ {
+ Assert(rcolor0->backBuffer && rdepth->backBuffer); // OpenGL can't mix FBO and native window at once
+ GLES_CHK(glBindFramebuffer( GL_FRAMEBUFFER, gDefaultFBO ));
+ }
+
+ // If we previously had a mip-mapped render texture, generate mip levels for it now.
+ if (s_ActiveColorTarget[0] &&
+ (s_ActiveColorTarget[0]->flags & kSurfaceCreateMipmap) &&
+ (s_ActiveColorTarget[0]->flags & kSurfaceCreateAutoGenMips))
+ {
+ const int textureTarget = kOpenGLESTextureDimensionTable[s_ActiveColorTarget[0]->dim];
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, s_ActiveColorTarget[0]->textureID, s_ActiveColorTarget[0]->dim, std::numeric_limits<float>::infinity());
+ GLES_CHK(glGenerateMipmap(textureTarget));
+ }
+
+ s_ActiveColorTargetCount = count;
+ for(int i = 0 ; i < count ; ++i)
+ s_ActiveColorTarget[i] = reinterpret_cast<RenderColorSurfaceGLES2*>(colorHandle[i].object);
+
+ s_ActiveDepthTarget = rdepth;
+ s_ActiveFace = face;
+ s_ActiveMip = mipLevel;
+
+ DiscardContentsImpl(discardClearFB);
+
+ return true;
+}
+
+// TODO: take index into account here too
+RenderSurfaceHandle GetActiveRenderColorSurfaceGLES2()
+{
+ return RenderSurfaceHandle(s_ActiveColorTarget[0]);
+}
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGLES2()
+{
+ return RenderSurfaceHandle(s_ActiveDepthTarget);
+}
+
+bool IsActiveRenderTargetWithColorGLES2()
+{
+ return !s_ActiveColorTarget[0] || s_ActiveColorTarget[0]->backBuffer || !IsDepthRTFormat (s_ActiveColorTarget[0]->format);
+}
+
+
+static void CreateFBORenderColorSurfaceGLES (RenderColorSurfaceGLES2& rs)
+{
+ int textureTarget = kOpenGLESTextureDimensionTable[rs.dim];
+
+ if( !IsDepthRTFormat(rs.format) )
+ {
+ GLenum internalFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorInternalFormatSRGBGLES2(rs.format) : RTColorInternalFormatGLES2(rs.format);
+ GLenum textureFormat = (rs.flags & kSurfaceCreateSRGB) ? RTColorTextureFormatSRGBGLES2(rs.format) : RTColorTextureFormatGLES2(rs.format);
+ GLenum textureType = RTColorTextureTypeGLES2(rs.format);
+
+ // Create texture to render for color
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, rs.dim, std::numeric_limits<float>::infinity());
+
+ if (rs.dim == kTexDimCUBE)
+ {
+ // cubemap: initialize all faces
+ for( int f = 0; f < 6; ++f )
+ {
+ glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL );
+ }
+ DBG_LOG_RT_GLES20("Creating FBO Color Surface (GL_TEXTURE_CUBE_MAP) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ if (rs.flags & kSurfaceCreateMipmap) { // establish mip map chain if needed
+ GLES_CHK(glGenerateMipmap( textureTarget ));
+ }
+ }
+ // not the best way around, but oh well
+ else if(!rs.textureID.m_ID)
+ {
+ internalFormat = RBColorInternalFormatGLES2(rs.format);
+ GLES_CHK(glGenRenderbuffers(1, &rs.m_ColorBuffer));
+ GLES_CHK(glBindRenderbuffer(GL_RENDERBUFFER, rs.m_ColorBuffer));
+ if(rs.samples > 1 && gGraphicsCaps.gles20.hasAppleMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, rs.samples, internalFormat, rs.width, rs.height));
+ else
+ GLES_CHK(glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rs.width, rs.height));
+ }
+ else
+ {
+ // regular texture: initialize
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ GLES_CHK(glTexImage2D( textureTarget, 0, internalFormat, rs.width, rs.height, 0, textureFormat, textureType, NULL ));
+
+ DBG_LOG_RT_GLES20("Creating FBO Color Surface (GL_TEXTURE_2D) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ if (rs.flags & kSurfaceCreateMipmap) { // establish mip map chain if needed
+ GLES_CHK(glGenerateMipmap( textureTarget ));
+ }
+ }
+ }
+ else
+ {
+ // Note : For GLES2.0Emu and NaCl we must create RenderBuffer even if no color
+ #if UNITY_WIN || UNITY_NACL
+ glGenRenderbuffers( 1, &rs.m_ColorBuffer );
+ glBindRenderbuffer( GL_RENDERBUFFER, rs.m_ColorBuffer );
+ glRenderbufferStorage( GL_RENDERBUFFER, GL_RGBA4, rs.width, rs.height );
+ DBG_LOG_RT_GLES20("Creating dummy color buffer (GL_RENDERBUFFER) [%d] - Width(%d) x Height(%d)", rs.m_ColorBuffer, rs.width, rs.height);
+ #endif
+ }
+}
+
+
+static void CreateFBORenderDepthSurfaceGLES (RenderDepthSurfaceGLES2& rs)
+{
+ int textureTarget = kOpenGLESTextureDimensionTable[kTexDim2D];
+
+ GLenum depthFormat;
+ GLenum genericFormat;
+ GLenum typeFormat;
+
+ // TODO 24bit depth should be revisited
+ if (rs.depthFormat == kDepthFormat24 && gGraphicsCaps.hasStencil)
+ {
+ rs.depthWithStencil = true;
+ depthFormat = GL_DEPTH24_STENCIL8_OES;
+ genericFormat = GL_DEPTH_STENCIL_OES;
+ typeFormat = GL_UNSIGNED_INT_24_8_OES;
+ }
+ else if (rs.depthFormat == kDepthFormat24 && gGraphicsCaps.gles20.has24DepthForFBO)
+ {
+ depthFormat = GL_DEPTH_COMPONENT24_OES;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_BYTE;
+ }
+ else if (gGraphicsCaps.gles20.hasNLZ)
+ {
+ depthFormat = 0x8E2C; // GL_DEPTH_COMPONENT16_NONLINEAR_NV
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_SHORT;
+ }
+ else
+ {
+ depthFormat = GL_DEPTH_COMPONENT16;
+ genericFormat = GL_DEPTH_COMPONENT;
+ typeFormat = GL_UNSIGNED_SHORT;
+ }
+
+ GLuint targetDepthTex = (GLuint)TextureIdMap::QueryNativeTexture(rs.textureID);
+ if (!targetDepthTex)
+ {
+ // Note : For GLES2.0Emu we must create RenderBuffer even if depth format is none
+#if !UNITY_WIN
+ if( rs.depthFormat != kDepthFormatNone )
+#endif
+ {
+ GLES_CHK(glGenRenderbuffers( 1, &rs.m_DepthBuffer ));
+ GLES_CHK(glBindRenderbuffer( GL_RENDERBUFFER, rs.m_DepthBuffer ));
+
+ if(rs.samples > 1)
+ {
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, rs.samples, depthFormat, rs.width, rs.height));
+ else if(gGraphicsCaps.gles20.hasImgMSAA)
+ GLES_CHK(gGlesExtFunc.glRenderbufferStorageMultisampleIMG(GL_RENDERBUFFER, rs.samples, depthFormat, rs.width, rs.height));
+ }
+ else
+ {
+ GLES_CHK(glRenderbufferStorage( GL_RENDERBUFFER, depthFormat, rs.width, rs.height ));
+ }
+
+ DBG_LOG_RT_GLES20("Creating FBO Depth Surface (GL_RENDERBUFFER) [%d] - Width(%d) x Height(%d)", rs.m_DepthBuffer, rs.width, rs.height);
+ }
+ }
+ else
+ {
+ // create depth texture
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs.textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glTexImage2D( textureTarget, 0, genericFormat, rs.width, rs.height, 0, genericFormat, typeFormat, NULL ));
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (rs.flags & kSurfaceCreateShadowmap)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_EXT, GL_LEQUAL);
+ }
+
+ DBG_LOG_RT_GLES20("Creating FBO Depth Surface (GL_TEXTURE_2D) [%d] - Width(%d) x Height(%d)", rs.textureID.m_ID, rs.width, rs.height);
+ }
+}
+
+static RenderColorSurfaceGLES2* CreateRenderColorSurfaceGLES2Impl(void* rs, TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(textureID);
+ Assert(rbID == 0 || targetTex == 0);
+
+ if( !gGraphicsCaps.hasRenderToTexture || !gGraphicsCaps.supportsRenderTextureFormat[format] )
+ return 0;
+
+ RenderColorSurfaceGLES2* ret = rs ? (RenderColorSurfaceGLES2*)rs : new RenderColorSurfaceGLES2;
+ RenderSurfaceBase_InitColor(*ret);
+ ret->width = width;
+ ret->height = height;
+ ret->format = format;
+ ret->dim = dim;
+ ret->flags = createFlags;
+ ret->samples = samples > gGraphicsCaps.gles20.maxSamples ? gGraphicsCaps.gles20.maxSamples : samples;
+
+ ret->textureID = textureID;
+ ret->m_ColorBuffer = rbID;
+
+ if(textureID.m_ID && !rbID)
+ TextureIdMapGLES20_QueryOrCreate(textureID);
+
+ return ret;
+}
+
+RenderSurfaceHandle CreateRenderColorSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples)
+{
+ RenderColorSurfaceGLES2* rs = CreateRenderColorSurfaceGLES2Impl(0, textureID, rbID, width, height, dim, createFlags, format, samples);
+ if(rs)
+ CreateFBORenderColorSurfaceGLES(*rs);
+
+ return RenderSurfaceHandle(rs);
+}
+
+static RenderDepthSurfaceGLES2* CreateRenderDepthSurfaceGLES2Impl(void* rs, TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples)
+{
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(textureID);
+ Assert(rbID == 0 || targetTex == 0);
+
+ if( !gGraphicsCaps.hasRenderToTexture )
+ return 0;
+
+ RenderDepthSurfaceGLES2* ret = rs ? (RenderDepthSurfaceGLES2*)rs : new RenderDepthSurfaceGLES2;
+ RenderSurfaceBase_InitDepth(*ret);
+ ret->depthWithStencil = false;
+ ret->width = width;
+ ret->height = height;
+ ret->depthFormat = depthFormat;
+ ret->flags = createFlags;
+ ret->samples = samples > gGraphicsCaps.gles20.maxSamples ? gGraphicsCaps.gles20.maxSamples : samples;
+
+ ret->textureID = textureID;
+ ret->m_DepthBuffer = rbID;
+
+ if(textureID.m_ID && !rbID)
+ TextureIdMapGLES20_QueryOrCreate(textureID);
+
+ return ret;
+}
+
+
+RenderSurfaceHandle CreateRenderDepthSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples)
+{
+ RenderDepthSurfaceGLES2* rs = CreateRenderDepthSurfaceGLES2Impl(0, textureID, rbID, width, height, createFlags, depthFormat, samples);
+ if(rs)
+ CreateFBORenderDepthSurfaceGLES(*rs);
+
+ return RenderSurfaceHandle(rs);
+}
+
+
+static void InternalDestroyRenderSurfaceGLES (RenderSurfaceBase* rs)
+{
+ AssertIf( !rs );
+
+ if (rs->textureID.m_ID)
+ {
+ GetRealGfxDevice().DeleteTexture( rs->textureID );
+ DBG_LOG_RT_GLES20("Destroying GL_TEXTURE_2D/GL_CUBE_MAP %d", rs->textureID.m_ID);
+ }
+ GLESAssert ();
+
+
+ RenderSurfaceHandle defaultColor = GetRealGfxDevice().GetBackBufferColorSurface();
+ RenderSurfaceHandle defaultDepth = GetRealGfxDevice().GetBackBufferDepthSurface();
+
+
+ for (int i = 0 ; i < s_ActiveColorTargetCount ; ++i)
+ {
+ if (s_ActiveColorTarget[i] == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGLES2 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBO);
+ }
+ }
+ if (s_ActiveDepthTarget == rs)
+ {
+ ErrorString( "RenderTexture warning: Destroying active render texture. Switching to main context." );
+ SetRenderTargetGLES2 (1, &defaultColor, defaultDepth, 0, kCubeFaceUnknown, gDefaultFBO);
+ }
+
+ OnFBOAttachmentDelete(rs);
+
+ if (rs->colorSurface)
+ {
+ RenderColorSurfaceGLES2* rsc = static_cast<RenderColorSurfaceGLES2*>(rs);
+ if( rsc->m_ColorBuffer )
+ {
+ GLES_CHK(glDeleteRenderbuffers( 1, &rsc->m_ColorBuffer ));
+ DBG_LOG_RT_GLES20("Destroying GL_RENDER_BUFFER %d", rsc->m_ColorBuffer);
+ }
+ }
+ else
+ {
+ RenderDepthSurfaceGLES2* rsd = static_cast<RenderDepthSurfaceGLES2*>(rs);
+ if( rsd->m_DepthBuffer )
+ {
+ GLES_CHK(glDeleteRenderbuffers( 1, &rsd->m_DepthBuffer ));
+ DBG_LOG_RT_GLES20("Destroying GL_RENDER_BUFFER %d", rsd->m_DepthBuffer);
+ }
+ }
+}
+
+
+void DestroyRenderSurfaceGLES2 (RenderSurfaceHandle& rsHandle)
+{
+ if(rsHandle.object->backBuffer)
+ return;
+
+ RenderSurfaceBase* rs = rsHandle.object;
+ InternalDestroyRenderSurfaceGLES (rs);
+ delete rs;
+ rsHandle.object = NULL;
+}
+
+void ResolveMSAA(GLuint dstTex, GLuint srcRB, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ GLint oldFBO;
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+
+ GLES_CHK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, globalSharedFBO));
+ GLES_CHK(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTex, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_APPLE, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0));
+ GLES_CHK(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER_APPLE, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0));
+ ClearCurrentFBImpl(true, false);
+
+ GLES_CHK(glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, helperFBO));
+ GLES_CHK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, srcRB));
+ GLES_CHK(gGlesExtFunc.glResolveMultisampleFramebufferAPPLE());
+
+ DiscardCurrentFBImpl(true, false, GL_READ_FRAMEBUFFER_APPLE);
+ GLES_CHK(glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER_APPLE, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0));
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ }
+}
+
+RenderSurfaceBase* ResolveMSAASetupFBO(void* screenRS, int format, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ RenderSurfaceHandle ret;
+
+ RenderColorSurfaceGLES2* screen = (RenderColorSurfaceGLES2*)screenRS;
+ if(gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ TextureID texid = GetRealGfxDevice().CreateTextureID();
+ ret = CreateRenderColorSurfaceGLES2(texid, 0, screen->width, screen->height, kTexDim2D, 0, (RenderTextureFormat)format, 1);
+
+ GLuint gltex = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ ResolveMSAA(gltex, screen->m_ColorBuffer, globalSharedFBO, helperFBO);
+
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, globalSharedFBO));
+ GLES_CHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gltex, 0));
+ }
+
+ return ret.object;
+}
+
+void ResolveMSAASetupFBO_Cleanup(RenderSurfaceBase* rs)
+{
+ RenderSurfaceHandle handle(rs);
+
+ if(handle.IsValid())
+ DestroyRenderSurfaceGLES2(handle);
+}
+
+
+void ResolveColorSurfaceGLES2(RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ // TODO: unify all the places we do resolve
+
+ Assert (srcHandle.IsValid());
+ Assert (dstHandle.IsValid());
+ RenderColorSurfaceGLES2* src = reinterpret_cast<RenderColorSurfaceGLES2*>(srcHandle.object);
+ RenderColorSurfaceGLES2* dst = reinterpret_cast<RenderColorSurfaceGLES2*>(dstHandle.object);
+ if (!src->colorSurface || !dst->colorSurface)
+ {
+ WarningString("RenderTexture: Resolving non-color surfaces.");
+ return;
+ }
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(dst->textureID);
+ if (!src->m_ColorBuffer || !targetTex)
+ {
+ WarningString("RenderTexture: Resolving NULL buffers.");
+ return;
+ }
+
+ ResolveMSAA(targetTex, src->m_ColorBuffer, globalSharedFBO, helperFBO);
+}
+
+void GrabIntoRenderTextureGLES2 (RenderSurfaceHandle rsHandle, RenderSurfaceHandle rdHandle, int x, int y, int width, int height, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ if(!rsHandle.IsValid() || rsHandle.object->backBuffer)
+ return;
+
+ RenderColorSurfaceGLES2* rs = reinterpret_cast<RenderColorSurfaceGLES2*>(rsHandle.object);
+
+#if UNITY_IPHONE
+ GLint oldFBO = 0;
+ RenderSurfaceBase* resolveRS = 0;
+
+ if(IsActiveMSAARenderTargetGLES2() && gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ RenderColorSurfaceGLES2* screen = (RenderColorSurfaceGLES2*)UnityDefaultFBOColorBuffer();
+
+ bool fullScreenResolve = (x==0 && y==0 && width == screen->width && height == screen->height);
+ if(fullScreenResolve)
+ {
+ GLuint texColor = (GLuint)TextureIdMap::QueryNativeTexture(rs->textureID);
+ ResolveMSAA(texColor, screen->m_ColorBuffer, globalSharedFBO, helperFBO);
+
+ return;
+ }
+ else
+ {
+ // intentional fall-through - we setup resolved FBO
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+ resolveRS = ResolveMSAASetupFBO(screen, rs->format, globalSharedFBO, helperFBO);
+ }
+ }
+#endif
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, rs->textureID, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ bool needsReadPixelsFallback = false;
+#if UNITY_WIN
+ // GLES 2.0 Emulator seems to have a bug, in that it can only do glCopyTexSubImage2D for power of two textures.
+ needsReadPixelsFallback = !IsPowerOfTwo(rs->width) || !IsPowerOfTwo(rs->height);
+#endif
+#if UNITY_ANDROID
+ // on pre-honeycomb devices AND in case of EGL_ANDROID_framebuffer_target
+ // we may end up with alphabits=0 explicitely, which, on some devices, cases FB to be considered RGB
+ {
+ GLint rbits=0, gbits=0, bbits=0, abits=0;
+ CHECK(glGetIntegerv(GL_RED_BITS, &rbits));
+ CHECK(glGetIntegerv(GL_GREEN_BITS, &gbits));
+ CHECK(glGetIntegerv(GL_BLUE_BITS, &bbits));
+ CHECK(glGetIntegerv(GL_ALPHA_BITS, &abits));
+
+ if(rbits==8 && gbits==8 && bbits==8 && abits==0)
+ needsReadPixelsFallback = true;
+ }
+#endif
+
+ if (needsReadPixelsFallback)
+ {
+ UInt8* data = NULL;
+ switch (rs->format)
+ {
+ case kRTFormatARGB32:
+ data = new UInt8[width*height*4];
+ GLES_CHK(glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data));
+ GLES_CHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data));
+ break;
+
+ default:
+ ErrorStringMsg ("Unsupported render texture format :%d", rs->format);
+ break;
+ }
+ delete [] data;
+ }
+ else
+ {
+ GLES_CHK(glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, x, y, width, height));
+ }
+
+#if UNITY_IPHONE
+ if(resolveRS)
+ {
+ ResolveMSAASetupFBO_Cleanup(resolveRS);
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ }
+#endif
+}
+
+RenderTextureFormat GetCurrentFBColorFormatGLES20()
+{
+ if( s_ActiveColorTarget[0] )
+ return s_ActiveColorTarget[0]->format;
+
+ return QueryFBColorFormatGLES2();
+}
+
+void InitBackBufferGLES2(RenderSurfaceBase** outColor, RenderSurfaceBase** outDepth)
+{
+ RenderSurfaceBase_InitColor(s_BackBufferColor);
+ s_BackBufferColor.backBuffer = true;
+
+ RenderSurfaceBase_InitDepth(s_BackBufferDepth);
+ s_BackBufferDepth.backBuffer = true;
+
+ *outColor = s_ActiveColorTarget[0] = &s_BackBufferColor;
+ *outDepth = s_ActiveDepthTarget = &s_BackBufferDepth;
+}
+
+void SetBackBufferGLES2()
+{
+ s_ActiveColorTarget[0] = &s_BackBufferColor;
+ s_ActiveColorTargetCount= 0;
+ s_ActiveMip = 0;
+ s_ActiveFace = kCubeFaceUnknown;
+
+ s_ActiveDepthTarget = &s_BackBufferDepth;
+
+ EnsureDefaultFBInitedGLES2();
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, gDefaultFBO));
+}
+
+
+#if UNITY_IPHONE
+bool IsActiveMSAARenderTargetGLES2()
+{
+ return s_ActiveColorTarget[0] && !s_ActiveColorTarget[0]->backBuffer ? s_ActiveColorTarget[0]->samples > 1 : UnityDefaultFBOHasMSAA();
+}
+
+void* UnityCreateUpdateExternalColorSurfaceGLES2(void* surf, unsigned texid, unsigned rbid, int width, int height, bool is32bit)
+{
+ TextureID tex = surf ? ((RenderSurfaceBase*)surf)->textureID : GetUncheckedGfxDevice().CreateTextureID();
+ TextureIdMap::UpdateTexture(tex, texid);
+
+ // TODO for android we will probably need to properly distinct 24/32
+ RenderTextureFormat format = is32bit ? kRTFormatARGB32 : kRTFormatRGB565;
+ return CreateRenderColorSurfaceGLES2Impl(surf, tex, rbid, width, height, kTexDim2D, 0, format, 1);
+}
+void* UnityCreateUpdateExternalDepthSurfaceGLES2(void* surf, unsigned texid, unsigned rbid, int width, int height, bool is24bit)
+{
+ TextureID tex = surf ? ((RenderSurfaceBase*)surf)->textureID : GetUncheckedGfxDevice().CreateTextureID();
+ TextureIdMap::UpdateTexture(tex, texid);
+
+ DepthBufferFormat format = is24bit ? kDepthFormat24 : kDepthFormat16;
+ return CreateRenderDepthSurfaceGLES2Impl(surf, tex, rbid, width, height, 0, format, 1);
+}
+
+void UnityDestroyExternalColorSurfaceGLES2(void* surf)
+{
+ delete (RenderColorSurfaceGLES2*)surf;
+}
+void UnityDestroyExternalDepthSurfaceGLES2(void* surf)
+{
+ delete (RenderDepthSurfaceGLES2*)surf;
+}
+#endif
+
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/RenderTextureGLES20.h b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.h
new file mode 100644
index 0000000..b80a5a6
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/RenderTextureGLES20.h
@@ -0,0 +1,40 @@
+#ifndef UNITY_RENDER_TEXTURES_GLES20_H_
+#define UNITY_RENDER_TEXTURES_GLES20_H_
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "Runtime/GfxDevice/GfxDeviceObjects.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "IncludesGLES20.h"
+
+RenderSurfaceHandle CreateRenderColorSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, TextureDimension dim, UInt32 createFlags, RenderTextureFormat format, int samples);
+RenderSurfaceHandle CreateRenderDepthSurfaceGLES2 (TextureID textureID, unsigned rbID, int width, int height, UInt32 createFlags, DepthBufferFormat depthFormat, int samples);
+void DestroyRenderSurfaceGLES2 (RenderSurfaceHandle& rsHandle);
+bool SetRenderTargetGLES2 (int count, RenderSurfaceHandle* colorHandle, RenderSurfaceHandle depthHandle, int mipLevel, CubemapFace face, unsigned int globalSharedFBO);
+RenderSurfaceHandle GetActiveRenderColorSurfaceGLES2 ();
+RenderSurfaceHandle GetActiveRenderDepthSurfaceGLES2 ();
+void GrabIntoRenderTextureGLES2 (RenderSurfaceHandle rs, RenderSurfaceHandle rd, int x, int y, int width, int height, GLuint globalSharedFBO, GLuint helperFBO);
+void ResolveColorSurfaceGLES2(RenderSurfaceHandle srcHandle, RenderSurfaceHandle dstHandle, GLuint globalSharedFBO, GLuint helperFBO);
+void DiscardContentsGLES2(RenderSurfaceHandle rs);
+
+int GetCurrentFBGLES2();
+void EnsureDefaultFBInitedGLES2();
+
+RenderTextureFormat GetCurrentFBColorFormatGLES20();
+
+void InitBackBufferGLES2(RenderSurfaceBase** color, RenderSurfaceBase** depth);
+void SetBackBufferGLES2();
+
+#if UNITY_IPHONE
+void* UnityCreateExternalColorSurfaceGLES2(unsigned texid, unsigned rbid, int width, int height, bool is32bit);
+void* UnityCreateExternalDepthSurfaceGLES2(unsigned texid, unsigned rbid, int width, int height, bool is24bit);
+void UnityDestroyExternalColorSurfaceGLES2(void* surf);
+void UnityDestroyExternalDepthSurfaceGLES2(void* surf);
+#endif
+
+#if UNITY_IPHONE
+bool IsActiveMSAARenderTargetGLES2();
+#endif
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp
new file mode 100644
index 0000000..c9f4e78
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.cpp
@@ -0,0 +1,749 @@
+#include "UnityPrefix.h"
+#include "FixedFunctionStateGLES20.h"
+#include "ShaderGeneratorGLES20.h"
+#include "IncludesGLES20.h"
+#include "DebugGLES20.h"
+#include "External/shaderlab/Library/TextureBinding.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/BuiltinShaderParams.h"
+#include "Runtime/GfxDevice/BuiltinShaderParamsNames.h"
+#include "Runtime/Utilities/BitUtility.h"
+
+#include <sstream>
+#include <assert.h>
+
+
+#define CMP_STATE(member) { \
+ if (a.member < b.member) \
+ return true; \
+ else if (b.member < a.member) \
+ return false; \
+}
+
+// builtins support only 4 lights
+const int kMaxEmulatedVertexLights = 4;//kMaxSupportedVertexLights;
+
+bool FullStateCompareGLES20::operator() (FixedFunctionStateGLES20 const& a, const FixedFunctionStateGLES20& b) const
+{
+ CMP_STATE(lightingEnabled)
+ CMP_STATE(specularEnabled)
+ CMP_STATE(lightCount)
+ CMP_STATE(onlyDirectionalLights)
+ CMP_STATE(lightType)
+ CMP_STATE(texUnitMatrix)
+ CMP_STATE(useUniformInsteadOfVertexColor)
+ CMP_STATE(useVertexColorAsAmbientAndDiffuse)
+ CMP_STATE(useVertexColorAsEmission)
+
+ CMP_STATE(fogMode)
+
+ CMP_STATE(texUnitCount);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+
+ CMP_STATE(addSpecularAfterTexturing)
+ CMP_STATE(alphaTest)
+ CMP_STATE(setupPointSize)
+
+ return false; /* All equal, not lesser. */
+}
+
+bool VertexStateCompareGLES20::operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const
+{
+ CMP_STATE(lightingEnabled)
+ CMP_STATE(specularEnabled)
+ CMP_STATE(lightCount)
+ CMP_STATE(onlyDirectionalLights)
+ CMP_STATE(lightType)
+ CMP_STATE(texUnitMatrix)
+ CMP_STATE(useUniformInsteadOfVertexColor)
+ CMP_STATE(useVertexColorAsAmbientAndDiffuse)
+ CMP_STATE(useVertexColorAsEmission)
+
+ CMP_STATE(fogMode)
+ CMP_STATE(setupPointSize)
+
+ CMP_STATE(texUnitCount);
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ }
+ return false; /* All equal, not lesser. */
+}
+
+bool FragmentStateCompareGLES20::operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const
+{
+ CMP_STATE(fogMode)
+ CMP_STATE(texUnitCount)
+
+ for (int i = 0; i < a.texUnitCount; i++)
+ {
+ CMP_STATE(texUnitCube[i])
+ CMP_STATE(texUnitGen[i])
+ CMP_STATE(texUnitColorCombiner[i])
+ CMP_STATE(texUnitAlphaCombiner[i])
+ }
+
+ CMP_STATE(addSpecularAfterTexturing)
+ CMP_STATE(alphaTest)
+
+ return false; /* All equal, not lesser. */
+}
+
+
+// --- VERTEX program ----------------------------------------------------------------------------
+
+std::string BuildVertexShaderSourceGLES20 (const FixedFunctionStateGLES20& state)
+{
+ DBG_SHADER_VERBOSE_GLES20("ShaderGeneratorGLES20::BuildVertexShaderSource()\n");
+ DBG_SHADER_VERBOSE_GLES20(" state: %s\n", state.ToString().c_str());
+
+ bool eyePositionRequired = (state.lightingEnabled && state.lightCount > 0 && !state.onlyDirectionalLights);
+ for (int i = 0; i < state.texUnitCount; i++)
+ if (state.texUnitGen[i]==kTexGenCubeReflect)
+ eyePositionRequired = true;
+
+ std::ostringstream src;
+
+ /* Standard uniforms. */
+ src << "uniform highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMVP) << ";\n";
+ if (eyePositionRequired)
+ {
+ src << "uniform highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMV) << ";\n";
+ }
+
+ /* Default attributes. */
+ src << "attribute highp vec4 _glesVertex;\n";
+ src << "attribute mediump vec3 _glesNormal;\n";
+ if (state.useUniformInsteadOfVertexColor)
+ src << "uniform lowp vec4 _glesFFColor;\n";
+ else
+ src << "attribute lowp vec4 _glesColor;\n";
+
+ /* Default varyings. */
+ src << "varying lowp vec4 v_color;\n";
+
+ if (state.fogMode > kFogDisabled)
+ {
+ src << "uniform highp vec4 _glesFogParams;\n";
+ src << "uniform lowp vec4 _glesFogColor;\n";
+ src << "varying lowp vec4 _glesFogColorPreMul;\n";
+ src << "varying lowp vec4 _glesFogVar;\n";
+ }
+
+ /* Texture coordinates and transformation matrices. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ src << "attribute " << (state.NeedTexUnitMatrix(i)?"highp":"mediump") << " vec4 _glesMultiTexCoord" << i << ";\n";
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ src << "varying highp vec4 v_texGenObjCoord" << i << ";\n";
+ else
+ src << "varying mediump vec2 v_texCoord" << i << ";\n";
+ }
+ else
+ {
+ src << "varying highp vec3 v_texCoord" << i << ";\n";
+ }
+
+ if(state.NeedTexUnitMatrix(i))
+ src << "uniform highp mat4 " << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << ";\n";
+ }
+
+ /* Handle color -> material mapping. */
+ const char* ambientColor = state.useVertexColorAsAmbientAndDiffuse ? "vertexColor" : "_glesFrontMaterial.ambient";
+ const char* diffuseColor = state.useVertexColorAsAmbientAndDiffuse ? "vertexColor" : "_glesFrontMaterial.diffuse";
+ const char* emissionColor = state.useVertexColorAsEmission ? "vertexColor" : "_glesFrontMaterial.emission";
+
+ /* Light params. */
+ if (state.lightingEnabled)
+ {
+ src << "struct LightModelParameters {\n";
+ src << " vec4 ambient;\n";
+ src << "};\n";
+
+ src << "struct MaterialParameters {\n";
+ src << " vec4 emission;\n";
+ src << " vec4 ambient;\n";
+ src << " vec4 diffuse;\n";
+ src << " vec4 specular;\n";
+ src << " float shininess;\n";
+ src << "};\n";
+
+ src << "uniform LightModelParameters _glesLightModel;\n";
+ src << "uniform MaterialParameters _glesFrontMaterial;\n";
+
+ if (state.lightCount > 0)
+ {
+ src << "struct LightSourceParameters {\n";
+ src << " vec4 diffuse;\n";
+ src << " vec4 position;\n";
+ src << " vec3 spotDirection;\n";
+ src << " vec4 atten;\n";
+ src << "};\n";
+
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLightModelAmbient) << ";\n";
+ for (int q = 0; q < kMaxEmulatedVertexLights; ++q)
+ {
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Diffuse + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Position + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0SpotDirection + q) << ";\n";
+ src << "uniform vec4 " << GetBuiltinVectorParamName(kShaderVecLight0Atten + q) << ";\n";
+ }
+
+ src << "uniform mat3 _glesNormalMatrix;\n";
+
+ /* Compute functions. */
+ src << "\nvec3 direction (vec4 from, vec4 to)\n";
+ src << "{\n";
+ src << " return (to.xyz * from.w - from.xyz * to.w);\n";
+ src << "}\n";
+
+ src << "\nvec3 computeLighting(LightSourceParameters light, vec3 dirToLight, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " float NdotL = max(dot(eyeNormal, dirToLight), 0.0);\n";
+ // \note in Unity, light ambient is always zero
+ src << " vec3 color = NdotL * " << diffuseColor << ".rgb * light.diffuse.rgb;\n";
+ if (state.specularEnabled)
+ {
+ src << " if (NdotL > 0.0)\n";
+ src << " {\n";
+ src << " vec3 h = normalize(dirToLight + vec3(0.0, 0.0, 1.0));\n";
+ src << " float HdotN = max(dot(eyeNormal, h), 0.0);\n";
+ // \note in Unity, light specular color is always the same as diffuse color
+ src << " color += pow(HdotN, _glesFrontMaterial.shininess) * _glesFrontMaterial.specular.rgb * light.diffuse.rgb;\n";
+ src << " }\n";
+ }
+ src << " return color;\n";
+ src << "}\n";
+
+ src << "\nvec3 computeDirLight(LightSourceParameters light, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = light.position.xyz;\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ src << "}\n";
+
+ src << "\nvec3 computePointLight(LightSourceParameters light, vec4 eyePosition, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = direction(eyePosition, light.position);\n";
+ src << " float distSqr = dot(dirToLight, dirToLight);\n";
+ // \note in Unity, const attenuation=1.0, linear=0.0
+ src << " float att = 1.0 / (1.0 + light.atten.z * distSqr);\n";
+ src << " dirToLight *= inversesqrt(distSqr);\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(att * computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ src << "}\n";
+
+ src << "\nvec3 computeSpotLight(LightSourceParameters light, vec4 eyePosition, vec3 eyeNormal, vec4 vertexColor)\n";
+ src << "{\n";
+ src << " vec3 dirToLight = direction(eyePosition, light.position);\n";
+ src << " float distSqr = dot(dirToLight, dirToLight);\n";
+ // \note in Unity, const atten=1.0, linear=0.0
+ src << " float att = 1.0 / (1.0 + light.atten.z * distSqr);\n";
+ src << " dirToLight *= inversesqrt(distSqr);\n";
+ src << " float rho = max(dot(dirToLight, light.spotDirection), 0.0);\n";
+ src << " float spotAtt = (rho - light.atten.x) * light.atten.y;\n";
+ src << " spotAtt = clamp(spotAtt, 0.0, 1.0);\n";
+ // \note D3D backend uses min(val, 1.0). We use clamp(), because Img's wrapper is buggy!
+ src << " return clamp(att * spotAtt * computeLighting(light, dirToLight, eyeNormal, vertexColor), 0.0, 1.0);\n";
+ //src << " return computeLighting(light, dirToLight, eyeNormal, vertexColor);\n"; // DEBUG DEBUG
+
+ src << "}\n";
+ }
+ }
+
+
+
+ /* Main body. */
+ src << "\nvoid main()\n";
+ src << "{\n";
+
+ /* Vertex transformation. */
+ src << " gl_Position = " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMVP) << " * _glesVertex;\n";
+ if(state.setupPointSize)
+ src << " gl_PointSize = 1.0;\n";
+
+ /* Unpack vertex color if necessary. */
+ if (state.useUniformInsteadOfVertexColor)
+ src << " vec4 vertexColor = _glesFFColor;\n";
+ else
+ src << " vec4 vertexColor = _glesColor;\n";
+
+ if (eyePositionRequired)
+ src << " highp vec4 eyePosition = " << GetShaderInstanceMatrixParamName (kShaderInstanceMatMV) << " * _glesVertex;\n";
+
+ /* Pass and transform texture coordinates. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ {
+ Assert(state.NeedTexUnitMatrix(i));
+ src << " v_texGenObjCoord" << i << " = ";
+ src << "(" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ").xyzw;\n";
+ }
+ else
+ {
+ if(state.NeedTexUnitMatrix(i))
+ {
+ src << " vec4 tmpTexCoord" << i << " = (" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ").xyzw;\n";
+ if(state.IsTexUnitProjected(i))
+ src << " v_texCoord" << i << " = tmpTexCoord" << i << ".xy / tmpTexCoord" << i << ".w;\n";
+ else
+ src << " v_texCoord" << i << " = tmpTexCoord" << i << ".xy;\n";
+ }
+ else
+ {
+ src << " v_texCoord" << i << " = _glesMultiTexCoord" << i << ".xy;\n";
+ }
+ }
+ }
+ else
+ {
+ src << " v_texCoord" << i << " = ";
+ src << "vec3(" << GetShaderInstanceMatrixParamName (kShaderInstanceMatTexture0 + i) << " * _glesMultiTexCoord" << i << ");\n";
+ if (state.texUnitGen[i] == kTexGenCubeReflect)
+ {
+ Assert(eyePositionRequired);
+ src << "{\n";
+ src << " vec3 n = v_texCoord"<< i <<".xyz;\n";
+ src << " v_texCoord"<< i <<" = reflect(eyePosition.xyz * eyePosition.w, n);\n";
+ src << "}\n";
+ }
+ }
+ }
+
+ switch (state.fogMode)
+ {
+ case kFogLinear:
+ src << " _glesFogVar = vec4(clamp (_glesFogParams.z * gl_Position.z + _glesFogParams.w, 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ case kFogExp:
+ src << " float _patchFog = _glesFogParams.y * gl_Position.z;\n";
+ src << " _glesFogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ case kFogExp2:
+ src << " float _patchFog = _glesFogParams.x * gl_Position.z;\n";
+ src << " _patchFog = _patchFog * _patchFog;\n";
+ src << " _glesFogVar = vec4(clamp (exp2(-_patchFog), 0.0, 1.0)); _glesFogVar.a = 1.0;\n";
+ src << " _glesFogColorPreMul = _glesFogColor * (vec4(1.0)-_glesFogVar);\n";
+ break;
+ default:
+ break;
+ }
+
+ /* Vertex color computation. */
+ if (state.lightingEnabled)
+ {
+ src << " vec3 color = " << emissionColor << ".rgb + " << ambientColor << ".rgb * _glesLightModel.ambient.rgb;\n";
+// src << " color = vec3(0.0);\n"; // DEBUG DEBUG
+
+ if (state.lightCount > 0)
+ {
+ src << " vec3 eyeNormal = normalize(_glesNormalMatrix * _glesNormal);\n";
+ for (int i = 0; i < state.lightCount && i < kMaxEmulatedVertexLights; i++)
+ {
+ src << " LightSourceParameters light" << i << ";\n";
+ src << " light" << i << ".diffuse = " << GetBuiltinVectorParamName(kShaderVecLight0Diffuse + i) << ";\n";
+ src << " light" << i << ".position = " << GetBuiltinVectorParamName(kShaderVecLight0Position + i) << ";\n";
+ src << " light" << i << ".spotDirection = " << GetBuiltinVectorParamName(kShaderVecLight0SpotDirection + i) << ".xyz;\n";
+ src << " light" << i << ".atten = " << GetBuiltinVectorParamName(kShaderVecLight0Atten + i) << ";\n";
+
+ if(state.GetLightType(i) == kLightDirectional)
+ src << " color += computeDirLight(light" << i << ", eyeNormal, vertexColor);\n";
+ else if(state.GetLightType(i) == kLightSpot)
+ src << " color += computeSpotLight(light" << i << ", eyePosition, eyeNormal, vertexColor);\n";
+ else
+ src << " color += computePointLight(light" << i << ", eyePosition, eyeNormal, vertexColor);\n";
+
+ Assert(eyePositionRequired || state.GetLightType(i) == kLightDirectional);
+ }
+ }
+
+ src << " float alpha = " << diffuseColor << ".a;\n";
+ src << " v_color = vec4(color, alpha);\n";
+ }
+ else
+ {
+ src << " v_color = vertexColor;\n";
+ }
+
+ src << "}\n";
+
+ DBG_SHADER_VERBOSE_GLES20("Generated VERTEX program:\n%s\n---\n", src.str().c_str());
+
+ return src.str().c_str();
+}
+
+// --- FRAGMENT program ----------------------------------------------------------------------------
+
+
+static void AddTexOperandSrc (
+ std::ostringstream& src,
+ int unitNdx,
+ combiner::Channels channels,
+ combiner::Operand operand,
+ combiner::Source source)
+{
+ src << "(";
+
+ if (operand == combiner::kOperOneMinusSrcAlpha ||
+ operand == combiner::kOperOneMinusSrcColor)
+ {
+ src << "vec4(1.0) - ";
+ }
+
+ switch (source)
+ {
+ case combiner::kSrcPrimaryColor:
+ src << "v_color";
+ break;
+
+ case combiner::kSrcPrevious:
+ src << "prev";
+ break;
+
+ case combiner::kSrcTexture:
+ src << "texture";
+ break;
+
+ case combiner::kSrcConstant:
+ src << "_glesTextureEnvColor" << unitNdx;
+ break;
+
+ default:
+ printf_console("Error: Unsupported combiner source %d\n", source);
+ src << "vec4(1.0)"; /* Dummy value. */
+ }
+
+ src << ")";
+
+ switch (operand)
+ {
+ case combiner::kOperSrcColor:
+ case combiner::kOperOneMinusSrcColor:
+ if (channels == combiner::kRGBA)
+ src << ".rgba";
+ else if (channels == combiner::kRGB)
+ src << ".rgb";
+ else if (channels == combiner::kAlpha)
+ src << ".a";
+ break;
+
+ case combiner::kOperSrcAlpha:
+ case combiner::kOperOneMinusSrcAlpha:
+ if (channels == combiner::kRGBA)
+ src << ".aaaa";
+ else if (channels == combiner::kRGB)
+ src << ".aaa";
+ else if (channels == combiner::kAlpha)
+ src << ".a";
+ break;
+ }
+}
+
+static void AddTextureCombinerBody (std::ostringstream& src, int unitNdx, UInt32 combinerDesc, combiner::Channels channels)
+{
+ Assert(combiner::kRGBA == 0);
+ Assert(combiner::kRGB == 1);
+ Assert(combiner::kAlpha == 2);
+
+ const std::string channelTypes[] = { "vec4", "vec3", "float" };
+ const std::string channelMask[] = { "", ".rgb", ".a" };
+
+
+ combiner::Source sources[3];
+ combiner::Operand operands[3];
+ combiner::Operation op;
+ int scale;
+
+ combiner::DecodeTextureCombinerDescriptor(combinerDesc,
+ op, sources, operands, scale, false);
+
+ if ((op == combiner::kOpDot3RGBA || op == combiner::kOpDot3RGB))
+ {
+ if (channels == combiner::kAlpha)
+ return;
+ channels = combiner::kRGBA;
+ }
+
+ src << " color" << channelMask[channels] << " = ";
+
+ switch (op)
+ {
+ case combiner::kOpReplace:
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ break;
+
+ case combiner::kOpModulate:
+ {
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ break;
+ }
+
+ case combiner::kOpAdd:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpSubtract:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " - ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpAddSigned:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << " - " << channelTypes[channels] << "(0.5)";
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpLerp:
+ {
+ // NOTE: arguments of Unity LERP combiner are reversed for some reason
+ src << "mix(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ", ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << ", ";
+ AddTexOperandSrc(src, unitNdx, combiner::kAlpha, operands[2], sources[2]);
+ src << ")";
+ break;
+ }
+
+ case combiner::kOpDot3RGB:
+ {
+ if (channels == combiner::kRGBA)
+ {
+ src << channelTypes[channels] << "(vec3(4.0 * dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5))), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kAlpha, operands[0], sources[0]);
+ src << ")";
+ // Note: I am really not sure what goes into alpha channel when dot3_rgb is performed, it's definetly not the from dot
+ // My best guess, that original alpha value is kept
+
+ }
+ else
+ {
+ src << channelTypes[channels] << "(4.0* dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5)))";
+ }
+ break;
+ }
+
+ case combiner::kOpDot3RGBA:
+ {
+ src << channelTypes[channels] << "(4.0 * dot(";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[0], sources[0]);
+ src << " - vec3(0.5), ";
+ AddTexOperandSrc(src, unitNdx, combiner::kRGB, operands[1], sources[1]);
+ src << " - vec3(0.5)))";
+ break;
+ }
+ case combiner::kOpMulAdd:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[2], sources[2]);
+ src << " + ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+ case combiner::kOpMulSub:
+ {
+ src << "(";
+ AddTexOperandSrc(src, unitNdx, channels, operands[0], sources[0]);
+ src << " * ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[2], sources[2]);
+ src << " - ";
+ AddTexOperandSrc(src, unitNdx, channels, operands[1], sources[1]);
+ src << ")";
+ break;
+ }
+ default:
+ ErrorString(Format("Error: Unsupported combiner operation %d\n", op).c_str());
+
+ /* Dummy value. */
+ src << channelTypes[channels] << "(1.0)";
+ break;
+ }
+
+ if (scale != 1)
+ src << " * float(" << scale << ".0)";
+ src << ";\n";
+}
+
+static void AddTextureCombinerSrc (std::ostringstream& src, int unitNdx, bool isCube, UInt32 colorCombiner, UInt32 alphaCombiner)
+{
+ src << " {\n /* Combiner " << unitNdx << " */\n";
+
+ /* Perform lookup. */
+ src << " lowp vec4 texture = " << (isCube ? "textureCube" : "texture2D") << "(u_sampler" << unitNdx << ", v_texCoord" << unitNdx << ");\n";
+
+ src << " lowp vec4 prev = " << ((unitNdx > 0)? "color": "v_color") << ";\n";
+
+ /* Combine. */
+ if (colorCombiner == alphaCombiner)
+ {
+ // In case of color and alpha combiner being the same
+ // we calc all 4 channels in a single operation
+ // as some GLSL compilers (iPhone) will silently fail on following:
+ // color.rgb = arg0.rgb (op) arg1.rgb
+ // color.a = arg0.a (op) arg1.a
+ // instead we spit:
+ // color = arg0 (op) arg1
+ // plus it is more readable
+ AddTextureCombinerBody(src, unitNdx, colorCombiner, combiner::kRGBA);
+ }
+ else
+ {
+ AddTextureCombinerBody(src, unitNdx, colorCombiner, combiner::kRGB);
+ AddTextureCombinerBody(src, unitNdx, alphaCombiner, combiner::kAlpha);
+ }
+
+ src << " }\n";
+}
+
+
+std::string BuildFragmentShaderSourceGLES20 (const FixedFunctionStateGLES20& state)
+{
+ DBG_SHADER_VERBOSE_GLES20("ShaderGeneratorGLES20::BuildFragmentShaderSource()\n");
+ DBG_SHADER_VERBOSE_GLES20(" state: %s\n", state.ToString().c_str());
+
+ std::ostringstream src;
+
+ bool alphaTestEnabled = state.alphaTest != kFuncDisabled &&
+ state.alphaTest != kFuncAlways;
+
+ /* Default varyings. */
+ src << "varying lowp vec4 v_color;\n";
+
+ /* Uniforms. */
+ if (alphaTestEnabled)
+ src << "uniform lowp float _glesAlphaTestReference;\n";
+
+ if (state.fogMode > kFogDisabled)
+ {
+ src << "varying lowp vec4 _glesFogColorPreMul;\n";
+ src << "varying lowp vec4 _glesFogVar;\n";
+ }
+
+ /* Texture units. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i])
+ {
+ if (state.texUnitGen[i] == kTexGenObject)
+ src << "varying highp vec4 v_texGenObjCoord" << i << ";\n";
+ else
+ src << "varying mediump vec2 v_texCoord" << i << ";\n";
+
+ src << "uniform sampler2D u_sampler" << i << ";\n";
+ }
+ else
+ {
+ src << "varying highp vec3 v_texCoord" << i << ";\n";
+ src << "uniform samplerCube u_sampler" << i << ";\n";
+ }
+
+ src << "uniform lowp vec4 _glesTextureEnvColor" << i << ";\n";
+ }
+
+
+ /* Main body. */
+ src << "\nvoid main()\n";
+ src << "{\n";
+
+ /* Initialize color. */
+ src << " lowp vec4 color = v_color;\n";
+
+ /* Generate correct texCoords if we have texGenObject */
+ for (int i = 0; i < state.texUnitCount; i++)
+ {
+ if (!state.texUnitCube[i] && state.texUnitGen[i] == kTexGenObject)
+ src << " highp vec2 v_texCoord" << i << " = v_texGenObjCoord" << i << ".xy / v_texGenObjCoord" << i << ".w;\n";
+ }
+
+ /* Texturing. */
+ for (int i = 0; i < state.texUnitCount; i++)
+ AddTextureCombinerSrc(src, i, state.texUnitCube[i], state.texUnitColorCombiner[i], state.texUnitAlphaCombiner[i]);
+
+ if (state.fogMode > kFogDisabled)
+ src << " gl_FragColor = color * _glesFogVar + _glesFogColorPreMul;\n";
+ else
+ src << " gl_FragColor = color;\n";
+
+ /* Alpha test. */
+ if (alphaTestEnabled)
+ {
+ Assert(gGraphicsCaps.gles20.hasAlphaTestQCOM == false);
+
+ if (state.alphaTest == kFuncNever)
+ {
+ // ToDo: Do we just discard everything, or skip drawing itself at vbo level?
+ src << " discard;\n";
+ }
+ else
+ {
+ // Reverse logic because we're using here 'discard'
+ static const char* s_cmpOps[] =
+ {
+ "", // kFuncDisabled
+ "", // kFuncNever
+ ">=", // kFuncLess
+ "!=", // kFuncEqual
+ ">", // kFuncLEqual
+ "<=", // kFuncGreater
+ "==", // kFuncNotEqual
+ "<", // kFuncGEqual
+ "", // kFuncAlways
+ };
+
+ src << " if (color.a " << s_cmpOps[state.alphaTest] << "_glesAlphaTestReference)\n";
+ src << " discard;\n";
+ }
+ }
+// src << " gl_FragColor = vec4(v_color.xyz, 1.0);\n"; // DEBUG DEBUG
+// src << " gl_FragColor = 0.5 * texture2D(u_sampler0, v_texCoord0);\n"; // DEBUG DEBUG
+// src << " gl_FragColor = vec4(_glesTextureEnvColor0.rgb, 1.0);\n"; // DEBUG DEBUG
+ src << "}\n";
+
+ DBG_SHADER_VERBOSE_GLES20("Generated FRAGMENT program:\n%s\n---\n", src.str().c_str());
+
+ return src.str().c_str();
+}
diff --git a/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h
new file mode 100644
index 0000000..26669a1
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/ShaderGeneratorGLES20.h
@@ -0,0 +1,24 @@
+#ifndef SHADERGENERATOR_GLES20_H
+#define SHADERGENERATOR_GLES20_H
+
+#include <string>
+
+class FixedFunctionStateGLES20;
+
+std::string BuildVertexShaderSourceGLES20 (const FixedFunctionStateGLES20& state);
+std::string BuildFragmentShaderSourceGLES20 (const FixedFunctionStateGLES20& state);
+
+struct FullStateCompareGLES20
+{
+ bool operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const;
+};
+struct VertexStateCompareGLES20
+{
+ bool operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const;
+};
+struct FragmentStateCompareGLES20
+{
+ bool operator() (FixedFunctionStateGLES20 const& a, FixedFunctionStateGLES20 const& b) const;
+};
+
+#endif /* SHADERGENERATOR_GLES20_H */
diff --git a/Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h b/Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h
new file mode 100644
index 0000000..eb0c6dd
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TextureIdMapGLES20.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#include "IncludesGLES20.h"
+
+inline GLuint TextureIdMapGLES20_QueryOrCreate(TextureID texid)
+{
+ GLuint ret = (GLuint)TextureIdMap::QueryNativeTexture(texid);
+ if(ret == 0)
+ {
+ GLES_CHK(glGenTextures(1, &ret));
+ TextureIdMap::UpdateTexture(texid, ret);
+ }
+
+ return ret;
+}
diff --git a/Runtime/GfxDevice/opengles20/TexturesGLES20.cpp b/Runtime/GfxDevice/opengles20/TexturesGLES20.cpp
new file mode 100644
index 0000000..b300e85
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TexturesGLES20.cpp
@@ -0,0 +1,633 @@
+#include "UnityPrefix.h"
+#include "TexturesGLES20.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/TextureUploadUtils.h"
+#include "Runtime/Graphics/S3Decompression.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "DebugGLES20.h"
+#include "TextureIdMapGLES20.h"
+
+
+#if GFX_SUPPORTS_OPENGLES20
+
+// these are used to fill in table without using too much width
+#define GL_ETC1 GL_ETC1_RGB8_OES
+#define GL_DXT1 GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+#define GL_DXT3 GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+#define GL_DXT5 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+#define GL_SRGBDXT1 GL_COMPRESSED_SRGB_S3TC_DXT1_NV
+#define GL_SRGBDXT3 GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV
+#define GL_SRGBDXT5 GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV
+#define GL_PVR1RGB2 GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+#define GL_PVR1RGBA2 GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+#define GL_PVR1RGB4 GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+#define GL_PVR1RGBA4 GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+#define GL_ATC4 GL_ATC_RGB_AMD
+#define GL_ATC8 GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
+
+#define RESERVED_FORMAT -1,-1,-1,0,
+
+const static int kTextureFormatTable[kTexFormatTotalCount*4] =
+{
+ // for GLES format == internal format, so we keep only one
+ // format, srgb format, datatype, alternative format
+ 0, 0, 0, 0,
+ GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 0, // Alpha8
+ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0, // ARGB4444
+ GL_RGB, GL_SRGB_EXT, GL_UNSIGNED_BYTE, 0, // RGB24
+ GL_RGBA, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, 0, // RGBA32
+ -1, -1, -1, kTexFormatRGBA32, // ARGB32
+ GL_RGBA, GL_SRGB_ALPHA_EXT, GL_HALF_FLOAT_OES, kTexFormatRGBA32, // ARGBFloat
+ GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0, // RGB565
+ -1, -1, -1, kTexFormatRGB24, // BGR24
+ -1, -1, -1, kTexFormatAlpha8, // AlphaLum16
+ GL_DXT1, GL_SRGBDXT1, -1, 0, // DXT1
+ GL_DXT3, GL_SRGBDXT3, -1, 0, // DXT3
+ GL_DXT5, GL_SRGBDXT5, -1, 0, // DXT5
+ GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0, // RGBA4444
+
+ RESERVED_FORMAT /*14*/ RESERVED_FORMAT /*15*/ RESERVED_FORMAT /*16*/ RESERVED_FORMAT /*17*/
+ RESERVED_FORMAT /*18*/ RESERVED_FORMAT /*19*/ RESERVED_FORMAT /*20*/ RESERVED_FORMAT /*21*/
+ RESERVED_FORMAT /*22*/ RESERVED_FORMAT /*23*/ RESERVED_FORMAT /*24*/ RESERVED_FORMAT /*25*/
+ RESERVED_FORMAT /*26*/ RESERVED_FORMAT /*27*/ RESERVED_FORMAT /*28*/ RESERVED_FORMAT /*29*/
+
+ GL_PVR1RGB2, -1, -1, 0, // PVRTC_RGB2
+ GL_PVR1RGBA2, -1, -1, 0, // PVRTC_RGBA2
+ GL_PVR1RGB4, -1, -1, 0, // PVRTC_RGB4
+ GL_PVR1RGBA4, -1, -1, 0, // PVRTC_RGBA4
+ GL_ETC1, -1, -1, 0, // ETC_RGB4
+ GL_ATC4, -1, -1, 0, // ATC_RGB4
+ GL_ATC8, -1, -1, 0, // ATC_RGBA8
+ GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0, // BGRA32
+};
+
+#undef GL_ETC1
+#undef GL_DXT1
+#undef GL_DXT3
+#undef GL_DXT5
+#undef GL_PVR1RGB2
+#undef GL_PVR1RGBA2
+#undef GL_PVR1RGB4
+#undef GL_PVR1RGBA4
+#undef GL_ATC4
+#undef GL_ATC8
+#undef RESERVED_FORMAT
+
+
+static int IsSupportedTextureFormat(TextureFormat inFormat)
+{
+ return (kTextureFormatTable[inFormat*4+0] > 0) && gGraphicsCaps.supportsTextureFormat[inFormat];
+}
+
+static int RemapToAlternativeFormat (TextureFormat inFormat)
+{
+ int altFormat = kTextureFormatTable[inFormat*4+3];
+ return altFormat > 0 ? altFormat : kTexFormatRGBA32;
+}
+
+static void GetUncompressedTextureFormat (int inFormat, bool srgb, int* format, int* dataType)
+{
+ DebugAssertIf( IsAnyCompressedTextureFormat(inFormat) );
+ *format = kTextureFormatTable[inFormat*4 + (srgb?1:0)];
+ *dataType = kTextureFormatTable[inFormat*4 + 2];
+}
+
+static void GetCompressedTextureFormat (int inFormat, bool srgb, int width, int height, int* internalFormat, int* size)
+{
+ // srgb support will be enabled only if compressed srgb is supported
+ // also we will ever use only supported formats
+ *internalFormat = kTextureFormatTable[inFormat*4 + (srgb?1:0)];
+ Assert(*internalFormat != -1);
+
+ switch(inFormat)
+ {
+ case kTexFormatPVRTC_RGB4:
+ case kTexFormatPVRTC_RGBA4:
+ *size = (std::max(width, 8) * std::max(height, 8) * 4 + 7) / 8;
+ break;
+ case kTexFormatPVRTC_RGB2:
+ case kTexFormatPVRTC_RGBA2:
+ *size = (std::max(width, 16) * std::max(height, 8) * 2 + 7) / 8;
+ break;
+ case kTexFormatDXT1:
+ case kTexFormatATC_RGB4:
+ *size = ((width + 3) / 4) * ((height + 3) / 4) * 8;
+ break;
+ case kTexFormatDXT5:
+ case kTexFormatATC_RGBA8:
+ *size = ((width + 3) / 4) * ((height + 3) / 4) * 16;
+ break;
+ case kTexFormatETC_RGB4:
+ *size = 8 * ((width+3)>>2) * ((height+3)>>2); // 8 bytes per 4x4 block
+ break;
+ default:
+ Assert(false && "Texture is not compressed");
+ }
+}
+
+static TextureFormat GetUploadFormat(TextureFormat format, bool& uploadIsCompressed, bool& decompressOnTheFly)
+{
+ TextureFormat uploadFormat = format;
+ uploadIsCompressed = IsAnyCompressedTextureFormat(format);
+ decompressOnTheFly = false;
+ if (!IsSupportedTextureFormat(format))
+ {
+ if( uploadIsCompressed )
+ {
+ uploadIsCompressed = false;
+ decompressOnTheFly = true;
+ }
+
+ uploadFormat = RemapToAlternativeFormat(format);
+ }
+
+ if( decompressOnTheFly )
+ {
+ if (IsCompressedPVRTCTextureFormat(format))
+ {
+ printf_console ("WARNING: PVRTC texture format is not supported, decompressing texture\n");
+ }
+ else if (IsCompressedDXTTextureFormat(format))
+ {
+ printf_console ("WARNING: DXT texture format is not supported, decompressing texture\n");
+ }
+ else if (IsCompressedETCTextureFormat(format))
+ {
+ printf_console ("WARNING: ETC texture format is not supported, decompressing texture\n");
+ }
+ else if (IsCompressedATCTextureFormat(format))
+ {
+ printf_console ("WARNING: ATC texture format is not supported, decompressing texture\n");
+ }
+ }
+
+ return uploadFormat;
+}
+
+#if UNITY_PEPPER
+// Unity generates textures as ARGB4444, but GLES only supports RGBA4444.
+// On mobile GLES, we swizzle textures at build time, but on NaCl, we use the same data format as used
+// in the normal web player, so we cannot do that here. So we swizzle at runtime.
+// TODO for 4.0: See if there is any good reason to use ARGB4444 at all, or if we should switch to RGBA4444 completely!
+UInt8 *SwizzleRGBA4444 (UInt8 *srcData, UInt8 **decompressBuffer, int width, int height)
+{
+ if( *decompressBuffer == NULL )
+ *decompressBuffer = new UInt8[width * height * 2];
+
+ UInt8 *feedData = *decompressBuffer;
+ for (int q = 0; q < width * height; ++q)
+ {
+ // RGBA4444 <- ARGB4444
+ UInt32 argb = *(UInt16*)(srcData + q * 2);
+ UInt16 rgba = (argb << 4) | (0x0F & (argb >> 12));
+
+ feedData[q * 2 + 0] = (UInt8)rgba;
+ feedData[q * 2 + 1] = (UInt8)(rgba >> 8);
+ }
+ return feedData;
+}
+#endif
+
+void UploadTexture2DGLES2(
+ TextureID tid, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureColorSpace colorSpace )
+{
+ Assert( srcData != NULL );
+ AssertIf( (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) && !IsNPOTTextureAllowed(mipCount > 1) );
+ if( dimension != kTexDim2D )
+ {
+ ErrorStringMsg( "Incorrect texture dimension! (dimension = %d)", dimension );
+ return;
+ }
+
+ bool uploadIsCompressed, decompressOnTheFly;
+ TextureFormat uploadFormat = GetUploadFormat(format, uploadIsCompressed, decompressOnTheFly);
+
+ DBG_TEXTURE_VERBOSE_GLES20("Texture2D #%i (%ix%i) unityFmt: %d, uploadFmt: %d, compressed: %d, decompressOnCpu: %d",
+ tid.m_ID, width, height, format, uploadFormat, (int)uploadIsCompressed, (int)decompressOnTheFly);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGLES20_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, dimension, std::numeric_limits<float>::infinity());
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ int maxLevel = mipCount - 1;
+ int baseLevel = std::min( masterTextureLimit, maxLevel );
+
+#if UNITY_IPHONE && GL_APPLE_texture_max_level
+ if( gGraphicsCaps.hasMipMaxLevel )
+ {
+ GLES_CHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL_APPLE, maxLevel));
+ }
+#endif
+
+ // xenon, for real?
+ bool isSRGB = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+ UInt8* decompressBuffer = NULL;
+ int glesFormat, datatype;
+ int uploadedSize = 0;
+ int skippedMipCount = 0;
+ for( int level = 0; level <= maxLevel; ++level )
+ {
+ UInt8* feedData;
+ int uploadLevel = level - baseLevel - skippedMipCount;
+
+ // Should this level be skipped because of master texture limit?
+ if( level < baseLevel )
+ {
+ feedData = NULL;
+ }
+ // Should this level be skipped because it is too large for hardware limits?
+ else if (width > gGraphicsCaps.maxTextureSize || height > gGraphicsCaps.maxTextureSize)
+ {
+ skippedMipCount++;
+ DBG_TEXTURE_VERBOSE_GLES20("Texture2D #%i mip level %i (%ix%i) is too big, skipping!", tid.m_ID, level, width, height);
+ feedData = NULL;
+ }
+ // Allocate temporary memory and decompress texture
+ else if( decompressOnTheFly )
+ {
+ Assert(uploadFormat == kTexFormatRGBA32);
+
+ int dstWidth = std::max( width, 4 );
+ int dstHeight = std::max( height, 4 );
+ int decompressedSize = CalculateImageSize( dstWidth, dstHeight, uploadFormat );
+ if( decompressBuffer == NULL )
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+ DecompressNativeTextureFormat ( format, width, height, (UInt32*)srcData, dstWidth, dstHeight, (UInt32*)feedData );
+
+ if (UNITY_IPHONE && (format == kTexFormatDXT3 || format == kTexFormatDXT5))
+ {
+ printf_console ("swapping DXT channels\n");
+
+ for (int q = 0; q < width * height; ++q)
+ {
+ std::swap(feedData[q * 4 + 0], feedData[q * 4 + 3]);
+ std::swap(feedData[q * 4 + 1], feedData[q * 4 + 2]);
+ }
+ }
+ }
+ // Allocate temporary memory and swizzle texture
+ else if (uploadFormat != format)
+ {
+ int decompressedSize = CalculateImageSize (width, height, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, format), format, srcData);
+ ImageReference dst (width, height, GetRowBytesFromWidthAndFormat (width, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+#if UNITY_PEPPER
+ else if (uploadFormat == kTexFormatARGB4444)
+ {
+ feedData = SwizzleRGBA4444 (srcData, &decompressBuffer, width, height);
+ }
+#endif
+ else
+ {
+ // Just feed the data
+ feedData = srcData;
+ }
+
+ // If this level should be skipped, just do nothing
+ if( feedData == NULL )
+ {
+ }
+ else
+ {
+ if (uploadIsCompressed)
+ {
+ int size;
+ GetCompressedTextureFormat (uploadFormat, isSRGB, width, height, &glesFormat, &size);
+ if( glesFormat <= 0 )
+ {
+ ErrorString(Format("Format not supported: %u!", (unsigned)uploadFormat));
+ return;
+ }
+
+ DBG_TEXTURE_VERBOSE_GLES20("GLESDebug texture: glCompressedTexImage2D: level=%i fmt=%i width=%i height=%i type=%i data=%x",
+ uploadLevel, glesFormat, width, height, datatype, (UInt32)feedData);
+ GLES_CHK(glCompressedTexImage2D (GL_TEXTURE_2D, uploadLevel, glesFormat, width, height, 0, size, feedData));
+ }
+ else
+ {
+ GetUncompressedTextureFormat (uploadFormat, isSRGB, &glesFormat, &datatype);
+ if( glesFormat <= 0 )
+ {
+ ErrorString(Format("Format not supported: %u!", (unsigned)uploadFormat));
+ return;
+ }
+
+ int internalFormat = glesFormat;
+ #if UNITY_IPHONE
+ //APPLE_texture_format_BGRA8888 dictates internal format for GL_BGRA_EXT to be GL_RGBA
+ if(glesFormat == GL_BGRA_EXT)
+ internalFormat = GL_RGBA;
+ #endif
+
+ DBG_TEXTURE_VERBOSE_GLES20("GLESDebug texture: glTexImage2D: level=%i fmt=%i width=%i height=%i type=%i data=%x",
+ uploadLevel, glesFormat, width, height, datatype, (UInt32)feedData);
+ GLES_CHK(glTexImage2D (GL_TEXTURE_2D, uploadLevel, internalFormat, width, height, 0, glesFormat, datatype, feedData));
+ }
+ }
+ if( gGraphicsCaps.gles20.needFlushAfterTextureUpload )
+ GLES_CHK(glFlush());
+
+ // Go to next mip level
+ int levelSize = CalculateImageSize (width, height, format);;
+ uploadedSize += levelSize;
+ srcData += levelSize;
+ AssertIf( width == 1 && height == 1 && level != maxLevel );
+ width = std::max( width / 2, 1 );
+ height = std::max( height / 2, 1 );
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID, uploadedSize, tid.m_ID);
+
+ AssertIf( baseLevel > maxLevel );
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+void UploadTextureSubData2DGLES2( TextureID glname, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace )
+{
+ Assert( !IsAnyCompressedTextureFormat( format ) );
+
+ bool uploadIsCompressed, decompressOnTheFly;
+ TextureFormat uploadFormat = GetUploadFormat(format, uploadIsCompressed, decompressOnTheFly);
+ Assert( !uploadIsCompressed );
+ Assert( !decompressOnTheFly );
+
+ GLuint targetTex = (GLuint)TextureIdMap::QueryNativeTexture(glname);
+
+ Assert(targetTex != 0);
+ if(targetTex == 0)
+ return;
+
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, glname, kTexDim2D, std::numeric_limits<float>::infinity());
+ GLES_CHK(glActiveTexture(GL_TEXTURE0));
+ GLES_CHK(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+
+ UInt8* decompressBuffer = NULL;
+ UInt8* feedData = srcData;
+
+ // Allocate temporary memory and swizzle texture
+ if( uploadFormat != format )
+ {
+ int decompressedSize = CalculateImageSize (width, height, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, format), format, srcData);
+ ImageReference dst (width, height, GetRowBytesFromWidthAndFormat (width, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+#if UNITY_PEPPER
+ else if (uploadFormat == kTexFormatARGB4444)
+ {
+ feedData = SwizzleRGBA4444 (srcData, &decompressBuffer, width, height);
+ }
+#endif
+
+ // xenon, for real?
+ bool isSRGB = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+ int glesFormat, datatype;
+ GetUncompressedTextureFormat (uploadFormat, isSRGB, &glesFormat, &datatype);
+
+ GLES_CHK(glTexSubImage2D( GL_TEXTURE_2D, mipLevel, x, y, width, height, glesFormat, datatype, feedData ));
+
+ if( gGraphicsCaps.gles20.needFlushAfterTextureUpload )
+ GLES_CHK(glFlush());
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+void UploadTextureCubeGLES2(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace )
+{
+ bool uploadIsCompressed, decompressOnTheFly;
+ TextureFormat uploadFormat = GetUploadFormat(format, uploadIsCompressed, decompressOnTheFly);
+ DBG_TEXTURE_VERBOSE_GLES20("TextureCUBE #%i (%ix%ix6) unityFmt: %d, uploadFmt: %d, compressed: %d, decompressOnCpu: %d",
+ tid.m_ID, size, size, format, uploadFormat, (int)uploadIsCompressed, (int)decompressOnTheFly);
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(tid.m_ID);
+
+ TextureIdMapGLES20_QueryOrCreate(tid);
+ GetRealGfxDevice().SetTexture (kShaderFragment, 0, 0, tid, kTexDimCUBE, std::numeric_limits<float>::infinity());
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+ // xenon, for real?
+ bool isSRGB = (colorSpace == kTexColorSpaceSRGBXenon || colorSpace == kTexColorSpaceSRGB);
+
+ UInt8* decompressBuffer = NULL;
+
+ const GLenum faces[6] =
+ {
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+ };
+ int uploadedSize = 0;
+ int maxLevel = mipCount - 1;
+ for (int face=0;face<6;face++)
+ {
+ int mipSize = size;
+ UInt8* data = srcData + face * faceDataSize;
+ int glesFormat, datatype, size;
+
+ for( int level=0;level<=maxLevel;level++ )
+ {
+ UInt8* feedData;
+
+ // Allocate temporary memory and decompress texture
+ if( decompressOnTheFly )
+ {
+ int dstSize = std::max( mipSize, 4 );
+ int decompressedSize = CalculateImageSize( dstSize, dstSize, uploadFormat );
+ if( decompressBuffer == NULL )
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+ DecompressNativeTextureFormat( format, mipSize, mipSize, (UInt32*)data, dstSize, dstSize, (UInt32*)feedData );
+ //SetUnpackClientStorage (false);
+ }
+ // Allocate temporary memory and swizzle texture
+ else if( uploadFormat != format )
+ {
+ int decompressedSize = CalculateImageSize (mipSize, mipSize, uploadFormat);
+ if (decompressBuffer == NULL)
+ decompressBuffer = new UInt8[decompressedSize];
+ feedData = decompressBuffer;
+
+ ImageReference src (mipSize, mipSize, GetRowBytesFromWidthAndFormat (mipSize, format), format, data);
+ ImageReference dst (mipSize, mipSize, GetRowBytesFromWidthAndFormat (mipSize, uploadFormat), uploadFormat, feedData);
+ dst.BlitImage( src );
+ }
+#if UNITY_PEPPER
+ else if (uploadFormat == kTexFormatARGB4444)
+ {
+ feedData = SwizzleRGBA4444 (srcData, &decompressBuffer, mipSize, mipSize);
+ }
+#endif
+ // Just feed the data
+ else
+ {
+ feedData = data;
+ }
+
+ // Upload
+ if( uploadIsCompressed )
+ {
+ GetCompressedTextureFormat( uploadFormat, isSRGB, mipSize, mipSize, &glesFormat, &size );
+ GLES_CHK(glCompressedTexImage2D (faces[face], level, glesFormat, mipSize, mipSize, 0, size, feedData));
+ }
+ else
+ {
+ GetUncompressedTextureFormat( uploadFormat, isSRGB, &glesFormat, &datatype );
+
+ int internalFormat = glesFormat;
+ #if UNITY_IPHONE
+ //APPLE_texture_format_BGRA8888 dictates internal format for GL_BGRA_EXT to be GL_RGBA
+ if(glesFormat == GL_BGRA_EXT)
+ internalFormat = GL_RGBA;
+ #endif
+
+ GLES_CHK(glTexImage2D (faces[face], level, internalFormat, mipSize, mipSize, 0, glesFormat, datatype, feedData));
+ }
+
+ if( gGraphicsCaps.gles20.needFlushAfterTextureUpload )
+ GLES_CHK(glFlush());
+
+ //GLAssert ();
+ int levelSize = CalculateImageSize( mipSize, mipSize, format );
+ uploadedSize += levelSize;
+ data += levelSize;
+ AssertIf( mipSize == 1 && level != maxLevel );
+
+ mipSize = std::max( mipSize / 2, 1 );
+ }
+ }
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(tid.m_ID, uploadedSize, tid.m_ID);
+
+ if( decompressBuffer )
+ delete[] decompressBuffer;
+}
+
+#if UNITY_IPHONE
+ extern bool IsActiveMSAARenderTargetGLES2();
+ extern RenderSurfaceBase* ResolveMSAASetupFBO(void*, int, GLuint, GLuint);
+ extern void ResolveMSAASetupFBO_Cleanup(RenderSurfaceBase*);
+
+ extern "C" void* UnityDefaultFBOColorBuffer();
+#endif
+
+bool ReadbackTextureGLES2(ImageReference& image, int left, int bottom, int width, int height, int destX, int destY, GLuint globalSharedFBO, GLuint helperFBO)
+{
+ bool result = true;
+
+#if UNITY_IPHONE
+ RenderSurfaceBase* resolveRS = 0;
+ GLint oldFBO = 0;
+ if(IsActiveMSAARenderTargetGLES2() && gGraphicsCaps.gles20.hasAppleMSAA)
+ {
+ GLES_CHK(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO));
+ resolveRS = ResolveMSAASetupFBO(UnityDefaultFBOColorBuffer(), 0, globalSharedFBO, helperFBO);
+ }
+#endif
+
+ int glesFormat, datatype;
+
+
+ // The whole image we're reading into can be larger than the rect we read, so setup the alignment.
+ GLES_CHK(glPixelStorei( GL_PACK_ALIGNMENT, 1 ));
+
+ void* dstImagePtr[kTexFormatPCCount] =
+ {
+ 0, //
+ image.GetRowPtr(destY) + destX, // kTexFormatAlpha8
+ 0, // kTexFormatARGB4444
+ image.GetRowPtr(destY) + destX * 3, // kTexFormatRGB24
+ image.GetRowPtr(destY) + destX * 4, // kTexFormatRGBA32
+ image.GetRowPtr(destY) + destX * 4, // kTexFormatARGB32
+ 0, // kTexFormatARGBFloat
+ image.GetRowPtr(destY) + destX * 2, // kTexFormatRGB565
+ 0, // kTexFormatBGR24
+
+ 0, // kTexFormatAlphaLum16
+ 0, // kTexFormatDXT1
+ 0, // kTexFormatDXT3
+ 0, // kTexFormatDXT5
+ 0 // kTexFormatRGBA4444
+ };
+
+ GetUncompressedTextureFormat( image.GetFormat(), false, &glesFormat, &datatype );
+ void* dstPtr = dstImagePtr[image.GetFormat()];
+ switch( image.GetFormat() )
+ {
+ case kTexFormatRGB565:
+ case kTexFormatRGBA32:
+ case kTexFormatARGB32:
+ case kTexFormatRGB24:
+ case kTexFormatAlpha8:
+ {
+ int readFormat, readType;
+ GLES_CHK(glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat));
+ GLES_CHK(glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType));
+
+ // Check if reading format/type matches our image's format/type, and width (as glReadPixels knows nothing about dst pitch)
+ if (glesFormat == readFormat && datatype == readType && width == image.GetWidth())
+ {
+ GLES_CHK(glReadPixels(left, bottom, width, height, glesFormat, datatype, dstPtr));
+ }
+ else
+ {
+ // Read as GL_RGBA/GL_UNSIGNED_BYTE, because it's always valid in OpenGL ES, and convert it to specified image format
+ int readFormat = kTexFormatRGBA32;
+ int readFormatSize = CalculateImageSize (width, height, readFormat);
+
+ GetUncompressedTextureFormat( readFormat, false, &glesFormat, &datatype );
+
+ UInt8* data = new UInt8[readFormatSize];
+ GLES_CHK(glReadPixels(left, bottom, width, height, glesFormat, datatype, data));
+
+ ImageReference src (width, height, GetRowBytesFromWidthAndFormat (width, readFormat), readFormat, data);
+ ImageReference dst (width, height, image.GetRowBytes(), image.GetFormat(), dstPtr);
+ dst.BlitImage( src );
+
+ delete[]data;
+ }
+ }
+ break;
+ default:
+ AssertString ("Not Supported");
+ result = false;
+ }
+
+#if UNITY_IPHONE
+ if(resolveRS)
+ {
+ GLES_CHK(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO));
+ ResolveMSAASetupFBO_Cleanup(resolveRS);
+ }
+#endif
+
+ return result;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/TexturesGLES20.h b/Runtime/GfxDevice/opengles20/TexturesGLES20.h
new file mode 100644
index 0000000..ded70e9
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TexturesGLES20.h
@@ -0,0 +1,21 @@
+#ifndef UNITY_TEXTURES_GLES_H_
+#define UNITY_TEXTURES_GLES_H_
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Graphics/TextureFormat.h"
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+
+class ImageReference;
+
+void UploadTexture2DGLES2(
+ TextureID glname, TextureDimension dimension, UInt8* srcData, int width, int height,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, int masterTextureLimit, TextureColorSpace colorSpace );
+void UploadTextureSubData2DGLES2(
+ TextureID glname, UInt8* srcData,
+ int mipLevel, int x, int y, int width, int height, TextureFormat format, TextureColorSpace colorSpace );
+void UploadTextureCubeGLES2(
+ TextureID tid, UInt8* srcData, int faceDataSize, int size,
+ TextureFormat format, int mipCount, UInt32 uploadFlags, TextureColorSpace colorSpace );
+bool ReadbackTextureGLES2(ImageReference& image, int left, int bottom, int width, int height, int destX, int destY, unsigned int globalSharedFBO, unsigned int helperFBO);
+
+#endif // UNITY_TEXTURES_GLES_H_
diff --git a/Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp
new file mode 100644
index 0000000..1a5887e
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.cpp
@@ -0,0 +1,109 @@
+#include "UnityPrefix.h"
+#if ENABLE_PROFILER && GFX_SUPPORTS_OPENGLES20
+#include "TimerQueryGLES20.h"
+#include "AssertGLES20.h"
+#include "UnityGLES20Ext.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+
+bool TimerQueriesGLES::Init()
+{
+ bool hasHWSupport = false;
+ if( QueryExtension("GL_NV_timer_query") )
+ {
+ hasHWSupport = gGlesExtFunc.glGetQueryObjectuivEXT && gGlesExtFunc.glGenQueriesEXT;
+
+ #if UNITY_ANDROID
+ hasHWSupport = hasHWSupport && gGlesExtFunc.glQueryCounterNV && gGlesExtFunc.glGetQueryObjectui64vNV;
+ #endif
+ }
+
+ gGraphicsCaps.hasTimerQuery = hasHWSupport;
+ if(hasHWSupport)
+ GLES_CHK(gGlesExtFunc.glGenQueriesEXT(kQueriesCount, timestamp_gl));
+
+ nextIndex = 0;
+ return true;
+}
+
+void TimerQueriesGLES::BeginTimerQueries()
+{
+ GLES_CHK(glFlush());
+ SetTimestamp();
+}
+
+void TimerQueriesGLES::EndTimerQueries()
+{
+ GLES_CHK(glFlush());
+}
+
+unsigned TimerQueriesGLES::SetTimestamp()
+{
+#if UNITY_ANDROID
+ GLES_CHK(gGlesExtFunc.glQueryCounterNV(timestamp_gl[nextIndex], GL_TIMESTAMP_NV));
+#endif
+ unsigned ret = nextIndex;
+
+ ++nextIndex;
+ if(nextIndex == kQueriesCount)
+ nextIndex = 0;
+
+ return ret;
+}
+
+UInt64 TimerQueriesGLES::GetElapsedTime(unsigned idx, bool wait)
+{
+#if UNITY_ANDROID
+ GLuint available = 0;
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectuivEXT(timestamp_gl[idx], GL_QUERY_RESULT_AVAILABLE_EXT, &available));
+ // sometimes timestamp might be not ready (we still dont know why)
+ // the only workaround would be to add glFlush into SetTimestamp
+ // but then some timings will be a bit off
+ if(wait)
+ {
+ for(unsigned i = 0 ; i < 100 && !available ; ++i)
+ {
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectuivEXT(timestamp_gl[idx], GL_QUERY_RESULT_AVAILABLE_EXT, &available));
+ }
+ }
+
+ if(available)
+ {
+ unsigned prev_idx = idx > 0 ? idx-1 : kQueriesCount-1;
+
+ EGLuint64NV time1, time2;
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectui64vNV(timestamp_gl[prev_idx], GL_QUERY_RESULT_EXT, &time1));
+ GLES_CHK(gGlesExtFunc.glGetQueryObjectui64vNV(timestamp_gl[idx], GL_QUERY_RESULT_EXT, &time2));
+
+ return time2-time1;
+ }
+#endif
+
+ return kInvalidProfileTime;
+
+}
+
+
+TimerQueryGLES::TimerQueryGLES()
+ : m_Index(0),
+ m_Time(kInvalidProfileTime)
+{
+}
+
+void TimerQueryGLES::Measure()
+{
+ m_Index = g_TimerQueriesGLES.SetTimestamp();
+ m_Time = kInvalidProfileTime;
+}
+
+ProfileTimeFormat TimerQueryGLES::GetElapsed(UInt32 flags)
+{
+ if(m_Time == kInvalidProfileTime)
+ m_Time = g_TimerQueriesGLES.GetElapsedTime(m_Index, (flags & kWaitRenderThread) != 0);
+
+ return m_Time;
+}
+
+TimerQueriesGLES g_TimerQueriesGLES;
+
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/TimerQueryGLES20.h b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.h
new file mode 100644
index 0000000..67137eb
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/TimerQueryGLES20.h
@@ -0,0 +1,47 @@
+#ifndef TIMERQUERYGLES_H
+#define TIMERQUERYGLES_H
+
+#if ENABLE_PROFILER && GFX_SUPPORTS_OPENGLES20
+
+#include "IncludesGLES20.h"
+#include "Runtime/GfxDevice/GfxTimerQuery.h"
+
+struct TimerQueriesGLES
+{
+ enum
+ {
+ kQueriesCount = 128,
+ };
+
+ GLuint timestamp_gl[kQueriesCount];
+ unsigned nextIndex;
+
+ bool Init();
+
+ void BeginTimerQueries();
+ void EndTimerQueries();
+
+ unsigned SetTimestamp();
+ UInt64 GetElapsedTime(unsigned idx, bool wait);
+};
+extern TimerQueriesGLES g_TimerQueriesGLES;
+
+
+class TimerQueryGLES
+ : public GfxTimerQuery
+{
+public:
+
+ TimerQueryGLES();
+
+ virtual void Measure();
+ virtual ProfileTimeFormat GetElapsed(UInt32 flags);
+
+private:
+
+ unsigned m_Index;
+ ProfileTimeFormat m_Time;
+};
+
+#endif
+#endif
diff --git a/Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp
new file mode 100644
index 0000000..5ba6532
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.cpp
@@ -0,0 +1,40 @@
+#include "UnityPrefix.h"
+#include "IncludesGLES20.h"
+#include "UnityGLES20Ext.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+void GlesExtFunc::InitExtFunc()
+{
+ glPushGroupMarkerEXT = (glPushGroupMarkerEXTFunc)GetGLExtProcAddress("glPushGroupMarkerEXT");
+ glPopGroupMarkerEXT = (glPopGroupMarkerEXTFunc)GetGLExtProcAddress("glPopGroupMarkerEXT");
+ glDiscardFramebufferEXT = (glDiscardFramebufferEXTFunc)GetGLExtProcAddress("glDiscardFramebufferEXT");
+ glGenQueriesEXT = (glGenQueriesEXTFunc)GetGLExtProcAddress("glGenQueriesEXT");
+ glDeleteQueriesEXT = (glDeleteQueriesEXTFunc)GetGLExtProcAddress("glDeleteQueriesEXT");
+ glGetQueryObjectuivEXT = (glGetQueryObjectuivEXTFunc)GetGLExtProcAddress("glGetQueryObjectuivEXT");
+
+ glGetProgramBinaryOES = (glGetProgramBinaryOESFunc)GetGLExtProcAddress("glGetProgramBinaryOES");
+ glProgramBinaryOES = (glProgramBinaryOESFunc)GetGLExtProcAddress("glProgramBinaryOES");
+
+ glMapBufferOES = (glMapBufferOESFunc)GetGLExtProcAddress("glMapBufferOES");
+ glUnmapBufferOES = (glUnmapBufferOESFunc)GetGLExtProcAddress("glUnmapBufferOES");
+
+ glMapBufferRangeEXT = (glMapBufferRangeEXTFunc)GetGLExtProcAddress("glMapBufferRangeEXT");
+ glFlushMappedBufferRangeEXT = (glFlushMappedBufferRangeEXTFunc)GetGLExtProcAddress("glFlushMappedBufferRangeEXT");
+
+ glRenderbufferStorageMultisampleAPPLE = (glRenderbufferStorageMultisampleAPPLEFunc)GetGLExtProcAddress("glRenderbufferStorageMultisampleAPPLE");
+ glResolveMultisampleFramebufferAPPLE = (glResolveMultisampleFramebufferAPPLEFunc)GetGLExtProcAddress("glResolveMultisampleFramebufferAPPLE");
+
+ glRenderbufferStorageMultisampleIMG = (glRenderbufferStorageMultisampleIMGFunc)GetGLExtProcAddress("glRenderbufferStorageMultisampleIMG");
+ glFramebufferTexture2DMultisampleIMG = (glFramebufferTexture2DMultisampleIMGFunc)GetGLExtProcAddress("glFramebufferTexture2DMultisampleIMG");
+
+ glRenderbufferStorageMultisampleEXT = (glRenderbufferStorageMultisampleEXTFunc)GetGLExtProcAddress("glRenderbufferStorageMultisampleEXT");
+ glFramebufferTexture2DMultisampleEXT = (glFramebufferTexture2DMultisampleEXTFunc)GetGLExtProcAddress("glFramebufferTexture2DMultisampleEXT");
+
+ glDrawBuffersNV = (glDrawBuffersNVFunc)GetGLExtProcAddress("glDrawBuffersNV");
+ glQueryCounterNV = (glQueryCounterNVFunc)GetGLExtProcAddress("glQueryCounterNV");
+ glGetQueryObjectui64vNV = (glGetQueryObjectui64vNVFunc)GetGLExtProcAddress("glGetQueryObjectui64vNV");
+
+ glAlphaFuncQCOM = (glAlphaFuncQCOMFunc)GetGLExtProcAddress("glAlphaFuncQCOM");
+}
+
+GlesExtFunc gGlesExtFunc;
diff --git a/Runtime/GfxDevice/opengles20/UnityGLES20Ext.h b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.h
new file mode 100644
index 0000000..4ab0e1d
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/UnityGLES20Ext.h
@@ -0,0 +1,342 @@
+#pragma once
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/GfxDevice/opengles/ExtensionsGLES.h"
+
+// The reason to have this file is that we use plenty of gles extensions,
+// and not all of them are handled in (all) sdk we build with.
+// Still we do guard all the usages with runtime ifs, so there is no need to intro addidtional preprocessor magic
+
+
+// ----------------------------------------------------------------------------
+// Texture Formats
+//
+
+#ifndef GL_BGRA_EXT
+ #define GL_BGRA_EXT 0x80E1
+#endif
+#ifndef GL_ETC1_RGB8_OES
+ #define GL_ETC1_RGB8_OES 0x8D64
+#endif
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+ #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+ #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+#ifndef GL_COMPRESSED_SRGB_S3TC_DXT1_NV
+ #define GL_COMPRESSED_SRGB_S3TC_DXT1_NV 0x8C4C
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV 0x8C4D
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV 0x8C4E
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV
+ #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV 0x8C4F
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+ #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#endif
+#ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+ #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+ #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#endif
+#ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+ #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+#ifndef GL_ATC_RGB_AMD
+ #define GL_ATC_RGB_AMD 0x8C92
+#endif
+#ifndef GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
+ #define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
+#endif
+
+#ifndef GL_HALF_FLOAT_OES
+ #define GL_HALF_FLOAT_OES 0x8D61
+#endif
+#ifndef GL_SRGB_EXT
+ #define GL_SRGB_EXT 0x8C40
+#endif
+#ifndef GL_SRGB_ALPHA_EXT
+ #define GL_SRGB_ALPHA_EXT 0x8C42
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_blend_minmax
+//
+
+#ifndef GL_MIN_EXT
+ #define GL_MIN_EXT 0x8007
+#endif
+
+#ifndef GL_MAX_EXT
+ #define GL_MAX_EXT 0x8008
+#endif
+
+// ----------------------------------------------------------------------------
+// GL_EXT_debug_marker
+//
+
+typedef void (*glPushGroupMarkerEXTFunc)(int len, const char* name);
+typedef void (*glPopGroupMarkerEXTFunc)();
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_discard_framebuffer
+//
+
+#ifndef GL_COLOR_EXT
+ #define GL_COLOR_EXT 0x1800
+#endif
+#ifndef GL_DEPTH_EXT
+ #define GL_DEPTH_EXT 0x1801
+#endif
+#ifndef GL_STENCIL_EXT
+ #define GL_STENCIL_EXT 0x1802
+#endif
+
+typedef void (*glDiscardFramebufferEXTFunc)(GLenum target, GLsizei numAttachments, const GLenum *attachments);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_occlusion_query_boolean
+// Note: while we dont use occlusion queries, all queries ext are based on that one
+//
+
+#ifndef GL_QUERY_RESULT_EXT
+ #define GL_QUERY_RESULT_EXT 0x8866
+#endif
+#ifndef GL_QUERY_RESULT_AVAILABLE_EXT
+ #define GL_QUERY_RESULT_AVAILABLE_EXT 0x8867
+#endif
+
+typedef void (*glGenQueriesEXTFunc)(GLuint n, GLuint *ids);
+typedef void (*glDeleteQueriesEXTFunc)(GLuint n, const GLuint *ids);
+typedef void (*glBeginQueryEXTFunc)(GLuint target, GLuint id);
+typedef void (*glEndQueryEXTFunc)(GLuint target);
+typedef void (*glGetQueryObjectuivEXTFunc)(GLuint id, GLuint pname, GLuint *params);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_shadow_samplers
+//
+
+#ifndef GL_TEXTURE_COMPARE_MODE_EXT
+ #define GL_TEXTURE_COMPARE_MODE_EXT 0x884C
+#endif
+#ifndef GL_TEXTURE_COMPARE_FUNC_EXT
+ #define GL_TEXTURE_COMPARE_FUNC_EXT 0x884D
+#endif
+#ifndef GL_COMPARE_REF_TO_TEXTURE_EXT
+ #define GL_COMPARE_REF_TO_TEXTURE_EXT 0x884E
+#endif
+#ifndef GL_SAMPLER_2D_SHADOW_EXT
+ #define GL_SAMPLER_2D_SHADOW_EXT 0x8B62
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_texture_rg
+//
+
+#ifndef GL_RED_EXT
+ #define GL_RED_EXT 0x1903
+#endif
+#ifndef GL_RG_EXT
+ #define GL_RG_EXT 0x8227
+#endif
+
+
+// ----------------------------------------------------------------------------
+// GL_OES_get_program_binary
+//
+
+#ifndef GL_PROGRAM_BINARY_LENGTH_OES
+ #define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#endif
+
+#ifndef GL_NUM_PROGRAM_BINARY_FORMATS_OES
+ #define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE
+#endif
+
+typedef void (*glGetProgramBinaryOESFunc)(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary);
+typedef void (*glProgramBinaryOESFunc)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length);
+
+
+// ----------------------------------------------------------------------------
+// GL_OES_mapbuffer
+//
+
+#ifndef GL_WRITE_ONLY_OES
+ #define GL_WRITE_ONLY_OES 0x88B9
+#endif
+
+typedef void* (*glMapBufferOESFunc)(GLenum target, GLenum access);
+typedef GLboolean (*glUnmapBufferOESFunc)(GLenum target);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_map_buffer_range
+//
+
+#ifndef GL_MAP_READ_BIT_EXT
+ #define GL_MAP_READ_BIT_EXT 0x0001
+#endif
+#ifndef GL_MAP_WRITE_BIT_EXT
+ #define GL_MAP_WRITE_BIT_EXT 0x0002
+#endif
+#ifndef GL_MAP_INVALIDATE_RANGE_BIT_EXT
+ #define GL_MAP_INVALIDATE_RANGE_BIT_EXT 0x0004
+#endif
+#ifndef GL_MAP_INVALIDATE_BUFFER_BIT_EXT
+ #define GL_MAP_INVALIDATE_BUFFER_BIT_EXT 0x0008
+#endif
+#ifndef GL_MAP_FLUSH_EXPLICIT_BIT_EXT
+ #define GL_MAP_FLUSH_EXPLICIT_BIT_EXT 0x0010
+#endif
+#ifndef GL_MAP_UNSYNCHRONIZED_BIT_EXT
+ #define GL_MAP_UNSYNCHRONIZED_BIT_EXT 0x0020
+#endif
+
+typedef void* (*glMapBufferRangeEXTFunc)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+typedef void (*glFlushMappedBufferRangeEXTFunc)(GLenum target, GLintptr offset, GLsizeiptr length);
+
+
+
+// ----------------------------------------------------------------------------
+// GL_APPLE_framebuffer_multisample
+//
+
+#ifndef GL_MAX_SAMPLES_APPLE
+ #define GL_MAX_SAMPLES_APPLE 0x8D57
+#endif
+#ifndef GL_READ_FRAMEBUFFER_APPLE
+ #define GL_READ_FRAMEBUFFER_APPLE 0x8CA8
+#endif
+#ifndef GL_DRAW_FRAMEBUFFER_APPLE
+ #define GL_DRAW_FRAMEBUFFER_APPLE 0x8CA9
+#endif
+
+
+typedef void (*glRenderbufferStorageMultisampleAPPLEFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glResolveMultisampleFramebufferAPPLEFunc)(void);
+
+
+// ----------------------------------------------------------------------------
+// GL_IMG_multisampled_render_to_texture
+//
+
+#ifndef GL_MAX_SAMPLES_IMG
+ #define GL_MAX_SAMPLES_IMG 0x9135
+#endif
+
+typedef void (*glRenderbufferStorageMultisampleIMGFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glFramebufferTexture2DMultisampleIMGFunc)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+
+
+// ----------------------------------------------------------------------------
+// GL_EXT_multisampled_render_to_texture
+//
+
+#ifndef GL_MAX_SAMPLES_EXT
+#define GL_MAX_SAMPLES_EXT 0x8D57
+#endif
+
+typedef void (*glRenderbufferStorageMultisampleEXTFunc)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (*glFramebufferTexture2DMultisampleEXTFunc)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+
+
+// ----------------------------------------------------------------------------
+// GL_NV_draw_buffers
+//
+
+#ifndef GL_MAX_DRAW_BUFFERS_NV
+ #define GL_MAX_DRAW_BUFFERS_NV 0x8824
+#endif
+#ifndef GL_COLOR_ATTACHMENT0_NV
+ #define GL_COLOR_ATTACHMENT0_NV 0x8CE0
+#endif
+
+typedef void (*glDrawBuffersNVFunc)(int len, const GLenum* bufs);
+
+
+// ----------------------------------------------------------------------------
+// GL_NV_timer_query
+//
+
+#ifndef GL_TIME_ELAPSED_NV
+ #define GL_TIME_ELAPSED_NV 0x88BF
+#endif
+#ifndef GL_TIMESTAMP_NV
+ #define GL_TIMESTAMP_NV 0x8E28
+#endif
+
+typedef unsigned long long int EGLuint64NV;
+
+typedef void (*glQueryCounterNVFunc)(GLuint target, GLuint id);
+typedef void (*glGetQueryObjectui64vNVFunc)(GLuint id, GLuint pname, EGLuint64NV *params);
+
+
+// ----------------------------------------------------------------------------
+// GL_QCOM_alpha_test
+//
+
+#ifndef GL_ALPHA_TEST_QCOM
+ #define GL_ALPHA_TEST_QCOM 0x0BC0
+#endif
+
+typedef void (*glAlphaFuncQCOMFunc)(GLenum func, GLfloat ref);
+
+
+// ----------------------------------------------------------------------------
+// common place to get function pointers
+//
+
+struct
+GlesExtFunc
+{
+ glPushGroupMarkerEXTFunc glPushGroupMarkerEXT;
+ glPopGroupMarkerEXTFunc glPopGroupMarkerEXT;
+ glDiscardFramebufferEXTFunc glDiscardFramebufferEXT;
+ glGenQueriesEXTFunc glGenQueriesEXT;
+ glDeleteQueriesEXTFunc glDeleteQueriesEXT;
+ glGetQueryObjectuivEXTFunc glGetQueryObjectuivEXT;
+
+ glGetProgramBinaryOESFunc glGetProgramBinaryOES;
+ glProgramBinaryOESFunc glProgramBinaryOES;
+
+ glMapBufferOESFunc glMapBufferOES;
+ glUnmapBufferOESFunc glUnmapBufferOES;
+
+ glMapBufferRangeEXTFunc glMapBufferRangeEXT;
+ glFlushMappedBufferRangeEXTFunc glFlushMappedBufferRangeEXT;
+
+
+ glRenderbufferStorageMultisampleAPPLEFunc glRenderbufferStorageMultisampleAPPLE;
+ glResolveMultisampleFramebufferAPPLEFunc glResolveMultisampleFramebufferAPPLE;
+
+ glRenderbufferStorageMultisampleIMGFunc glRenderbufferStorageMultisampleIMG;
+ glFramebufferTexture2DMultisampleIMGFunc glFramebufferTexture2DMultisampleIMG;
+
+ glRenderbufferStorageMultisampleEXTFunc glRenderbufferStorageMultisampleEXT;
+ glFramebufferTexture2DMultisampleEXTFunc glFramebufferTexture2DMultisampleEXT;
+
+ glDrawBuffersNVFunc glDrawBuffersNV;
+ glQueryCounterNVFunc glQueryCounterNV;
+ glGetQueryObjectui64vNVFunc glGetQueryObjectui64vNV;
+
+ glAlphaFuncQCOMFunc glAlphaFuncQCOM;
+
+ void InitExtFunc();
+};
+extern GlesExtFunc gGlesExtFunc;
diff --git a/Runtime/GfxDevice/opengles20/VBOGLES20.cpp b/Runtime/GfxDevice/opengles20/VBOGLES20.cpp
new file mode 100644
index 0000000..01b715b
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/VBOGLES20.cpp
@@ -0,0 +1,1559 @@
+#include "UnityPrefix.h"
+
+//chai
+//#define GFX_SUPPORTS_OPENGLES20 1
+
+#if GFX_SUPPORTS_OPENGLES20
+#include "VBOGLES20.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/GfxDevice/ChannelAssigns.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "IncludesGLES20.h"
+#include "AssertGLES20.h"
+#include "GpuProgramsGLES20.h"
+#include "DebugGLES20.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/GfxDevice/GLESChannels.h"
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+
+static const GLenum kTopologyGLES2[kPrimitiveTypeCount] = {
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLES,
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_POINTS,
+};
+
+template <typename T>
+inline T Align (T v, size_t alignment)
+{
+ return (v + (alignment-1)) & ~(alignment-1);
+}
+
+extern void VBOContainsColorGLES20 (bool flag);
+extern void GfxDeviceGLES20_SetDrawCallTopology(GfxPrimitiveType topology);
+
+#define DISABLE_GLES_CALLS 0
+#define DISABLE_DRAW_CALLS_ONLY 0
+
+
+static UInt32 sCurrentTargetMap = 0;
+void InvalidateVertexInputCacheGLES20()
+{
+ GLES_CHK(glDisableVertexAttribArray(GL_VERTEX_ARRAY));
+ GLES_CHK(glDisableVertexAttribArray(GL_NORMAL_ARRAY));
+ GLES_CHK(glDisableVertexAttribArray(GL_COLOR_ARRAY));
+ for (size_t q = 0; q < gGraphicsCaps.maxTexImageUnits; ++q)
+ {
+ if (GL_TEXTURE_ARRAY0 + q < gGraphicsCaps.gles20.maxAttributes)
+ {
+ GLES_CHK(glDisableVertexAttribArray(GL_TEXTURE_ARRAY0 + q));
+ }
+ }
+ sCurrentTargetMap = 0;
+}
+
+static UInt32 MaskUnavailableChannels(UInt32 targetMap, UInt32 unavailableChannels)
+{
+ if (unavailableChannels == 0)
+ return targetMap;
+
+ #define MASK_UNAVAILABLE_CHANNEL(schnl, vchnl) if (unavailableChannels & schnl) targetMap &= ~vchnl;
+
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Vertex), kVtxChnVertex);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Color), kVtxChnColor);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Normal), kVtxChnNormal);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(TexCoord0), kVtxChnTexCoord0);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(TexCoord1), kVtxChnTexCoord1);
+ MASK_UNAVAILABLE_CHANNEL(VERTEX_FORMAT1(Tangent), kVtxChnTexCoord2);
+
+ #undef MASK_UNAVAILABLE_CHANNEL
+
+ return targetMap;
+}
+
+#if NV_STATE_FILTERING
+typedef struct
+{
+ GLint size;
+ GLenum type;
+ GLboolean normalized;
+ GLsizei stride;
+ GLuint buffer;
+ const GLvoid* pointer;
+} FilteredVertexAttribPointer;
+static GLuint boundBuffers[2];
+static FilteredVertexAttribPointer* currPointers = 0;
+static GLint max_attribs;
+
+void filteredInitGLES20()
+{
+ static bool firstCall = true;
+ if (!firstCall)
+ return;
+ firstCall = false;
+
+ memset(boundBuffers, 0, 2*sizeof(GLuint));
+
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_attribs);
+ currPointers = new FilteredVertexAttribPointer[max_attribs]; // this memory is considered 'static' and is never freed.
+ memset(currPointers, 0x00, max_attribs * sizeof(FilteredVertexAttribPointer));
+}
+
+void filteredBindBufferGLES20(GLenum target, GLuint buffer, bool isImmediate)
+{
+ int index = 0;
+ if (target == GL_ELEMENT_ARRAY_BUFFER)
+ index = 1;
+
+ if ( (target != GL_ELEMENT_ARRAY_BUFFER && target != GL_ARRAY_BUFFER) ||
+ (boundBuffers[index] != buffer))
+ {
+ boundBuffers[index] = buffer;
+ glBindBuffer(target, buffer);
+
+ if (buffer && !isImmediate)
+ {
+ // TODO: This is to flush the matrix to the shader, but why is it needed?
+ // Isn't the matrix set when switching buffer elsewhere?
+ void GfxDeviceGLES20_MarkWorldViewProjDirty();
+ GfxDeviceGLES20_MarkWorldViewProjDirty();
+ }
+ }
+}
+
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
+{
+ bool doCall = false;
+ if (index < 0 || index >= max_attribs)
+ {
+ doCall = true;
+ }
+ else
+ {
+ FilteredVertexAttribPointer &p = currPointers[index];
+ if (p.buffer != boundBuffers[0] || p.size != size || p.type != type || p.normalized != normalized || p.stride != stride || p.pointer != pointer)
+ {
+ doCall = true;
+ p.buffer = boundBuffers[0];
+ p.size = size;
+ p.type = type;
+ p.normalized = normalized;
+ p.stride = stride;
+ p.pointer = pointer;
+ }
+ }
+ if (doCall)
+ {
+ glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ }
+}
+
+void filteredDeleteBuffersGLES20(GLsizei n, const GLuint *buffers)
+{
+ if (buffers && n)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ for (int j = 0; j < max_attribs; j++)
+ {
+ if (currPointers[j].buffer == buffers[i])
+ {
+ memset(&currPointers[j], 0, sizeof(FilteredVertexAttribPointer));
+ }
+ }
+ }
+ }
+ glDeleteBuffers(n, buffers);
+}
+
+#ifndef glBindBuffer
+#define glBindBuffer filteredBindBufferGLES20
+#endif
+#ifndef glDeleteBuffers
+#define glDeleteBuffers filteredDeleteBuffersGLES20
+#endif
+#ifndef glVertexAttribPointer
+#define glVertexAttribPointer filteredVertexAttribPointerGLES20
+#endif
+
+void StateFiltering_InvalidateVBOCacheGLES20()
+{
+ memset(boundBuffers, 0, 2*sizeof(GLuint));
+ if(currPointers)
+ memset(currPointers, 0x00, max_attribs * sizeof(FilteredVertexAttribPointer));
+}
+
+
+#endif
+
+
+#define SETUP_VERTEX_CHANNEL(vchnl, vcomp, glArray, norm, channelSize, channelType, stride, ptr) \
+ if (targetMap & vchnl) \
+ { \
+ if (channelsToEnable & vchnl) { \
+ GLES_CHK(glEnableVertexAttribArray(glArray)); \
+ } \
+ const ShaderChannel src = channels.GetSourceForTarget( vcomp ); \
+ GLES_CHK(glVertexAttribPointer(glArray, channelSize, channelType, norm, stride, ptr));\
+ } else if (channelsToDisable & vchnl) { \
+ GLES_CHK(glDisableVertexAttribArray(glArray)); \
+ } \
+
+#define SETUP_TEXCOORD_CHANNEL(vchnl, vcomp, glTex, setFunc) \
+ if (targetMap & vchnl) \
+ { \
+ if (channelsToEnable & vchnl) { \
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_COORD_ARRAY)); \
+ } \
+ const ShaderChannel src = channels.GetSourceForTarget( vcomp ); \
+ setFunc; \
+ } else if (channelsToDisable & vchnl) { \
+ GLES_CHK(glEnableVertexAttribArray(GL_TEXTURE_COORD_ARRAY)); \
+ }
+
+// TODO: normalize normals???
+#define LINK_TEXCOORD_CHANNEL(glTex) \
+ GLES_CHK(glEnableVertexAttribArray(glTex)); \
+ GLES_CHK(glVertexAttribPointer(glTex, channelSizes[src], channelTypes[src], false, \
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src])));
+
+
+#if UNITY_ANDROID || UNITY_TIZEN
+static void WorkaroundMaliBug(const UInt32 strides[kShaderChannelCount], void* dataChannel[kShaderChannelCount])
+{
+ // on Mali (first devices) there is bug in driver
+ // that results in attributes from interleaved streams
+ // remain active, even though they are disabled
+ // as a workaround, find any non null ptr in dataChannel and set it
+
+ void* readPtr = 0;
+ UInt32 stride = 0;
+ for( unsigned i = 0 ; i < kShaderChannelCount && !readPtr ; ++i)
+ {
+ readPtr = dataChannel[i];
+ stride = strides[i];
+ }
+
+ GLES_CHK(glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_COLOR_ARRAY, 4, GL_UNSIGNED_BYTE, true, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_NORMAL_ARRAY, 3, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY0, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY1, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY2, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY3, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY4, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY5, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY6, 2, GL_FLOAT, false, stride, readPtr));
+ GLES_CHK(glVertexAttribPointer(GL_TEXTURE_ARRAY7, 2, GL_FLOAT, false, stride, readPtr));
+
+ InvalidateVertexInputCacheGLES20();
+}
+#endif
+
+//如果是不用VBO的模式,直接上传顶点数据
+static void SetupVertexInput(const ChannelAssigns& channels, void* dataChannel[kShaderChannelCount], const UInt32 strides[kShaderChannelCount], const int channelSizes[kShaderChannelCount], const GLenum channelTypes[kShaderChannelCount], UInt32 unavailableChannels = 0)
+{
+#if DISABLE_GLES_CALLS
+ return;
+#endif
+ GfxDevice& device = GetRealGfxDevice();
+
+#if UNITY_ANDROID || UNITY_TIZEN
+ if( gGraphicsCaps.gles20.buggyDisableVAttrKeepsActive )
+ WorkaroundMaliBug(strides, dataChannel);
+#endif
+
+ UInt32 targetMap = MaskUnavailableChannels(channels.GetTargetMap(), unavailableChannels);
+
+ const UInt32 channelsDiff = sCurrentTargetMap ^ targetMap;
+ const UInt32 channelsToEnable = channelsDiff & targetMap;
+ const UInt32 channelsToDisable = channelsDiff & (~targetMap);
+
+ SETUP_VERTEX_CHANNEL(kVtxChnVertex, kVertexCompVertex, GL_VERTEX_ARRAY, false,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+ SETUP_VERTEX_CHANNEL(kVtxChnColor, kVertexCompColor, GL_COLOR_ARRAY, true,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+ SETUP_VERTEX_CHANNEL(kVtxChnNormal, kVertexCompNormal, GL_NORMAL_ARRAY, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord0, kVertexCompTexCoord0, GL_TEXTURE_ARRAY0, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord1, kVertexCompTexCoord1, GL_TEXTURE_ARRAY1, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord2, kVertexCompTexCoord2, GL_TEXTURE_ARRAY2, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord3, kVertexCompTexCoord3, GL_TEXTURE_ARRAY3, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord4, kVertexCompTexCoord4, GL_TEXTURE_ARRAY4, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord5, kVertexCompTexCoord5, GL_TEXTURE_ARRAY5, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord6, kVertexCompTexCoord6, GL_TEXTURE_ARRAY6, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+ SETUP_VERTEX_CHANNEL(kVtxChnTexCoord7, kVertexCompTexCoord7, GL_TEXTURE_ARRAY7, channelTypes[src] == GL_BYTE,
+ channelSizes[src], channelTypes[src],
+ strides[src], reinterpret_cast<GLvoid const*>(dataChannel[src]));
+
+ sCurrentTargetMap = targetMap;
+
+ // setup fixed function texGens
+ const UInt32 sourceMap = channels.GetSourceMap();
+ {
+ const ShaderChannel src = kShaderChannelVertex;
+ if( device.IsPositionRequiredForTexGen() && (sourceMap & (1 << src)) )
+ {
+ for (int texUnit = 0; texUnit < gGraphicsCaps.maxTexImageUnits; ++texUnit)
+ {
+ if( device.IsPositionRequiredForTexGen(texUnit) )
+ {
+ // pass position as tex-coord, if required by texgen operation
+ LINK_TEXCOORD_CHANNEL(GL_TEXTURE_ARRAY0 + texUnit);
+ Assert(texUnit < ARRAY_SIZE(sTexCoordChannels));
+ sCurrentTargetMap |= sTexCoordChannels[texUnit];
+ }
+ }
+ }
+ }
+
+ {
+ const ShaderChannel src = kShaderChannelNormal;
+ if( device.IsNormalRequiredForTexGen() && (sourceMap & (1 << src)) )
+ {
+ for (int texUnit = 0; texUnit < gGraphicsCaps.maxTexImageUnits; ++texUnit)
+ {
+ if( device.IsNormalRequiredForTexGen(texUnit) )
+ {
+ // pass normal as tex-coord, if required by texgen operation
+ LINK_TEXCOORD_CHANNEL(GL_TEXTURE_ARRAY0 + texUnit);
+ Assert(texUnit < ARRAY_SIZE(sTexCoordChannels));
+ sCurrentTargetMap |= sTexCoordChannels[texUnit];
+ }
+ }
+ }
+ }
+}
+
+/*
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+*/
+
+
+//chai:
+// called by
+// DrawVBO()
+// DrawCustomIndexed()
+void GLES2VBO::DrawInternal(int vertexBufferID, int indexBufferID, const ChannelAssigns& channels, void* indices, UInt32 indexCount, GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount)
+{
+ UInt32 unavailableChannels = GetUnavailableChannels(channels);
+
+ // should never happen; a dummy all-white vertex color array is always created by Mesh code
+ AssertIf( unavailableChannels & (1<<kShaderChannelColor) );
+
+#if DISABLE_GLES_CALLS
+ return;
+#endif
+
+ DBG_LOG_GLES20("---> GLES2VBO::DrawVBO indexCount:%d channels: %04X/%04X, unavailable: %04X", (int)indexCount, channels.GetTargetMap(), channels.GetSourceMap(), unavailableChannels, unavailableChannels);
+
+ VBOContainsColorGLES20 (channels.GetSourceForTarget (kVertexCompColor) == kShaderChannelColor);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID));
+
+ // setup vertex state
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID));
+ void* channelData[kShaderChannelCount];
+ UInt32 channelStrides[kShaderChannelCount];
+ if (vertexBufferID != 0)
+ GetChannelOffsetsAndStrides(channelData, channelStrides);
+ else
+ GetChannelDataAndStrides(channelData, channelStrides);
+
+ int channelSizes[kShaderChannelCount];
+ GLenum channelTypes[kShaderChannelCount];
+ SetupGLESChannelSizes(m_Channels, channelSizes);
+ SetupGLESChannelTypes(m_Channels, channelTypes);
+ SetupVertexInput(channels, channelData, channelStrides, channelSizes, channelTypes, unavailableChannels);
+
+ GfxDeviceGLES20_SetDrawCallTopology(topology);
+
+ //chai: 在这里设置使用的shader,并设置uniforms
+ //stack:
+ // GfxDevice::BeforeDrawCall
+ // GpuProgramsGLES20::ApplyGpuProgramES20
+ GetRealGfxDevice().BeforeDrawCall(false);
+
+#if DBG_LOG_GLES20_ACTIVE
+ DumpVertexArrayStateGLES20();
+#endif
+
+ // draw
+#if !DISABLE_DRAW_CALLS_ONLY
+ ABSOLUTE_TIME drawDt = START_TIME;
+ GLenum gltopo = kTopologyGLES2[topology];
+ GLES_CHK(glDrawElements(gltopo, indexCount, GL_UNSIGNED_SHORT, indices));
+ drawDt = ELAPSED_TIME(drawDt);
+
+ int primCount = GetPrimitiveCount(indexCount, topology, false);
+ GetRealGfxDevice().GetFrameStats().AddDrawCall (primCount, drawVertexCount, drawDt);
+#endif
+}
+
+void GLES2VBO::DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount )
+{
+ int indexBufferID = m_IndexBufferID;
+ void* indexBufferData = (UInt8*)(m_IndexBufferID ? 0 : m_IBData.indices) + firstIndexByte;
+
+ // If we're drawing quads, convert them into triangles; into a temporary index buffer area
+ void* tempIndexBuffer = NULL;
+ if (topology == kPrimitiveQuads)
+ {
+ UInt32 ibCapacityNeeded = indexCount/4*6*2;
+
+ // Get IB space from shared buffer or just allocate
+ void* ibPtr;
+ if (gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ ibPtr = UNITY_MALLOC(kMemDynamicGeometry, ibCapacityNeeded);
+ tempIndexBuffer = ibPtr;
+ }
+ else
+ {
+ ibPtr = LockSharedBufferGLES20 (GL_ELEMENT_ARRAY_BUFFER, ibCapacityNeeded);
+ }
+ DebugAssert (ibPtr);
+
+ // Convert quads into triangles
+ FillIndexBufferForQuads ((UInt16*)ibPtr, ibCapacityNeeded, (const UInt16*)((UInt8*)m_ReadableIndices + firstIndexByte), indexCount/4);
+
+ // Finish up with temporary space
+ if (gGraphicsCaps.gles20.slowDynamicVBO)
+ {
+ indexBufferID = 0;
+ indexBufferData = ibPtr;
+ }
+ else
+ {
+ indexBufferID = UnlockSharedBufferGLES20 ();
+ indexBufferData = NULL;
+ }
+ indexCount = indexCount/4*6;
+ }
+
+ // Draw!
+ DrawInternal( m_UsesVBO ? m_VertexBufferID[m_CurrentBufferIndex] : 0, indexBufferID, channels, indexBufferData, indexCount,
+ topology, firstVertex, firstVertex+vertexCount, vertexCount);
+
+ // Release any temporary buffer we might have allocated
+ if (tempIndexBuffer)
+ {
+ UNITY_FREE(kMemDynamicGeometry, tempIndexBuffer);
+ }
+}
+
+void GLES2VBO::DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount )
+{
+ Assert (topology != kPrimitiveQuads); // only called by static batching; which only handles triangles
+
+ int ibo = 0;
+ if(!gGraphicsCaps.gles20.forceStaticBatchFromMem)
+ {
+ // we expect static batches to be quite large ibos, and in that case drawing from memory is worst case scenario
+ // sure, unless running on some buggy piece of sh**t
+ const size_t ibCapacity = indexCount * kVBOIndexSize;
+ void* dstIndices = LockSharedBufferGLES20 (GL_ELEMENT_ARRAY_BUFFER, ibCapacity, true);
+ DebugAssert (dstIndices);
+ memcpy (dstIndices, indices, ibCapacity);
+ ibo = UnlockSharedBufferGLES20 (0, true);
+ indices = 0;
+ }
+
+ DrawInternal( m_UsesVBO ? m_VertexBufferID[m_CurrentBufferIndex] : 0, ibo, channels, indices, indexCount,
+ topology, vertexRangeBegin, vertexRangeEnd, drawVertexCount);
+}
+
+GLES2VBO::GLES2VBO()
+: m_CurrentBufferIndex(0)
+, m_IndexBufferID(0)
+, m_IBSize(0)
+, m_ReadableIndices(0)
+, m_VBOUsage(GL_STATIC_DRAW)
+, m_IBOUsage(GL_STATIC_DRAW)
+, m_UsesVBO(false)
+, m_UsesIBO(false)
+{
+ ::memset(m_VertexBufferID, 0x0, sizeof(m_VertexBufferID));
+ ::memset(&m_IBData, 0, sizeof(m_IBData));
+}
+
+GLES2VBO::~GLES2VBO()
+{
+ Cleanup ();
+}
+
+void GLES2VBO::Cleanup()
+{
+ int bufferCount = HasStreamWithMode(kStreamModeDynamic) ? DynamicVertexBufferCount : 1;
+ glDeregisterBufferData(bufferCount, (GLuint*)m_VertexBufferID);
+ GLES_CHK(glDeleteBuffers(bufferCount, (GLuint*)m_VertexBufferID));
+ ::memset(m_VertexBufferID, 0x0, sizeof(m_VertexBufferID));
+
+ if (m_IndexBufferID)
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferID));
+ m_IndexBufferID = 0;
+ }
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+}
+
+void GLES2VBO::Recreate()
+{
+ MarkBuffersLost();
+
+ if(m_VertexData.bufferSize > 0)
+ EnsureVerticesInited(true);
+
+ if(m_IBSize > 0)
+ EnsureIndicesInited();
+}
+
+bool GLES2VBO::IsVertexBufferLost() const
+{
+ return (m_VertexBufferID[m_CurrentBufferIndex] == 0 && m_UsesVBO);
+}
+
+bool GLES2VBO::IsIndexBufferLost() const
+{
+ return (m_IndexBufferID == 0 && m_UsesIBO);
+}
+
+void GLES2VBO::MarkBuffersLost()
+{
+ ::memset(m_VertexBufferID, 0x0, sizeof(m_VertexBufferID));
+ m_IndexBufferID = 0;
+
+ // we also want to pretend we use vbo/ibo to hit "lost" path
+ // in case of vbo we also need to get rid of bufferedvbo copy
+
+ if(!m_UsesVBO)
+ {
+ UnbufferVertexData();
+ m_UsesVBO = true;
+ }
+ m_UsesIBO = true;
+}
+
+
+bool GLES2VBO::MapVertexStream(VertexStreamData& outData, unsigned stream)
+{
+ DebugAssert(!IsAnyStreamMapped());
+ Assert(m_VertexData.bufferSize > 0);
+
+ if(HasStreamWithMode(kStreamModeDynamic))
+ m_CurrentBufferIndex = (m_CurrentBufferIndex + 1) % DynamicVertexBufferCount;
+ else
+ m_CurrentBufferIndex = 0;
+
+ // TODO: make it possible to change m_UsesVBO at runtime
+ // for now once we use mapbuffer, the buffered data is lost so no going back
+
+ if(m_UsesVBO && (gGraphicsCaps.gles20.hasMapbuffer || gGraphicsCaps.gles20.hasMapbufferRange))
+ {
+ static const int _MapRangeFlags = GL_MAP_WRITE_BIT_EXT | GL_MAP_INVALIDATE_BUFFER_BIT_EXT;
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferID[m_CurrentBufferIndex]));
+
+ void* buf = 0;
+ if(gGraphicsCaps.gles20.hasMapbufferRange)
+ buf = gGlesExtFunc.glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, m_VertexData.bufferSize, _MapRangeFlags);
+ else
+ buf = gGlesExtFunc.glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
+
+ GLESAssert();
+ if(buf)
+ {
+ const StreamInfo& info = m_VertexData.streams[stream];
+ outData.buffer = (UInt8*)buf + info.offset;
+ outData.channelMask = info.channelMask;
+ outData.stride = info.stride;
+ outData.vertexCount = m_VertexData.vertexCount;
+
+ UnbufferVertexData();
+ m_IsStreamMapped[stream] = true;
+ }
+ return buf != 0;
+ }
+
+ return BufferedVBO::MapVertexStream(outData, stream);
+}
+
+void GLES2VBO::UnmapVertexStream( unsigned stream )
+{
+ Assert(m_IsStreamMapped[stream]);
+ Assert(m_VertexData.bufferSize > 0);
+
+ m_IsStreamMapped[stream] = false;
+ if(m_UsesVBO)
+ {
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferID[m_CurrentBufferIndex]));
+ if(gGraphicsCaps.gles20.hasMapbuffer || gGraphicsCaps.gles20.hasMapbufferRange)
+ GLES_CHK(gGlesExtFunc.glUnmapBufferOES(GL_ARRAY_BUFFER));
+ else
+ GLES_CHK(glBufferSubData(GL_ARRAY_BUFFER, 0, m_VertexData.bufferSize, m_VertexData.buffer));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ }
+}
+
+// the threshold is pretty arbitrary:
+// from our tests on "slowDynamicVBO" gpus "small" buffers are faster drawn from mem, and "bigger" ones as buffers (like normal case)
+// so we just pick something as small buffer threshold and be gone
+
+static const int _SmallBufferSizeThreshold = 1024;
+
+bool GLES2VBO::ShouldUseVBO()
+{
+ if(HasStreamWithMode(kStreamModeDynamic) && gGraphicsCaps.gles20.slowDynamicVBO)
+ return m_VertexData.bufferSize > _SmallBufferSizeThreshold;
+
+ return true;
+}
+
+bool GLES2VBO::ShouldUseIBO()
+{
+ if(AreIndicesDynamic() && gGraphicsCaps.gles20.slowDynamicVBO)
+ return m_IBData.count * kVBOIndexSize > _SmallBufferSizeThreshold;
+
+ return true;
+}
+
+
+void GLES2VBO::EnsureVerticesInited(bool newBuffers)
+{
+ bool isDynamic = HasStreamWithMode(kStreamModeDynamic);
+
+ int createStart = 0;
+ int createCount = 0;
+ if(m_VertexBufferID[0] == 0)
+ {
+ // initial create
+ createCount = isDynamic ? DynamicVertexBufferCount : 1;
+ newBuffers = true;
+ }
+ else if(m_VertexBufferID[1] == 0 && isDynamic)
+ {
+ // changed to dynamic
+ createStart = 1;
+ createCount = DynamicVertexBufferCount - 1;
+ newBuffers = true;
+ }
+ else if(m_VertexBufferID[1] != 0 && !isDynamic)
+ {
+ // changed to static
+ glDeregisterBufferData(DynamicVertexBufferCount-1, (GLuint*)&m_VertexBufferID[1]);
+ GLES_CHK(glDeleteBuffers(DynamicVertexBufferCount-1, (GLuint*)&m_VertexBufferID[1]));
+ ::memset(&m_VertexBufferID[1], 0x0, (DynamicVertexBufferCount-1)*sizeof(int));
+ }
+
+ m_VBOUsage = isDynamic ? GL_STREAM_DRAW : GL_STATIC_DRAW;
+ m_UsesVBO = ShouldUseVBO();
+ if(m_UsesVBO)
+ {
+ if(newBuffers && createCount)
+ GLES_CHK(glGenBuffers(createCount, (GLuint*)&m_VertexBufferID[createStart]));
+
+ // TODO: the only reason to fill whole VB is for multi-stream support
+ // TODO: somehow check if we can get away with less data copied
+ for(int i = 0, count = isDynamic ? DynamicVertexBufferCount : 1 ; i < count ; ++i)
+ {
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, m_VertexBufferID[i]));
+ if(newBuffers)
+ {
+ GLES_CHK(glBufferData(GL_ARRAY_BUFFER, m_VertexData.bufferSize, m_VertexData.buffer, m_VBOUsage));
+ glRegisterBufferData(m_VertexBufferID[i], m_VertexData.bufferSize, this);
+ }
+ else
+ {
+ GLES_CHK(glBufferSubData(GL_ARRAY_BUFFER, 0, m_VertexData.bufferSize, m_VertexData.buffer));
+ }
+ GetRealGfxDevice().GetFrameStats().AddUploadVBO(m_VertexData.bufferSize);
+ }
+
+ // now the hacky stuff - we dont actually need extra mem copy as we will recreate VBO from mesh
+ // on the other hand we still need it if we want to map stream
+ // by coincidence ;-) we will map only when having dynamic streams, and kStreamModeWritePersist (for cloth)
+ if(!HasStreamWithMode(kStreamModeDynamic) && !HasStreamWithMode(kStreamModeWritePersist))
+ UnbufferVertexData();
+ }
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ m_CurrentBufferIndex = 0;
+}
+
+void GLES2VBO::EnsureIndicesInited()
+{
+ m_IBOUsage = AreIndicesDynamic() ? GL_STREAM_DRAW : GL_STATIC_DRAW;
+ m_UsesIBO = ShouldUseIBO();
+ if(m_UsesIBO)
+ {
+ const size_t size = CalculateIndexBufferSize(m_IBData);
+
+ if(!m_IndexBufferID)
+ GLES_CHK(glGenBuffers(1, (GLuint*)&m_IndexBufferID));
+
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBufferID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, m_IBData.indices, m_IBOUsage));
+ glRegisterBufferData(m_IndexBufferID, size, this);
+ GetRealGfxDevice().GetFrameStats().AddUploadIB(size);
+ m_IBSize = size;
+
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
+ else if(m_IndexBufferID)
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferID));
+ m_IndexBufferID = 0;
+ }
+}
+
+void GLES2VBO::UpdateVertexData( const VertexBufferData& srcBuffer )
+{
+ int oldVBSize = m_VertexData.bufferSize;
+
+ BufferedVBO::BufferAllVertexData(srcBuffer);
+ std::copy(srcBuffer.streams, srcBuffer.streams + kMaxVertexStreams, m_Streams);
+ std::copy(srcBuffer.channels, srcBuffer.channels + kShaderChannelCount, m_Channels);
+
+ EnsureVerticesInited(oldVBSize < m_VertexData.bufferSize);
+}
+
+void GLES2VBO::UpdateIndexData (const IndexBufferData& buffer)
+{
+ m_IBData.indices = buffer.indices;
+ m_IBData.count = buffer.count;
+ EnsureIndicesInited();
+
+ m_ReadableIndices = buffer.indices;
+}
+
+UInt32 GLES2VBO::GetUnavailableChannels(const ChannelAssigns& channels) const
+{
+ // Figure out which channels we can't find in the streams
+ UInt32 unavailableChannels = channels.GetSourceMap();
+ for (int stream = 0; stream < kMaxVertexStreams; stream++)
+ {
+ unavailableChannels &= ~m_Streams[stream].channelMask;
+ }
+ return unavailableChannels;
+}
+
+int GLES2VBO::GetRuntimeMemorySize() const
+{
+#if ENABLE_MEM_PROFILER
+ return GetMemoryProfiler()->GetRelatedMemorySize(this) +
+ GetMemoryProfiler()->GetRelatedIDMemorySize((UInt32)this);
+#else
+ return 0;
+#endif
+}
+
+/*
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+*/
+
+DynamicGLES2VBO::DynamicGLES2VBO()
+: m_LastRenderMode (kDrawIndexedTriangles)
+, m_VB (0)
+, m_LargeVB (0)
+, m_ActiveVB (0)
+, m_IB (0)
+, m_LargeIB (0)
+, m_ActiveIB (0)
+, m_QuadsIB (0)
+, m_IndexBufferQuadsID (0)
+, m_VtxSysMemStorage(0)
+, m_VtxSysMemStorageSize(0)
+, m_IdxSysMemStorage(0)
+, m_IdxSysMemStorageSize(0)
+{
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ m_BufferChannel[i] = 0;
+ }
+
+ //chai: https://stackoverflow.com/questions/15297773/opengl-es-ios-drawing-performance-a-lot-slower-with-vbos-than-without
+ const bool willUseMemory = gGraphicsCaps.gles20.slowDynamicVBO;
+ const bool willUseOnlyMemory = willUseMemory && gGraphicsCaps.gles20.forceStaticBatchFromMem;
+
+ if(willUseMemory)
+ {
+ m_VtxSysMemStorageSize = 8096;
+ m_VtxSysMemStorage = (UInt8*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_VtxSysMemStorageSize, 32);
+ m_IdxSysMemStorageSize = 4096;
+ m_IdxSysMemStorage = (UInt16*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_IdxSysMemStorageSize, 32);
+ }
+
+ if(!willUseOnlyMemory)
+ {
+ if(!gGraphicsCaps.gles20.hasVBOOrphaning)
+ {
+ // total preallocated memory ~2MB, but it can grow!
+ // we expect mostly small VBs
+ m_VB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ARRAY_BUFFER, 8096, 32, false);
+ m_IB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ELEMENT_ARRAY_BUFFER, 4096, 16, false);
+ m_LargeVB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ARRAY_BUFFER, 32768, 32, false);
+ m_LargeIB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ELEMENT_ARRAY_BUFFER, 16384, 32, false);
+ }
+ else
+ {
+ //chai: 支持VBO orphaning,避免implicit synchronization
+ m_VB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ARRAY_BUFFER, 32768);
+ m_IB = UNITY_NEW(SharedBuffer,kMemDynamicGeometry) (GL_ELEMENT_ARRAY_BUFFER, 8096);
+ }
+ }
+}
+
+DynamicGLES2VBO::~DynamicGLES2VBO ()
+{
+ UNITY_FREE(kMemDynamicGeometry, m_VtxSysMemStorage);
+ UNITY_FREE(kMemDynamicGeometry, m_IdxSysMemStorage);
+
+ UNITY_DELETE (m_VB, kMemDynamicGeometry);
+ UNITY_DELETE (m_IB, kMemDynamicGeometry);
+ UNITY_DELETE (m_LargeVB, kMemDynamicGeometry);
+ UNITY_DELETE (m_LargeIB, kMemDynamicGeometry);
+
+ if (m_IndexBufferQuadsID)
+ {
+ glDeregisterBufferData(1, (GLuint*)&m_IndexBufferQuadsID);
+ GLES_CHK(glDeleteBuffers(1, (GLuint*)&m_IndexBufferQuadsID));
+ }
+ UNITY_FREE (kMemDynamicGeometry, m_QuadsIB);
+}
+
+void DynamicGLES2VBO::Recreate()
+{
+ m_IndexBufferQuadsID = 0;
+
+ if(m_VB) m_VB->Recreate();
+ if(m_IB) m_IB->Recreate();
+ if(m_LargeVB) m_LargeVB->Recreate();
+ if(m_LargeIB) m_LargeIB->Recreate();
+}
+
+inline void DynamicGLES2VBO::InitializeQuadsIB()
+{
+ Assert(m_IndexBufferQuadsID == 0);
+
+ if(!m_QuadsIB)
+ {
+ m_QuadsIB = (UInt16*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, VBO::kMaxQuads * 6 * kVBOIndexSize, 32);
+ Assert (m_QuadsIB);
+
+ UInt16* ib = m_QuadsIB;
+ UInt32 baseIndex = 0;
+ for( int i = 0; i < VBO::kMaxQuads; ++i )
+ {
+ ib[0] = baseIndex + 1;
+ ib[1] = baseIndex + 2;
+ ib[2] = baseIndex;
+ ib[3] = baseIndex + 2;
+ ib[4] = baseIndex + 3;
+ ib[5] = baseIndex;
+ baseIndex += 4;
+ ib += 6;
+ }
+ }
+
+ GLES_CHK(glGenBuffers( 1, (GLuint*)&m_IndexBufferQuadsID ));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBufferQuadsID));
+ GLES_CHK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, VBO::kMaxQuads * 6 * kVBOIndexSize, m_QuadsIB, GL_STATIC_DRAW));
+ glRegisterBufferData(m_IndexBufferQuadsID, VBO::kMaxQuads * 6 * kVBOIndexSize, this);
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+}
+
+// chai: 绘制函数之一
+void DynamicGLES2VBO::DrawChunk (const ChannelAssigns& channels)
+{
+#if DISABLE_GLES_CALLS
+ return;
+#endif
+
+ // just return if nothing to render
+ if( !m_LastChunkShaderChannelMask )
+ return;
+
+ AssertIf ( !m_LastChunkShaderChannelMask || !m_LastChunkStride );
+ Assert (!m_LendedChunk);
+
+ UInt8* ibPointer = (UInt8*)m_IdxSysMemStorage;
+
+ GLuint vbo = m_VtxSysMemStorage ? 0 : m_ActiveVB->GetDrawable ();
+ GLuint ibo = 0;
+ if (m_LastRenderMode == kDrawQuads)
+ {
+ if (!m_IndexBufferQuadsID)
+ InitializeQuadsIB();
+ ibo = m_IndexBufferQuadsID;
+ ibPointer = 0;
+ }
+ else if (m_ActiveIB)
+ {
+ ibo = m_ActiveIB->GetDrawable ();
+ }
+
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
+
+ switch(m_LastRenderMode)
+ {
+ case kDrawIndexedTriangles:
+ case kDrawIndexedQuads:
+ case kDrawQuads:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitiveTriangles); break;
+ case kDrawTriangleStrip:
+ case kDrawIndexedTriangleStrip:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitiveTriangleStripDeprecated); break;
+ case kDrawIndexedLines:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitiveLines); break;
+ case kDrawIndexedPoints:
+ GfxDeviceGLES20_SetDrawCallTopology(kPrimitivePoints); break;
+ }
+
+
+ VBOContainsColorGLES20(m_LastChunkShaderChannelMask & (1<<kShaderChannelColor));
+
+ //chai: 在这里设置使用的shader,并设置uniforms
+ //stack:
+ // GfxDevice::BeforeDrawCall
+ // GpuProgramsGLES20::ApplyGpuProgramES20
+ GetRealGfxDevice().BeforeDrawCall( false );
+
+ //-------------------
+ // chai: 到这里顶点数据\uniforms都设置好了
+ //-------------------
+
+ // 没有开启的channel
+ const UInt32 unavailableChannels = channels.GetSourceMap() & ~m_LastChunkShaderChannelMask;
+ UInt32 strides[kShaderChannelCount];
+ std::fill(strides, strides + kShaderChannelCount, m_LastChunkStride);
+ SetupVertexInput(channels, m_BufferChannel, strides, kDefaultChannelSizes, kDefaultChannelTypes, unavailableChannels);
+
+ DBG_LOG_GLES20("--->");
+ DBG_LOG_GLES20("--->DrawChunk");
+ DBG_LOG_GLES20("--->");
+ // draw
+#if !DISABLE_DRAW_CALLS_ONLY
+ ABSOLUTE_TIME drawStartTime = START_TIME;
+ int primCount = 0; // 图元数量
+ switch (m_LastRenderMode)
+ {
+ case kDrawIndexedTriangles:
+ Assert (m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_TRIANGLES, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices/3;
+ break;
+ case kDrawTriangleStrip:
+ Assert (m_LastChunkIndices == 0);
+ GLES_CHK(glDrawArrays(GL_TRIANGLE_STRIP, 0, m_LastChunkVertices));
+ primCount = m_LastChunkVertices-2;
+ break;
+ case kDrawQuads:
+ GLES_CHK(glDrawElements(GL_TRIANGLES, (m_LastChunkVertices/2)*3, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkVertices/2;
+ break;
+ case kDrawIndexedTriangleStrip:
+ Assert (m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_TRIANGLE_STRIP, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices-2;
+ break;
+ case kDrawIndexedLines:
+ Assert(m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_LINES, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices/2;
+ break;
+ case kDrawIndexedPoints:
+ Assert(m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_POINTS, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices;
+ break;
+ case kDrawIndexedQuads:
+ Assert(m_LastChunkIndices != 0);
+ GLES_CHK(glDrawElements(GL_TRIANGLES, m_LastChunkIndices, GL_UNSIGNED_SHORT, ibPointer));
+ primCount = m_LastChunkIndices/3;
+ break;
+ }
+ GetRealGfxDevice().GetFrameStats().AddDrawCall (primCount, m_LastChunkVertices, ELAPSED_TIME(drawStartTime));
+#endif
+}
+
+bool DynamicGLES2VBO::GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, DynamicVBO::RenderMode renderMode, void** outVB, void** outIB )
+{
+ Assert( !m_LendedChunk );
+ Assert( maxVertices < 65536 && maxIndices < 65536*3 );
+ Assert(!((renderMode == kDrawQuads) && (VBO::kMaxQuads*4 < maxVertices)));
+ DebugAssert( outVB != NULL && maxVertices > 0 );
+ DebugAssert(
+ (renderMode == kDrawIndexedQuads && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedPoints && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedLines && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangles && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawIndexedTriangleStrip && (outIB != NULL && maxIndices > 0)) ||
+ (renderMode == kDrawTriangleStrip && (outIB == NULL && maxIndices == 0)) ||
+ (renderMode == kDrawQuads && (outIB == NULL && maxIndices == 0)));
+
+ m_LendedChunk = true;
+ m_LastChunkShaderChannelMask = shaderChannelMask; //chai: 设置启用的channel
+ m_LastRenderMode = renderMode;
+
+ if( maxVertices == 0 )
+ maxVertices = 8;
+
+ m_LastChunkStride = 0;
+ for( int i = 0; i < kShaderChannelCount; ++i ) {
+ if( shaderChannelMask & (1<<i) )
+ m_LastChunkStride += VBO::GetDefaultChannelByteSize(i);
+ }
+ DebugAssert (outVB);
+ const size_t vbCapacity = Align (maxVertices * m_LastChunkStride, 1024); // if size would require growing buffer, make sure to grow in descreet steps
+
+ if(m_VtxSysMemStorage)
+ {
+ //chai: 在内存里分配vbCapacity大小的区域
+ *outVB = GetVertexMemory(vbCapacity);
+ m_ActiveVB = 0;
+ }
+ else
+ {
+ //chai: 设置写入GPU
+ m_ActiveVB = GetSharedVB (vbCapacity);
+ *outVB = m_ActiveVB->Lock (vbCapacity);
+ if (!*outVB)
+ return false;
+ }
+
+ const bool indexed = (renderMode != kDrawQuads) && (renderMode != kDrawTriangleStrip);
+ if (maxIndices && indexed)
+ {
+ DebugAssert (outIB);
+ const size_t ibCapacity = Align( maxIndices * 2, 1024); // if size would require growing buffer, make sure to grow in discrete steps
+
+ if(m_IdxSysMemStorage)
+ {
+ *outIB = GetIndexMemory(ibCapacity);
+ m_ActiveIB = 0;
+ }
+ else
+ {
+ m_ActiveIB = GetSharedIB (ibCapacity);
+ *outIB = m_ActiveIB->Lock (ibCapacity);
+ if (!*outIB)
+ return false;
+ }
+ }
+ else
+ {
+ DebugAssert (!outIB);
+ m_ActiveIB = NULL;
+ }
+
+ return true;
+}
+
+
+// chai: 将buffer的数据上传到GPU
+void DynamicGLES2VBO::ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices )
+{
+ Assert( m_LendedChunk );
+ Assert( m_LastRenderMode == kDrawIndexedTriangleStrip || m_LastRenderMode == kDrawIndexedQuads || m_LastRenderMode == kDrawIndexedPoints || m_LastRenderMode == kDrawIndexedLines || actualIndices % 3 == 0 );
+ m_LendedChunk = false;
+
+ m_LastChunkVertices = actualVertices;
+ m_LastChunkIndices = actualIndices;
+
+ // chai: 如果构造函数里没有设置m_VtxSysMemStorage,上传到GPU
+ // gles20.slowDynamicVBO = isPvrGpu || isAdrenoGpu || isMaliGpu;
+ // 以上几款GPU使用Dynamic VBO还没有从内存直接拷贝快
+ if(!m_VtxSysMemStorage)
+ {
+ DebugAssert (m_ActiveVB);
+ m_ActiveVB->Unlock (actualVertices * m_LastChunkStride);
+ }
+
+ if(!m_IdxSysMemStorage)
+ {
+ if (m_ActiveIB)
+ m_ActiveIB->Unlock (actualIndices * kVBOIndexSize);
+ }
+
+ const bool indexed = (m_LastRenderMode != kDrawQuads) && (m_LastRenderMode != kDrawTriangleStrip);
+ if( !actualVertices || (indexed && !actualIndices) )
+ {
+ m_LastChunkShaderChannelMask = 0;
+ return;
+ }
+
+ UInt8* channelOffset = m_VtxSysMemStorage;
+ for( int i = 0; i < kShaderChannelCount; ++i )
+ {
+ //chai: 如果开启了这个channel
+ if( m_LastChunkShaderChannelMask & (1<<i) )
+ {
+ //chai: 记录这个channel的数据在m_VtxSysMemStorage中的偏移后的首地址
+ m_BufferChannel[i] = channelOffset;
+ channelOffset += VBO::GetDefaultChannelByteSize(i);
+ }
+ else
+ m_BufferChannel[i] = 0;
+ }
+}
+
+//chai: 调整m_VtxSysMemStorage大小
+void* DynamicGLES2VBO::GetVertexMemory(size_t bytes)
+{
+ if(m_VtxSysMemStorageSize < bytes)
+ {
+ UNITY_FREE(kMemDynamicGeometry, m_VtxSysMemStorage);
+
+ m_VtxSysMemStorageSize = bytes;
+ m_VtxSysMemStorage = (UInt8*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_VtxSysMemStorageSize, 32);
+ }
+ return m_VtxSysMemStorage;
+}
+
+void* DynamicGLES2VBO::GetIndexMemory(size_t bytes)
+{
+ if(m_IdxSysMemStorageSize < bytes)
+ {
+ UNITY_FREE(kMemDynamicGeometry, m_IdxSysMemStorage);
+
+ m_IdxSysMemStorageSize = bytes;
+ m_IdxSysMemStorage = (UInt16*)UNITY_MALLOC_ALIGNED(kMemDynamicGeometry, m_IdxSysMemStorageSize, 32);
+ }
+ return m_IdxSysMemStorage;
+}
+
+static SharedBuffer* ChooseBestBuffer (SharedBuffer* smallBuffer, SharedBuffer* largeBuffer, size_t bytes)
+{
+ if (!largeBuffer)
+ return smallBuffer;
+
+ const size_t couldGrow = smallBuffer->GetAvailableBytes () * 2;
+ if (couldGrow >= bytes && couldGrow < largeBuffer->GetAvailableBytes () / 2)
+ return smallBuffer;
+
+ return largeBuffer;
+}
+
+SharedBuffer* DynamicGLES2VBO::GetSharedVB (size_t bytes)
+{
+ return ChooseBestBuffer (m_VB, m_LargeVB, bytes);
+}
+
+SharedBuffer* DynamicGLES2VBO::GetSharedIB (size_t bytes)
+{
+ return ChooseBestBuffer (m_IB, m_LargeIB, bytes);
+}
+
+/*
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇
+*/
+
+class SharedBuffer
+{
+public:
+ SharedBuffer(int bufferType, size_t bytesPerBlock, size_t blocks = 1, bool driverSupportsBufferOrphaning = true);
+ ~SharedBuffer();
+ void Recreate();
+
+ void* Lock(size_t bytes);
+ void Unlock(size_t actualBytes = 0);
+
+ typedef int BufferId;
+ BufferId GetDrawable() const;
+ void MarkAsDrawn(BufferId bufferId) {}
+
+ size_t GetAvailableBytes() const;
+
+private:
+ void* OrphanLock(size_t bytes);
+ void OrphanUnlock(size_t actualBytes);
+
+ void* SimpleLock(size_t bytes);
+ void SimpleUnlock(size_t actualBytes);
+
+private:
+ int m_BufferType; // GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
+ vector<BufferId> m_BufferIDs;
+ vector<size_t> m_BufferSizes;
+ bool m_DriverSupportsBufferOrphaning;
+
+ size_t m_NextBufferIndex;
+ size_t m_ReadyBufferIndex; // chai: ready for drawing
+
+ UInt8* m_TemporaryDataStorage;
+ size_t m_TemporaryDataStorageSize;
+
+ size_t m_LockedBytes;
+};
+
+inline size_t AlignTemporaryDataStorageSize (size_t size)
+{
+ // NOTE: to simplify neon optimization - pad to 64 bit (neon register size)
+ size = Align (size, 8);
+ Assert (size % 8 == 0);
+ Assert (size > 0);
+ return size;
+}
+
+SharedBuffer::SharedBuffer (int bufferType, size_t bytesPerBlock, size_t blocks, bool driverSupportsBufferOrphaning)
+: m_BufferType (bufferType)
+, m_DriverSupportsBufferOrphaning (driverSupportsBufferOrphaning)
+, m_NextBufferIndex (0)
+, m_ReadyBufferIndex (~0UL)
+, m_TemporaryDataStorage (NULL)
+, m_TemporaryDataStorageSize (0)
+, m_LockedBytes (0)
+{
+ Assert (bufferType == GL_ARRAY_BUFFER || bufferType == GL_ELEMENT_ARRAY_BUFFER);
+ Assert (blocks > 0);
+ Assert (bytesPerBlock > 0);
+ bytesPerBlock = AlignTemporaryDataStorageSize (bytesPerBlock);
+
+ if (m_DriverSupportsBufferOrphaning)
+ {
+ Assert (blocks == 1);
+ }
+
+ if (!gGraphicsCaps.gles20.hasMapbuffer || !m_DriverSupportsBufferOrphaning)
+ {
+ m_TemporaryDataStorageSize = bytesPerBlock;
+ m_TemporaryDataStorage = (UInt8*)UNITY_MALLOC_ALIGNED (kMemDynamicGeometry, m_TemporaryDataStorageSize, 32);
+ Assert (m_TemporaryDataStorage);
+ memset (m_TemporaryDataStorage, 0x00, m_TemporaryDataStorageSize);
+ }
+
+ m_BufferSizes.resize (blocks);
+ m_BufferIDs.resize (blocks);
+
+ for (size_t q = 0; q < m_BufferSizes.size(); ++q)
+ m_BufferSizes[q] = bytesPerBlock;
+
+ Recreate();
+
+ DebugAssert (m_BufferIDs.size() == m_BufferSizes.size ());
+}
+
+void SharedBuffer::Recreate()
+{
+ GLES_CHK (glGenBuffers (m_BufferIDs.size(), (GLuint*)&m_BufferIDs[0]));
+
+ if (!m_DriverSupportsBufferOrphaning)
+ {
+ for (size_t q = 0; q < m_BufferIDs.size(); ++q)
+ {
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[q]));
+ GLES_CHK(glBufferData(m_BufferType, m_BufferSizes[q], m_TemporaryDataStorage, GL_STREAM_DRAW));
+ glRegisterBufferData(m_BufferIDs[q], m_BufferSizes[q], this);
+ }
+ GLES_CHK (glBindBuffer (m_BufferType, 0));
+ }
+}
+
+SharedBuffer::~SharedBuffer ()
+{
+ glDeregisterBufferData(m_BufferIDs.size (), (GLuint*)&m_BufferIDs[0]);
+ GLES_CHK(glDeleteBuffers(m_BufferIDs.size (), (GLuint*)&m_BufferIDs[0]));
+ UNITY_FREE (kMemDynamicGeometry, m_TemporaryDataStorage);
+}
+
+void* SharedBuffer::Lock (size_t bytes)
+{
+ Assert (m_LockedBytes == 0);
+
+ m_LockedBytes = bytes;
+
+ if (m_TemporaryDataStorage && m_TemporaryDataStorageSize < bytes)
+ {
+ m_TemporaryDataStorageSize = AlignTemporaryDataStorageSize (bytes);
+ m_TemporaryDataStorage = (UInt8*)UNITY_REALLOC (kMemDynamicGeometry, m_TemporaryDataStorage, m_TemporaryDataStorageSize, 32);
+ Assert (m_TemporaryDataStorage);
+ }
+
+ // chai: 策略模式,两种策略。
+ if (m_DriverSupportsBufferOrphaning)
+ return OrphanLock (bytes);
+ else
+ return SimpleLock (bytes);
+}
+
+void SharedBuffer::Unlock (size_t actualBytes)
+{
+ if (actualBytes == 0)
+ actualBytes = m_LockedBytes;
+
+ Assert (actualBytes <= m_LockedBytes);
+
+ if (m_DriverSupportsBufferOrphaning)
+ OrphanUnlock (actualBytes);
+ else
+ SimpleUnlock (actualBytes);
+
+ DebugAssert (m_NextBufferIndex != ~0UL);
+ DebugAssert (m_ReadyBufferIndex != ~0UL);
+
+ DebugAssert (m_NextBufferIndex < m_BufferIDs.size());
+ DebugAssert (m_ReadyBufferIndex < m_BufferIDs.size());
+
+ m_LockedBytes = 0;
+}
+
+void* SharedBuffer::OrphanLock (size_t bytes)
+{
+ DebugAssert (m_DriverSupportsBufferOrphaning);
+ DebugAssert (m_BufferIDs.size () == 1);
+ DebugAssert (m_NextBufferIndex == 0);
+
+ UInt8* lockedBuffer = NULL;
+
+ if (gGraphicsCaps.gles20.hasMapbuffer)
+ {
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[0]));
+ GLES_CHK (glBufferData (m_BufferType, bytes, 0, GL_STREAM_DRAW)); // orphan old buffer, driver will allocate new storage
+ //chai: 调用glBufferData(,,NULL,)能让驱动知道接下来对此buffer的bufferdata操作不需要之前的数据
+ // 可以重新创建一个buffer,使两者可以并行
+ lockedBuffer = reinterpret_cast<UInt8*> (gGlesExtFunc.glMapBufferOES(m_BufferType, GL_WRITE_ONLY_OES));
+ GLESAssert();
+ }
+ else
+ {
+ Assert (m_TemporaryDataStorage);
+ lockedBuffer = m_TemporaryDataStorage;
+
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[0]));
+ GLES_CHK (glBufferData (m_BufferType, 0, 0, GL_STREAM_DRAW)); // orphan old buffer, driver will allocate new storage
+ }
+
+ Assert (lockedBuffer);
+ return lockedBuffer;
+}
+
+void SharedBuffer::OrphanUnlock (size_t actualBytes)
+{
+ Assert (m_NextBufferIndex == 0);
+
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[0]));
+
+ if (gGraphicsCaps.gles20.hasMapbuffer)
+ GLES_CHK(gGlesExtFunc.glUnmapBufferOES (m_BufferType));
+ else
+ GLES_CHK(glBufferData (m_BufferType, actualBytes, m_TemporaryDataStorage, GL_STREAM_DRAW));
+
+ GLES_CHK (glBindBuffer (m_BufferType, 0));
+
+ m_NextBufferIndex = 0;
+ m_ReadyBufferIndex = 0;
+}
+
+void* SharedBuffer::SimpleLock (size_t bytes)
+{
+ DebugAssert (!m_DriverSupportsBufferOrphaning);
+ DebugAssert (!m_BufferIDs.empty ());
+
+ Assert (m_TemporaryDataStorageSize >= bytes);
+ Assert (m_TemporaryDataStorage);
+ return m_TemporaryDataStorage;
+}
+
+//chai: 手动实现buffer object orphaning
+void SharedBuffer::SimpleUnlock (size_t actualBytes)
+{
+ DebugAssert (m_BufferIDs.size() == m_BufferSizes.size ());
+ // NOTE: current implementation with naively pick next buffer
+ // it is possible that GPU haven't rendered it and CPU will stall
+ // however we expect that enough buffers are allocated and stall doesn't happen in practice
+
+ m_ReadyBufferIndex = m_NextBufferIndex;
+ ++m_NextBufferIndex;
+ if (m_NextBufferIndex >= m_BufferIDs.size ())
+ m_NextBufferIndex = 0;
+ DebugAssert (m_NextBufferIndex != m_ReadyBufferIndex);
+
+ const size_t bufferIndex = m_ReadyBufferIndex;
+ DebugAssert (bufferIndex < m_BufferIDs.size());
+
+ Assert (m_TemporaryDataStorageSize >= actualBytes);
+
+ GLES_CHK (glBindBuffer (m_BufferType, m_BufferIDs[bufferIndex]));
+ if (m_BufferSizes[bufferIndex] < actualBytes)
+ {
+ GLES_CHK (glBufferData (m_BufferType, actualBytes, m_TemporaryDataStorage, GL_STREAM_DRAW));
+ m_BufferSizes[bufferIndex] = actualBytes;
+ }
+ else
+ {
+ GLES_CHK (glBufferSubData (m_BufferType, 0, actualBytes, m_TemporaryDataStorage));
+ }
+ GLES_CHK (glBindBuffer (m_BufferType, 0));
+}
+
+size_t SharedBuffer::GetAvailableBytes () const
+{
+ return m_BufferSizes[m_NextBufferIndex];
+}
+
+SharedBuffer::BufferId SharedBuffer::GetDrawable () const
+{
+ DebugAssert (m_ReadyBufferIndex != ~0UL);
+ DebugAssert (m_ReadyBufferIndex < m_BufferIDs.size());
+ if (m_ReadyBufferIndex >= m_BufferIDs.size ())
+ return 0;
+
+ return m_BufferIDs[m_ReadyBufferIndex];
+}
+
+
+// WARNING: this is just a temp solution, as we really need to clean up all this mess
+
+static SharedBuffer* lockedSharedBuffer = NULL;
+void* LockSharedBufferGLES20 (int bufferType, size_t bytes, bool forceBufferObject)
+{
+ Assert (bufferType == GL_ARRAY_BUFFER || bufferType == GL_ELEMENT_ARRAY_BUFFER);
+ DynamicGLES2VBO& dynamicVBO = static_cast<DynamicGLES2VBO&> (GetRealGfxDevice ().GetDynamicVBO ());
+
+ if(gGraphicsCaps.gles20.slowDynamicVBO && !forceBufferObject)
+ {
+ if (bufferType == GL_ARRAY_BUFFER)
+ return dynamicVBO.GetVertexMemory(bytes);
+ else if (bufferType == GL_ELEMENT_ARRAY_BUFFER)
+ return dynamicVBO.GetIndexMemory(bytes);
+ }
+ else
+ {
+ DebugAssert (lockedSharedBuffer == NULL);
+ if (bufferType == GL_ARRAY_BUFFER)
+ lockedSharedBuffer = dynamicVBO.GetSharedVB (bytes);
+ else if (bufferType == GL_ELEMENT_ARRAY_BUFFER)
+ lockedSharedBuffer = dynamicVBO.GetSharedIB (bytes);
+
+ Assert (lockedSharedBuffer);
+ return lockedSharedBuffer->Lock (bytes);
+ }
+
+ return 0;
+}
+
+int UnlockSharedBufferGLES20 (size_t actualBytes, bool forceBufferObject)
+{
+ if(gGraphicsCaps.gles20.slowDynamicVBO && !forceBufferObject)
+ {
+ return 0;
+ }
+ else
+ {
+ Assert (lockedSharedBuffer);
+ lockedSharedBuffer->Unlock (actualBytes);
+ const int bufferId = lockedSharedBuffer->GetDrawable ();
+ lockedSharedBuffer = NULL;
+ return bufferId;
+ }
+}
+
+#if NV_STATE_FILTERING
+ #ifdef glDeleteBuffers
+ #undef glDeleteBuffers
+ #endif
+ #ifdef glBindBuffer
+ #undef glBindBuffer
+ #endif
+ #ifdef glVertexAttribPointer
+ #undef glVertexAttribPointer
+ #endif
+#endif
+
+#endif // GFX_SUPPORTS_OPENGLES20
diff --git a/Runtime/GfxDevice/opengles20/VBOGLES20.h b/Runtime/GfxDevice/opengles20/VBOGLES20.h
new file mode 100644
index 0000000..62ce55d
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/VBOGLES20.h
@@ -0,0 +1,147 @@
+#ifndef VBO_GLES20_H
+#define VBO_GLES20_H
+
+#include "Runtime/Shaders/BufferedVBO.h"
+#include "IncludesGLES20.h"
+#include <map>
+
+class GLES2VBO : public BufferedVBO {
+public:
+ GLES2VBO();
+ virtual ~GLES2VBO ();
+
+ virtual void UpdateVertexData( const VertexBufferData& buffer );
+ virtual void UpdateIndexData (const IndexBufferData& buffer);
+ virtual void DrawVBO (const ChannelAssigns& channels, UInt32 firstIndexByte, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 firstVertex, UInt32 vertexCount );
+ virtual void DrawCustomIndexed( const ChannelAssigns& channels, void* indices, UInt32 indexCount,
+ GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount );
+
+ virtual bool MapVertexStream( VertexStreamData& outData, unsigned stream );
+ virtual void UnmapVertexStream( unsigned stream );
+
+ virtual void Cleanup();
+ virtual void Recreate();
+ virtual bool IsVertexBufferLost() const;
+ virtual bool IsIndexBufferLost() const;
+ virtual void MarkBuffersLost();
+
+ virtual bool IsUsingSourceVertices() const { return m_VertexBufferID[0] == 0; }
+ virtual bool IsUsingSourceIndices() const { return m_IndexBufferID == 0; }
+
+ virtual int GetRuntimeMemorySize() const;
+
+private:
+
+ void DrawInternal(int vertexBufferID, int indexBufferID, const ChannelAssigns& channels, void* indices, UInt32 indexCount, GfxPrimitiveType topology, UInt32 vertexRangeBegin, UInt32 vertexRangeEnd, UInt32 drawVertexCount);
+
+ UInt32 GetUnavailableChannels(const ChannelAssigns& channels) const;
+
+ ChannelInfoArray m_Channels;
+ IndexBufferData m_IBData;
+ enum { DynamicVertexBufferCount = 3 };
+ int m_VertexBufferID[DynamicVertexBufferCount];
+ int m_CurrentBufferIndex;
+ int m_IndexBufferID;
+ int m_IBSize;
+ void* m_ReadableIndices;
+
+ int m_VBOUsage;
+ int m_IBOUsage;
+
+
+ bool m_UsesVBO;
+ bool m_UsesIBO;
+
+ // for now these are only called on UpdateVertexData/UpdateIndexData
+ // so they are extracted only for convinience
+ bool ShouldUseVBO();
+ bool ShouldUseIBO();
+
+ void EnsureVerticesInited(bool newBuffers);
+ void EnsureIndicesInited();
+};
+
+class SharedBuffer;
+
+// Usage:
+// 1) GetChunk
+// if this returns false, don't use data pointers, bail out
+// 2) fill with data
+// 3) ReleaseChunk
+// 4) DrawChunk (possibly multiple times)
+//
+// The key is that drawing must happen immediately after filling the chunk, because the next
+// GetChunk might destroy the previous chunk's data. So never count on chunks being persistent.
+class DynamicGLES2VBO : public DynamicVBO
+{
+public:
+ DynamicGLES2VBO();
+ ~DynamicGLES2VBO();
+
+ virtual bool GetChunk( UInt32 shaderChannelMask, UInt32 maxVertices, UInt32 maxIndices, RenderMode renderMode, void** outVB, void** outIB );
+ virtual void ReleaseChunk( UInt32 actualVertices, UInt32 actualIndices );
+ virtual void DrawChunk (const ChannelAssigns& channels);
+
+ virtual void Recreate();
+
+ SharedBuffer* GetSharedVB (size_t bytes);
+ SharedBuffer* GetSharedIB (size_t bytes);
+
+ void* GetVertexMemory(size_t bytes);
+ void* GetIndexMemory(size_t bytes);
+
+private:
+ void InitializeQuadsIB();
+
+private:
+ //chai: 记录channel在m_VtxSysMemStorage中的各个channel的首地址
+ void* m_BufferChannel[kShaderChannelCount];
+ RenderMode m_LastRenderMode;
+
+ //chai: 记录开启的channel
+ //UInt32 m_LastChunkShaderChannelMask;
+
+
+ SharedBuffer* m_VB;
+ SharedBuffer* m_LargeVB;
+ SharedBuffer* m_ActiveVB;
+ SharedBuffer* m_IB;
+ SharedBuffer* m_LargeIB;
+ SharedBuffer* m_ActiveIB;
+
+ UInt16* m_QuadsIB;
+ int m_IndexBufferQuadsID;
+
+ UInt8* m_VtxSysMemStorage;
+ unsigned m_VtxSysMemStorageSize;
+
+ UInt16* m_IdxSysMemStorage;
+ unsigned m_IdxSysMemStorageSize;
+};
+
+void InvalidateVertexInputCacheGLES20();
+// WARNING: forceBufferObject is just a temp solution, as we really need to clean up all this mess
+// we need it for bigger dynamic vbos
+void* LockSharedBufferGLES20 (int bufferType, size_t bytes, bool forceBufferObject=false);
+int UnlockSharedBufferGLES20 (size_t actualBytes = 0, bool forceBufferObject=false);
+
+#define GL_VERTEX_ARRAY 0
+#define GL_COLOR_ARRAY 1
+#define GL_NORMAL_ARRAY 2
+#define GL_TEXTURE_ARRAY0 3
+#define GL_TEXTURE_ARRAY1 GL_TEXTURE_ARRAY0 + 1
+#define GL_TEXTURE_ARRAY2 GL_TEXTURE_ARRAY0 + 2
+#define GL_TEXTURE_ARRAY3 GL_TEXTURE_ARRAY0 + 3
+#define GL_TEXTURE_ARRAY4 GL_TEXTURE_ARRAY0 + 4
+#define GL_TEXTURE_ARRAY5 GL_TEXTURE_ARRAY0 + 5
+#define GL_TEXTURE_ARRAY6 GL_TEXTURE_ARRAY0 + 6
+#define GL_TEXTURE_ARRAY7 GL_TEXTURE_ARRAY0 + 7
+
+#if NV_STATE_FILTERING
+void filteredBindBufferGLES20(GLenum target, GLuint buffer,bool isImmediate=false);
+void filteredVertexAttribPointerGLES20(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
+void StateFiltering_InvalidateVBOCacheGLES20();
+#endif
+
+#endif
diff --git a/Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp b/Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp
new file mode 100644
index 0000000..a72f01a
--- /dev/null
+++ b/Runtime/GfxDevice/opengles20/_DebugStuffGLES20.cpp
@@ -0,0 +1,438 @@
+#include "UnityPrefix.h"
+#include "AssertGLES20.h"
+#include "IncludesGLES20.h"
+#include "Runtime/Math/Matrix4x4.h"
+#include "_DebugStuffGLES20.h"
+#include "VBOGLES20.h"
+
+void PrintMatrix(const char* name, const Matrix4x4f& m)
+{
+ printf_console("Outputing:%s\n------------\n"
+ "mat4 mat = (%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f,\n"
+ "%.2f, %.2f, %.2f, %2f);\n\n",name,
+ m[0], m[1], m[2], m[3],
+ m[4], m[5], m[6], m[7],
+ m[8], m[9], m[10], m[11],
+ m[12], m[13], m[14], m[15]);
+}
+void _Debug::MiniLoopGLES20()
+{
+
+ #define VERTEX_ARRAY 0
+ float pfIdentity[] =
+ {
+ 1.0f,0.0f,0.0f,0.0f,
+ 0.0f,1.0f,0.0f,0.0f,
+ 0.0f,0.0f,1.0f,0.0f,
+ 0.0f,0.0f,0.0f,1.0f
+ };
+
+ // Fragment and vertex shaders code
+ char* pszFragShader = "\
+ void main (void)\
+ {\
+ gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\
+ }";
+ char* pszVertShader = "\
+ attribute highp vec3 myVertex;\
+ uniform mediump mat4 myPMVMatrix;\
+ void main(void)\
+ {\
+ gl_Position = myPMVMatrix * vec4(myVertex,1.0);\
+ }";
+
+
+ GLuint uiFragShader, uiVertShader; /* Used to hold the fragment and vertex shader handles */
+ GLuint uiProgramObject; /* Used to hold the program handle (made out of the two previous shaders */
+
+ // Create the fragment shader object
+ uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ // Load the source code into it
+ glShaderSource(uiFragShader, 1, (const char**)&pszFragShader, NULL);
+
+ // Compile the source code
+ glCompileShader(uiFragShader);
+
+ // Check if compilation succeeded
+ GLint bShaderCompiled;
+ bShaderCompiled = 0;
+ glGetShaderiv(uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);
+
+ if (!bShaderCompiled)
+ {
+#ifndef NO_GDI
+ // An error happened, first retrieve the length of the log message
+ int i32InfoLogLength, i32CharsWritten;
+ glGetShaderiv(uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
+
+ // Allocate enough space for the message and retrieve it
+ char* pszInfoLog = new char[i32InfoLogLength];
+ glGetShaderInfoLog(uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
+
+ printf_console(pszInfoLog);
+ // Displays the error in a dialog box
+ delete[] pszInfoLog;
+#endif
+ return;
+ }
+
+ // Loads the vertex shader in the same way
+ uiVertShader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(uiVertShader, 1, (const char**)&pszVertShader, NULL);
+ glCompileShader(uiVertShader);
+ glGetShaderiv(uiVertShader, GL_COMPILE_STATUS, &bShaderCompiled);
+ if (!bShaderCompiled)
+ {
+#ifndef NO_GDI
+ int i32InfoLogLength, i32CharsWritten;
+ glGetShaderiv(uiVertShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
+ char* pszInfoLog = new char[i32InfoLogLength];
+ glGetShaderInfoLog(uiVertShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+#endif
+ return;
+ }
+
+ // Create the shader program
+ uiProgramObject = glCreateProgram();
+ // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
+ glBindAttribLocation(uiProgramObject, VERTEX_ARRAY, "myVertex");
+ // Attach the fragment and vertex shaders to it
+ glAttachShader(uiProgramObject, uiFragShader);
+ glAttachShader(uiProgramObject, uiVertShader);
+
+
+
+ // Link the program
+ glLinkProgram(uiProgramObject);
+
+ // Check if linking succeeded in the same way we checked for compilation success
+ GLint bLinked;
+ glGetProgramiv(uiProgramObject, GL_LINK_STATUS, &bLinked);
+ if (!bLinked)
+ {
+#ifndef NO_GDI
+ int i32InfoLogLength, i32CharsWritten;
+ glGetProgramiv(uiProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
+ char* pszInfoLog = new char[i32InfoLogLength];
+ glGetProgramInfoLog(uiProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+#endif
+ return;
+ }
+
+
+ // Actually use the created program
+ glUseProgram(uiProgramObject);
+ GLESAssert();
+
+ // Sets the clear color.
+ // The colours are passed per channel (red,green,blue,alpha) as float values from 0.0 to 1.0
+ glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
+ GLESAssert();
+
+ // We're going to draw a triangle to the screen so create a vertex buffer object for our triangle
+ GLuint ui32Vbo; // Vertex buffer object handle
+
+ // Interleaved vertex data
+ GLfloat afVertices[] = { -0.1f,-0.4f,0.0f, // Position
+ 0.4f ,-0.4f,0.0f,
+ 0.0f ,0.4f ,0.0f};
+
+ // Generate the vertex buffer object (VBO)
+ glGenBuffers(1, &ui32Vbo);
+ GLESAssert();
+
+ // Bind the VBO so we can fill it with data
+ glBindBuffer(GL_ARRAY_BUFFER, ui32Vbo);
+ GLESAssert();
+
+ // Set the buffer's data
+ unsigned int uiSize = 3 * (sizeof(GLfloat) * 3); // Calc afVertices size (3 vertices * stride (3 GLfloats per vertex))
+ glBufferData(GL_ARRAY_BUFFER, uiSize, afVertices, GL_STATIC_DRAW);
+ GLESAssert();
+
+ glClearDepthf(1.0f);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+ glDisable(GL_CULL_FACE);
+ printf_console("Loop launched\n");
+ while (1)
+ {
+ glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ GLESAssert();
+
+
+ /*
+ Bind the projection model view matrix (PMVMatrix) to
+ the associated uniform variable in the shader
+ */
+
+ // First gets the location of that variable in the shader using its name
+ int i32Location = glGetUniformLocation(uiProgramObject, "myPMVMatrix");
+
+ // Then passes the matrix to that variable
+ glUniformMatrix4fv( i32Location, 1, GL_FALSE, pfIdentity);
+ GLESAssert();
+
+ /*
+ Enable the custom vertex attribute at index VERTEX_ARRAY.
+ We previously binded that index to the variable in our shader "vec4 MyVertex;"
+ */
+ glEnableVertexAttribArray(VERTEX_ARRAY);
+ GLESAssert();
+
+ // Sets the vertex data to this attribute index
+ glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
+ /*
+ Draws a non-indexed triangle array from the pointers previously given.
+ This function allows the use of other primitive types : triangle strips, lines, ...
+ For indexed geometry, use the function glDrawElements() with an index list.
+ */
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ GLESAssert();
+
+ void PresentContextGLES();
+ PresentContextGLES();
+ }
+}
+int _Debug::CreateDefaultShader (bool texturing)
+{
+ char* strVertexShader = NULL;
+ char* strFragmentShader = NULL;
+ if (texturing)
+ {
+ strVertexShader =
+ "attribute highp vec3 _glesVertex;\n"
+ "attribute highp vec4 _glesUV0;\n"
+ "uniform mediump mat4 _glesMVP;\n"
+ "varying mediump vec2 _glesVarUV0;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = _glesMVP * vec4(_glesVertex,1.0);\n"
+ " _glesVarUV0 = _glesUV0.st;\n"
+ "}";
+
+ strFragmentShader =
+ "uniform sampler2D texDiffuse;\n"
+ "varying mediump vec2 _glesVarUV0;\n"
+ "void main (void)\n"
+ "{\n"
+ " gl_FragColor = texture2D(texDiffuse, _glesVarUV0);\n"
+ "}";
+ }
+ else
+ {
+ strVertexShader =
+ "attribute highp vec3 _glesVertex;\n"
+ "uniform mediump mat4 _glesMVP;\n"
+ "void main(void)\n"
+ "{\n"
+ " gl_Position = _glesMVP * vec4(_glesVertex,1.0);\n"
+ "}";
+
+
+ strFragmentShader =
+ "void main (void)\n"
+ "{\n"
+ " gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\n"
+ "}";
+
+ }
+ return CreateShader(strVertexShader, strFragmentShader);
+
+}
+int _Debug::CreateShader(const char* vertexShader, const char* fragmenShader)
+{
+ GLuint vertShader, fragShader;
+ GLuint shader;
+
+ GLint compiled, linked;
+
+ //Vertex shader
+ GLES_CHK(vertShader = glCreateShader(GL_VERTEX_SHADER));
+ GLES_CHK(glShaderSource(vertShader, 1, (const char**)&vertexShader, NULL));
+ GLES_CHK(glCompileShader(vertShader));
+
+ compiled = 0;
+ GLES_CHK(glGetShaderiv(vertShader, GL_COMPILE_STATUS, &compiled));
+
+ if (!compiled)
+ {
+ int i32InfoLogLength, i32CharsWritten;
+ GLES_CHK(glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength));
+ char* pszInfoLog = new char[i32InfoLogLength];
+ GLES_CHK(glGetShaderInfoLog(vertShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog));
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+ return 0;
+ }
+
+ //Fragment shader
+ GLES_CHK(fragShader = glCreateShader(GL_FRAGMENT_SHADER));
+ GLES_CHK(glShaderSource(fragShader, 1, (const char**)&fragmenShader, NULL));
+ GLES_CHK(glCompileShader(fragShader));
+
+ compiled = 0;
+ GLES_CHK(glGetShaderiv(fragShader, GL_COMPILE_STATUS, &compiled));
+
+ if (!compiled)
+ {
+ int i32InfoLogLength, i32CharsWritten;
+ GLES_CHK(glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength));
+ char* pszInfoLog = new char[i32InfoLogLength];
+ GLES_CHK(glGetShaderInfoLog(fragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog));
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+ return 0;
+ }
+
+ // Final Step, attacha and link shaders
+ shader = glCreateProgram();
+ (glBindAttribLocation(shader, GL_VERTEX_ARRAY, "_glesVertex"));
+ (glBindAttribLocation(shader, GL_TEXTURE_ARRAY0, "_glesUV0"));
+
+ GLES_CHK(glAttachShader(shader, fragShader));
+ GLES_CHK(glAttachShader(shader, vertShader));
+ GLES_CHK(glLinkProgram(shader));
+
+ linked = 0;
+ GLES_CHK(glGetProgramiv(shader, GL_LINK_STATUS, &linked));
+ if (!linked)
+ {
+ int i32InfoLogLength, i32CharsWritten;
+ GLES_CHK(glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &i32InfoLogLength));
+ char* pszInfoLog = new char[i32InfoLogLength];
+ GLES_CHK(glGetProgramInfoLog(shader, i32InfoLogLength, &i32CharsWritten, pszInfoLog));
+ printf_console(pszInfoLog);
+ delete[] pszInfoLog;
+ return 0;
+ }
+ return shader;
+}
+
+int _Debug::CreateDefaultVBO (bool texCoords)
+{
+ float verts[] =
+ {
+ -0.5f, -0.5f, 0.0f,
+ 0.5f, -0.5f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ -0.5f, -0.5f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ -0.5f, 0.5f, 0.0f
+ };
+
+ float vertsUV[] =
+ {
+ -0.5f, -0.5f, 0.0f,
+ 0.0f, 0.0f,
+ 0.5f, -0.5f, 0.0f,
+ 1.0f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ 1.0f, 1.0f,
+ -0.5f, -0.5f, 0.0f,
+ 0.0f, 0.0f,
+ 0.5f, 0.5f, 0.0f,
+ 1.0f, 1.0f,
+ -0.5f, 0.5f, 0.0f,
+ 0.0f, 1.0f,
+ };
+
+ if (texCoords)
+ {
+ return CreateVBO(vertsUV, 6, sizeof(float) * 5);
+ }
+ else
+ {
+ return CreateVBO(vertsUV, 6, sizeof(float) * 3);
+ }
+}
+
+
+int _Debug::CreateVBO(const float* data, int vertexCount, int vertexSize)
+{
+ GLuint vbo;
+ GLES_CHK(glGenBuffers(1, &vbo));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vbo));
+ GLES_CHK(glBufferData(GL_ARRAY_BUFFER, vertexSize * vertexCount, data, GL_STATIC_DRAW));
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+ return vbo;
+}
+
+void _Debug::SetUniformMatrix4x4f(int shaderID, const char* name, const Matrix4x4f& mat)
+{
+ int loc ;
+ GLES_CHK(loc = glGetUniformLocation(shaderID, name));
+
+ if (loc != -1)GLES_CHK(glUniformMatrix4fv(loc, 1, GL_FALSE, mat.GetPtr()));
+ else printf_console("Failed to find uniform %s\n", name);
+}
+void _Debug::SetUniformSample2D (int shaderID, const char* name, int texUnit)
+{
+ int loc ;
+ GLES_CHK(loc = glGetUniformLocation(shaderID, name));
+
+ if (loc != -1)GLES_CHK(glUniform1i(loc, texUnit));
+ else printf_console("Failed to find uniform %s\n", name);
+}
+void _Debug::UseShader(int shaderID)
+{
+ GLES_CHK(glUseProgram(shaderID));
+}
+void _Debug::DrawVBO(int vboID, int vertexCount, bool texCoords)
+{
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, vboID));
+ if (texCoords)
+ {
+ glEnableVertexAttribArray(GL_VERTEX_ARRAY);
+ glVertexAttribPointer(GL_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, 0);
+ glEnableVertexAttribArray(GL_TEXTURE_ARRAY0);
+ glVertexAttribPointer(GL_TEXTURE_ARRAY0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (void*) (3 * sizeof(GLfloat)));
+
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+
+ GLES_CHK(glDisableVertexAttribArray(GL_VERTEX_ARRAY));
+ GLES_CHK(glDisableVertexAttribArray(GL_TEXTURE_ARRAY0));
+ }
+ else
+ {
+ GLES_CHK(glEnableVertexAttribArray(GL_VERTEX_ARRAY));
+ GLES_CHK(glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0));
+ GLES_CHK(glDrawArrays(GL_TRIANGLES, 0, vertexCount));
+ GLES_CHK(glDisableVertexAttribArray(GL_VERTEX_ARRAY));
+ }
+ GLES_CHK(glBindBuffer(GL_ARRAY_BUFFER, 0));
+}
+void _Debug::DisplayTextureMiniLoop(int textureID)
+{
+ bool texCoords = true;
+ int vbo = CreateDefaultVBO(texCoords);
+ int shader = CreateDefaultShader(texCoords);
+ GLES_CHK(glDisable(GL_DEPTH_TEST));
+ GLES_CHK(glDisable(GL_CULL_FACE));
+ GLES_CHK(glDisable(GL_BLEND));
+ GLES_CHK(glBindFramebuffer (GL_FRAMEBUFFER, 0));
+ while (1)
+ {
+ GLES_CHK(glClearColor(1.0f, 1.0f, 0.0f, 1.0f));
+ GLES_CHK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
+ GLES_CHK(glActiveTexture(GL_TEXTURE0));
+ GLES_CHK(glBindTexture(GL_TEXTURE_2D, textureID));
+
+ UseShader(shader);
+ SetUniformMatrix4x4f(shader, "_glesMVP", Matrix4x4f::identity);
+ SetUniformSample2D(shader, "texDiffuse", 0);
+ DrawVBO(vbo, 6, texCoords);
+
+ void PresentContextGLES();
+ PresentContextGLES();
+ }
+} \ No newline at end of file