diff options
Diffstat (limited to 'Runtime/GfxDevice/opengles20')
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(¶ms, -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 |