I asked this in the iPhone forum, but thought I'd give it a chance here instead. It seems Unity is setting precision specifiers automatically in GLSL ES shaders with no way of overriding (VERTEX_P as higp, FRAGMENT_P as mediump). This makes it really hard to squeeze out that extra bit of performance (since there is a 2x theoretical throughput between each level according to the SGX specification). Is there any way to manually override this?
You can redefine VERTEX_P and FRAGMENT_P by simply defining them at the top of your shader file. Example: Code (csharp): #define VERTEX_P mediump #define FRAGMENT_P lowp
Adding the VERTEX_P and FRAGMENT_P defines produce pink output instead of the regular super-simple textured output normally produced by the shader. Works fine in the editor, but turns pink when run on devices (iPad iPhone4). When removing VERTEX_P and FRAGMENT_P, the shader works fine on the devices. It makes no difference if I use lowp, normalp or even highp. Code (csharp): Shader "OGLES2_Test" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "Queue" = "Geometry" } Pass { GLSLPROGRAM [COLOR="red"]#define VERTEX_P highp #define FRAGMENT_P lowp [/COLOR] #ifdef VERTEX varying vec2 uv; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; uv = gl_MultiTexCoord0.xy; } #endif #ifdef FRAGMENT uniform sampler2D _MainTex; varying vec2 uv; void main() { gl_FragColor = texture2D(_MainTex, uv); } #endif ENDGLSL } } } Any ideas?
Included is the LOG file (from running on iPhone4, but iPad give the same output). Seems like something is wrong with the setup for fragment shader and how VERTEX_P is defined. Code (csharp): GL_APPLE_framebuffer_multisample GL_APPLE_rgb_422 GL_APPLE_texture_format_BGRA8888 GL_APPLE_texture_max_level GL_EXT_blend_minmax GL_EXT_discard_framebuffer GL_EXT_read_format_bgra GL_EXT_shader_texture_lod GL_EXT_texture_filter_anisotropic GL_IMG_read_format GL_IMG_texture_compression_pvrtc GL_OES_depth24 GL_OES_depth_texture GL_OES_fbo_render_mipmap GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_OES_standard_derivatives GL_OES_texture_float GL_OES_texture_half_float GL_OES_vertex_array_object Creating OpenGLES2.0 graphics device [COLOR="red"]-------- GLSL info log: ERROR: 0:27: 'VERTEX_P' : syntax error syntax error ERROR: Parser found no code to compile in source strings. Failed to compile vertex shader [/COLOR] #define SHADER_API_OGLES20 1 #define tex2D texture2D #define highp_vec2 highp vec2 #define mediump_vec2 mediump vec2 #define lowp_vec2 lowp vec2 #define highp_vec3 highp vec3 #define mediump_vec3 mediump vec3 #define lowp_vec3 lowp vec3 #define highp_vec4 highp vec4 #define mediump_vec4 mediump vec4 #define lowp_vec4 lowp vec4 #define highp_mat2 highp mat2 #define mediump_mat2 mediump mat2 #define lowp_mat2 lowp mat2 #define highp_mat3 highp mat3 #define mediump_mat3 mediump mat3 #define lowp_mat3 lowp mat3 #define highp_mat4 highp mat4 #define mediump_mat4 mediump mat4 #define lowp_mat4 lowp mat4 #define highp_float highp float #define mediump_float mediump float #define lowp_float lowp float #define gl_ModelViewProjectionMatrix glstate_matrix_mvp uniform highp_mat4 glstate_matrix_mvp; #define gl_Vertex _glesVertex attribute VERTEX_P vec4 _glesVertex; #define gl_MultiTexCoord0 _glesMultiTexCoord0 attribute VERTEX_P vec4 _glesMultiTexCoord0; #line 9 #define VERTEX_P highp #define FRAGMENT_P lowp varying VERTEX_P vec2 uv; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; uv = gl_MultiTexCoord0.xy; } (Filename: /Applications/buildAgent/work/68355d6e5d19d587/Projects/../Runtime/GfxDevice/opengles20/GpuProgramsGLES20.cpp Line: 702) Unsupported: OGLES2_Test Have anyone been able to successfully use VERTEX_P and FRAGMENT_P in a OGLES2 shader?
What if you write #define VERTEX_P highp after #ifdef VERTEX and #define FRAGMENT_P lowp after #ifdef FRAGMENT
I just posted to the iPhone beta list a FIX for those who want to experiment with precision modifiers in their EGLES2 shaders: Just include the following right after your GLSLPROGRAM statement, and do not include VERTEX_P nor FRAGMENT_P statements. Then, instead of using float, vec2, vec3, vec4, mat2, mat3, mat4 - use the new types instead. The shaders will compile and work BOTH in the editor and on the device. Without using precision modifiers, my sample scene (4 textures mixed together, vertex lighting-diffuse + separate specular) ran on an iPhone 4 HD at 17FPS with 100% render utilization, 17% tiler utilization according to the OpenGL ES profiler. After reducing the precision where I could (like texture colors, texture mixer values, etc), the same scene now ran at 30FPS, with 61% render utilization and 17% tiler utilization - that is a 2.5x performance increase without touching any shader logic! Code (csharp): #ifdef SHADER_API_OGLES20 #define _highp_vec2 highp vec2 #define _mediump_vec2 mediump vec2 #define _lowp_vec2 lowp vec2 #define _highp_vec3 highp vec3 #define _mediump_vec3 mediump vec3 #define _lowp_vec3 lowp vec3 #define _highp_vec4 highp vec4 #define _mediump_vec4 mediump vec4 #define _lowp_vec4 lowp vec4 #define _highp_mat2 highp mat2 #define _mediump_mat2 mediump mat2 #define _lowp_mat2 lowp mat2 #define _highp_mat3 highp mat3 #define _mediump_mat3 mediump mat3 #define _lowp_mat3 lowp mat3 #define _highp_mat4 highp mat4 #define _mediump_mat4 mediump mat4 #define _lowp_mat4 lowp mat4 #define _highp_float highp float #define _mediump_float mediump float #define _lowp_float lowp float #else #define _highp_vec2 vec2 #define _mediump_vec2 vec2 #define _lowp_vec2 vec2 #define _highp_vec3 vec3 #define _mediump_vec3 vec3 #define _lowp_vec3 vec3 #define _highp_vec4 vec4 #define _mediump_vec4 vec4 #define _lowp_vec4 vec4 #define _highp_mat2 mat2 #define _mediump_mat2 mat2 #define _lowp_mat2 mat2 #define _highp_mat3 mat3 #define _mediump_mat3 mat3 #define _lowp_mat3 mat3 #define _highp_mat4 mat4 #define _mediump_mat4 mat4 #define _lowp_mat4 mat4 #define _highp_float float #define _mediump_float float #define _lowp_float float #endif Ps. Credit should really go to the unity devs - this is almost the same set of defines that unity3 adds to your shader code when you define VERTEX_P and FRAGMENT_P - except there is a glitch which makes everything crash.
I don't see a point in this, because you can define the precision of individual structures. For example, this works: Code (csharp): varying lowp vec2 uv; But not having a global way to set precision makes the code quite annoying to read and write. Is there a solution yet? Is this a bug that needs to be reported? This code sample from Apple just causes an error in Unity: Code (csharp): default precision highp;
Most of the thread above is not relevant to Unity 3.2 and later (all that HIGH_P stuff etc.). So... Jessy, can you post a full shader that is causing problems?
Because of surface shaders? It's not a problem with a particular shader. The problem is that I generally want to work at a certain precision, and Unity, as far as I know, forces me to put a precision specifier in front of everything unless I'm good with the defaults, which wastes time and space, because I generally am not. Here's an example where a client had no time to do this stuff in Photoshop, and needed the controls in the shader. Writing "mediump" only once would have been appreciated (this is the first time I had a need for > lowp in the fragment shader): Code (csharp): Shader "Custom/Lightmap Levels + Mirrored Hemispheremap" { Properties { _Color ("Main Color", Color) = (1,1,1) _LM_OutputBlack ("Shadow Color", Color) = (0,0,0) _LM_InputBlack ("Shadows", Range(0,1)) = 0 _LM_InputWhite ("Highlights", Range(0,1)) = 1 _LM_Intensity ("Lightmap Intensity", Float) = 1 _Mixer ("Lightmap Channel Mixer (for reflection)", Color) = (0.222, 0.706, 0.072) _ReflectionIntensity ("Reflection Intensity", Float) = 1 _MainTex ("Base (A=Specular)", 2D) = "white" _Hemisphere ("Hemispheremap", 2D) = "black" } SubShader {Pass { GLSLPROGRAM varying mediump vec3 lightmapScale, lightmapOffset; varying mediump vec2 uv_mainTex, uv_lightmap, uv_hemisphere; #ifdef VERTEX uniform mediump vec4 _MainTex_ST, unity_LightmapST; uniform mediump vec3 _Color, _LM_OutputBlack; uniform mediump float _LM_Intensity, _LM_InputWhite, _LM_InputBlack; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; uv_mainTex = gl_MultiTexCoord0.xy * _MainTex_ST.xy + _MainTex_ST.zw; uv_lightmap = gl_MultiTexCoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw; // The same texture coordinates are used, // whether the vector points toward or away from camera-forward. uv_hemisphere = reflect( normalize((gl_ModelViewMatrix * gl_Vertex).xyz), gl_NormalMatrix * gl_Normal ).xy * .5 + .5; // Scale+translate to fit in 0-1 texture space. // Input Levels: (lightmap - _LM_InputBlack) / (_LM_InputWhite - _LM_InputBlack) // Output Levels: Input Levels * (_Color * _LM_Intensity - _LM_OutputBlack) + _LM_OutputBlack // Refactored for fragment performance, but clamp is necessary there. lightmapScale = (_Color * _LM_Intensity - _LM_OutputBlack) / (_LM_InputWhite - _LM_InputBlack); lightmapOffset = -_LM_InputBlack * lightmapScale + _LM_OutputBlack; } #endif #ifdef FRAGMENT uniform mediump sampler2D _MainTex, unity_Lightmap, _Hemisphere; uniform mediump vec3 _Mixer, _LM_OutputBlack; uniform mediump float _ReflectionIntensity; void main() { mediump vec4 mainTex_spec = texture2D(_MainTex, uv_mainTex); mediump vec3 lightmap = max( texture2D(unity_Lightmap, uv_lightmap).rgb * lightmapScale + lightmapOffset, _LM_OutputBlack); mediump vec3 reflection = mainTex_spec.a * dot(lightmap, _Mixer) * _ReflectionIntensity * texture2D(_Hemisphere, uv_hemisphere).rgb; gl_FragColor.rgb = mainTex_spec.rgb * lightmap + reflection; } #endif ENDGLSL }} }
No, because we reworked the way we do precision qualifiers in the shaders. Those VERTEX_P etc. macros are gone as far as I remember. Ok, so just that I understand it correctly: the problem is that you can't put int "precision float mediump;" (or any other global precision statement) in your own GLSL shaders? If that's the case then indeed we should fix it. Did not cross my mind so far since most people just write Cg/HLSL to begin with, instead of GLSL. Out of curiosity - why you write in GLSL? To make stuff work on less platforms?
Yes, that's all. I like the way Cg handles precision better, but... To ensure stuff works well on the platform that matters. The GLSL converter has, in the past, provided unintended results, specifically fixed4 translating to mediump under certain conditions, resulting in performance loss. I would help you make the converter better, by providing bug reports, but the code it provides is not readable without a lot of work, mostly reassigning variable names that make sense and removing redundant lines. Considering people generally come to me for "mobile" or "iOS" shaders, and iOS is the only platform I have been making anything for in my own time, it hasn't been worth it to me to write surface shaders / Cg. The time expense to make sure the GLSL is good is just too great; every source code update requires another translation adventure. With something like what I just posted, it's a better use of my time to write for OpenGL ES 2.0, and then follow up with another SubShader in Cg if necessary. I wish it were not like that.
Hey, guys. Sorry for waking a sleepy thread but 2 follow-on questions (kinda)... 1. What became of this (for GLSL code)? 2. More importantly, how can one set the default GLSL precision specifier from Cg shader? Sorry if I've missed an obvious one! I've included "#pragma fragmentoption ARB_precision_hint_fastest" (I thought maybe?) but no precision specifier appears in CgBatchOutput.shader and using PerfHUD ES to see what's running on the Tegra3 (an Ouya), I see "precision highp float;" at the top of the fragment shader. Thanks in advance! Rupert.
well, that is way more complicated thingy then you imagine. It is actually an nvidia-endorsed workaround for the bug in their shader compiler (inside drivers). On the other hand - tegra operates on float32 always internally, so there is no slowdown due to using that (apart from needing more registers etc, but you can ignore it for now)
Hi, @Alexey Thanks for the speedy and insightful reply! So the short version: don't worry about that default precision specifier on Tegra? (for now) (right?) I'm pretty new to shader writing so extra info like you supplied is gold I definitely didn't know that about the Tegra's internally always using float32! To again check I understood, one won't receive a performance benefit from using half/fixed (mediump/lowp) due to it needing to operate at lower precision but (as you hinted) one might get some benefit from the numbers packing better into registers (due to sub-highp consuming less space). Right? Guess I'll continue to 'aim as low as possible' for my variables ;-) I have another question on shader performance on Tegra but not related to this thread. Which forum area (or StackExchangey thing?) is best to post to? (the ShaderLab or Android one?) Thanks again! Rupert.
>>Guess I'll continue to 'aim as low as possible' for my variables well, yes. That will help everyone 8). Also, second note - do not forget that this default prec is just default - it wont override your own specifiers. >>I have another question on shader performance on Tegra but not related to this thread. Which forum area (or StackExchangey thing?) is best to post to? (the ShaderLab or Android one?) well i dunno - pick smth 8)