Changeset View
Changeset View
Standalone View
Standalone View
source/blender/gpu/intern/gpu_shader.c
| Context not available. | |||||
| #include "GPU_glew.h" | #include "GPU_glew.h" | ||||
| #include "GPU_shader.h" | #include "GPU_shader.h" | ||||
| #include "GPU_texture.h" | #include "GPU_texture.h" | ||||
| #include "gpu_codegen.h" | |||||
| /* TODO(sergey): Find better default values for this constants. */ | /* TODO(sergey): Find better default values for this constants. */ | ||||
| #define MAX_DEFINE_LENGTH 1024 | #define MAX_DEFINE_LENGTH 1024 | ||||
| #define MAX_EXT_DEFINE_LENGTH 1024 | #define MAX_EXT_DEFINE_LENGTH 1024 | ||||
| /* Non-generated shaders */ | /* Non-generated shaders */ | ||||
| extern char datatoc_gpu_shader_basic_instancing_frag_glsl[]; | |||||
| extern char datatoc_gpu_shader_basic_instancing_vert_glsl[]; | |||||
| extern char datatoc_gpu_shader_frame_buffer_frag_glsl[]; | |||||
| extern char datatoc_gpu_shader_frame_buffer_vert_glsl[]; | |||||
| extern char datatoc_gpu_shader_fire_frag_glsl[]; | extern char datatoc_gpu_shader_fire_frag_glsl[]; | ||||
| extern char datatoc_gpu_shader_smoke_vert_glsl[]; | extern char datatoc_gpu_shader_smoke_vert_glsl[]; | ||||
| extern char datatoc_gpu_shader_smoke_frag_glsl[]; | extern char datatoc_gpu_shader_smoke_frag_glsl[]; | ||||
| Context not available. | |||||
| extern char datatoc_gpu_shader_fx_dof_hq_geo_glsl[]; | extern char datatoc_gpu_shader_fx_dof_hq_geo_glsl[]; | ||||
| extern char datatoc_gpu_shader_fx_depth_resolve_glsl[]; | extern char datatoc_gpu_shader_fx_depth_resolve_glsl[]; | ||||
| extern char datatoc_gpu_shader_fx_lib_glsl[]; | extern char datatoc_gpu_shader_fx_lib_glsl[]; | ||||
| extern char datatoc_gpu_shader_lib_glsl[]; | |||||
| static struct GPUShadersGlobal { | static struct GPUShadersGlobal { | ||||
| struct { | struct { | ||||
| GPUShader *vsm_store; | GPUShader *vsm_store; | ||||
| GPUShader *vsm_store_instancing; | |||||
| GPUShader *sep_gaussian_blur; | GPUShader *sep_gaussian_blur; | ||||
| GPUShader *smoke; | GPUShader *smoke; | ||||
| GPUShader *smoke_fire; | GPUShader *smoke_fire; | ||||
| GPUShader *smoke_coba; | GPUShader *smoke_coba; | ||||
| GPUShader *instancing; | |||||
| GPUShader *draw_frame_buffer; | |||||
| GPUShader *stereo_stipple; | |||||
| GPUShader *stereo_anaglyph; | |||||
| /* cache for shader fx. Those can exist in combinations so store them here */ | /* cache for shader fx. Those can exist in combinations so store them here */ | ||||
| GPUShader *fx_shaders[MAX_FX_SHADERS * 2]; | GPUShader *fx_shaders[MAX_FX_SHADERS * 2]; | ||||
| } shaders; | } shaders; | ||||
| Context not available. | |||||
| static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH], | static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH], | ||||
| bool use_opensubdiv, | bool use_opensubdiv, | ||||
| bool use_instancing, | |||||
| bool use_new_shading) | bool use_new_shading) | ||||
| { | { | ||||
| /* some useful defines to detect GPU type */ | /* some useful defines to detect GPU type */ | ||||
| Context not available. | |||||
| UNUSED_VARS(use_opensubdiv); | UNUSED_VARS(use_opensubdiv); | ||||
| #endif | #endif | ||||
| if (use_instancing) { | |||||
| strcat(defines, "#define USE_INSTANCING\n"); | |||||
| } | |||||
| if (use_new_shading) { | if (use_new_shading) { | ||||
| strcat(defines, "#define USE_NEW_SHADING\n"); | strcat(defines, "#define USE_NEW_SHADING\n"); | ||||
| } | } | ||||
| Context not available. | |||||
| UNUSED_VARS(flags); | UNUSED_VARS(flags); | ||||
| bool use_opensubdiv = false; | bool use_opensubdiv = false; | ||||
| #endif | #endif | ||||
| bool use_instancing = (flags & GPU_SHADER_FLAGS_SPECIAL_INSTANCING) != 0; | |||||
| bool resetline = (flags & GPU_SHADER_FLAGS_SPECIAL_RESET_LINE) != 0; | |||||
| GLint status; | GLint status; | ||||
| GLchar log[5000]; | GLchar log[5000]; | ||||
| GLsizei length = 0; | GLsizei length = 0; | ||||
| Context not available. | |||||
| gpu_shader_standard_defines(standard_defines, | gpu_shader_standard_defines(standard_defines, | ||||
| use_opensubdiv, | use_opensubdiv, | ||||
| use_instancing, | |||||
| (flags & GPU_SHADER_FLAGS_NEW_SHADING) != 0); | (flags & GPU_SHADER_FLAGS_NEW_SHADING) != 0); | ||||
| gpu_shader_standard_extensions(standard_extensions, geocode != NULL); | gpu_shader_standard_extensions(standard_extensions, geocode != NULL); | ||||
| if (vertexcode) { | if (vertexcode) { | ||||
| const char *source[5]; | const char *source[7]; | ||||
| /* custom limit, may be too small, beware */ | /* custom limit, may be too small, beware */ | ||||
| int num_source = 0; | int num_source = 0; | ||||
| source[num_source++] = gpu_shader_version(); | source[num_source++] = gpu_shader_version(); | ||||
| source[num_source++] = standard_extensions; | source[num_source++] = standard_extensions; | ||||
| source[num_source++] = standard_defines; | source[num_source++] = standard_defines; | ||||
| source[num_source++] = datatoc_gpu_shader_lib_glsl; | |||||
| if (defines) source[num_source++] = defines; | if (defines) source[num_source++] = defines; | ||||
| if (resetline) { | |||||
| /* Print error message with the correct line number corresponding to the passed code */ | |||||
| source[num_source++] = "#line 0\n"; | |||||
| } | |||||
| source[num_source++] = vertexcode; | source[num_source++] = vertexcode; | ||||
| glAttachShader(shader->program, shader->vertex); | glAttachShader(shader->program, shader->vertex); | ||||
| Context not available. | |||||
| } | } | ||||
| if (fragcode) { | if (fragcode) { | ||||
| const char *source[7]; | const char *source[9]; | ||||
| int num_source = 0; | int num_source = 0; | ||||
| source[num_source++] = gpu_shader_version(); | source[num_source++] = gpu_shader_version(); | ||||
| source[num_source++] = standard_extensions; | source[num_source++] = standard_extensions; | ||||
| source[num_source++] = standard_defines; | source[num_source++] = standard_defines; | ||||
| source[num_source++] = datatoc_gpu_shader_lib_glsl; | |||||
| #ifdef WITH_OPENSUBDIV | #ifdef WITH_OPENSUBDIV | ||||
| /* TODO(sergey): Move to fragment shader source code generation. */ | /* TODO(sergey): Move to fragment shader source code generation. */ | ||||
| Context not available. | |||||
| if (defines) source[num_source++] = defines; | if (defines) source[num_source++] = defines; | ||||
| if (libcode) source[num_source++] = libcode; | if (libcode) source[num_source++] = libcode; | ||||
| if (resetline) { | |||||
| /* Print error message with the correct line number corresponding to the passed code */ | |||||
| source[num_source++] = "#line 0\n"; | |||||
| } | |||||
| source[num_source++] = fragcode; | source[num_source++] = fragcode; | ||||
| glAttachShader(shader->program, shader->fragment); | glAttachShader(shader->program, shader->fragment); | ||||
| Context not available. | |||||
| } | } | ||||
| if (geocode) { | if (geocode) { | ||||
| const char *source[6]; | const char *source[8]; | ||||
| int num_source = 0; | int num_source = 0; | ||||
| source[num_source++] = gpu_shader_version(); | source[num_source++] = gpu_shader_version(); | ||||
| source[num_source++] = standard_extensions; | source[num_source++] = standard_extensions; | ||||
| source[num_source++] = standard_defines; | source[num_source++] = standard_defines; | ||||
| source[num_source++] = datatoc_gpu_shader_lib_glsl; | |||||
| if (defines) source[num_source++] = defines; | if (defines) source[num_source++] = defines; | ||||
| if (resetline) { | |||||
| /* Print error message with the correct line number corresponding to the passed code */ | |||||
| source[num_source++] = "#line 0\n"; | |||||
| } | |||||
| source[num_source++] = geocode; | source[num_source++] = geocode; | ||||
| glAttachShader(shader->program, shader->geometry); | glAttachShader(shader->program, shader->geometry); | ||||
| Context not available. | |||||
| return shader; | return shader; | ||||
| } | } | ||||
| char *GPU_shader_validate(GPUShader *shader) | |||||
| { | |||||
| int stat = 0; | |||||
| glValidateProgram(shader->program); | |||||
| glGetObjectParameterivARB(shader->program, GL_OBJECT_VALIDATE_STATUS_ARB, (GLint *)&stat); | |||||
| if (stat > 0) { | |||||
| int charlen = 0; | |||||
| char *log = (char *)MEM_mallocN(stat, "GPU_shader_validate"); | |||||
| glGetInfoLogARB(shader->program, stat, (GLsizei *)&charlen, log); | |||||
| return log; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| void GPU_shader_bind(GPUShader *shader) | void GPU_shader_bind(GPUShader *shader) | ||||
| { | { | ||||
| GPU_ASSERT_NO_GL_ERRORS("Pre Shader Bind"); | GPU_ASSERT_NO_GL_ERRORS("Pre Shader Bind"); | ||||
| Context not available. | |||||
| GPU_ASSERT_NO_GL_ERRORS("Post Shader Unbind"); | GPU_ASSERT_NO_GL_ERRORS("Post Shader Unbind"); | ||||
| } | } | ||||
| int GPU_shader_program(GPUShader *shader) | |||||
| { | |||||
| return shader->program; | |||||
| } | |||||
| void GPU_shader_free(GPUShader *shader) | void GPU_shader_free(GPUShader *shader) | ||||
| { | { | ||||
| if (shader->vertex) | if (shader->vertex) | ||||
| Context not available. | |||||
| GPU_CHECK_ERRORS_AROUND(glUniform1i(location, value)); | GPU_CHECK_ERRORS_AROUND(glUniform1i(location, value)); | ||||
| } | } | ||||
| void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) | |||||
| { | |||||
| if (location == -1) | |||||
| return; | |||||
| GPU_CHECK_ERRORS_AROUND(glUniform1f(location, value)); | |||||
| } | |||||
| void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) | void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) | ||||
| { | { | ||||
| if (GPU_geometry_shader_support_via_extension()) { | if (GPU_geometry_shader_support_via_extension()) { | ||||
| Context not available. | |||||
| return index; | return index; | ||||
| } | } | ||||
| void GPU_shader_bind_attribute(GPUShader *shader, int location, const char *name) | |||||
| { | |||||
| GPU_CHECK_ERRORS_AROUND(glBindAttribLocation(shader->program, location, name)); | |||||
| } | |||||
| // Used only for VSM shader with geometry instancing support. | |||||
| void GPU_shader_bind_instancing_attrib(GPUShader *shader, void *matrixoffset, void *positionoffset, unsigned int stride) | |||||
| { | |||||
| int posloc = GPU_shader_get_attribute(shader, GPU_builtin_name(GPU_INSTANCING_POSITION_ATTRIB)); | |||||
| int matloc = GPU_shader_get_attribute(shader, GPU_builtin_name(GPU_INSTANCING_MATRIX_ATTRIB)); | |||||
| // Matrix | |||||
| if (matloc != -1) { | |||||
| glEnableVertexAttribArrayARB(matloc); | |||||
| glEnableVertexAttribArrayARB(matloc + 1); | |||||
| glEnableVertexAttribArrayARB(matloc + 2); | |||||
| glVertexAttribPointerARB(matloc, 3, GL_FLOAT, GL_FALSE, stride, matrixoffset); | |||||
| glVertexAttribPointerARB(matloc + 1, 3, GL_FLOAT, GL_FALSE, stride, ((char *)matrixoffset) + 3 * sizeof(float)); | |||||
| glVertexAttribPointerARB(matloc + 2, 3, GL_FLOAT, GL_FALSE, stride, ((char *)matrixoffset) + 6 * sizeof(float)); | |||||
| glVertexAttribDivisorARB(matloc, 1); | |||||
| glVertexAttribDivisorARB(matloc + 1, 1); | |||||
| glVertexAttribDivisorARB(matloc + 2, 1); | |||||
| } | |||||
| // Position | |||||
| if (posloc != -1) { | |||||
| glEnableVertexAttribArrayARB(posloc); | |||||
| glVertexAttribPointerARB(posloc, 3, GL_FLOAT, GL_FALSE, stride, positionoffset); | |||||
| glVertexAttribDivisorARB(posloc, 1); | |||||
| } | |||||
| } | |||||
| void GPU_shader_unbind_instancing_attrib(GPUShader *shader) | |||||
| { | |||||
| int posloc = GPU_shader_get_attribute(shader, GPU_builtin_name(GPU_INSTANCING_POSITION_ATTRIB)); | |||||
| int matloc = GPU_shader_get_attribute(shader, GPU_builtin_name(GPU_INSTANCING_MATRIX_ATTRIB)); | |||||
| // Matrix | |||||
| if (matloc != -1) { | |||||
| glDisableVertexAttribArrayARB(matloc); | |||||
| glDisableVertexAttribArrayARB(matloc + 1); | |||||
| glDisableVertexAttribArrayARB(matloc + 2); | |||||
| glVertexAttribDivisorARB(matloc, 0); | |||||
| glVertexAttribDivisorARB(matloc + 1, 0); | |||||
| glVertexAttribDivisorARB(matloc + 2, 0); | |||||
| } | |||||
| // Position | |||||
| if (posloc != -1) { | |||||
| glDisableVertexAttribArrayARB(posloc); | |||||
| glVertexAttribDivisorARB(posloc, 0); | |||||
| } | |||||
| } | |||||
| GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) | GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) | ||||
| { | { | ||||
| GPUShader *retval = NULL; | GPUShader *retval = NULL; | ||||
| Context not available. | |||||
| NULL, NULL, NULL, 0, 0, 0); | NULL, NULL, NULL, 0, 0, 0); | ||||
| retval = GG.shaders.vsm_store; | retval = GG.shaders.vsm_store; | ||||
| break; | break; | ||||
| case GPU_SHADER_VSM_STORE_INSTANCING: | |||||
| if (!GG.shaders.vsm_store_instancing) | |||||
| GG.shaders.vsm_store_instancing = GPU_shader_create( | |||||
| datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl, | |||||
| NULL, NULL, "#define USE_INSTANCING;\n", 0, 0, 0); | |||||
| retval = GG.shaders.vsm_store_instancing; | |||||
| break; | |||||
| case GPU_SHADER_SEP_GAUSSIAN_BLUR: | case GPU_SHADER_SEP_GAUSSIAN_BLUR: | ||||
| if (!GG.shaders.sep_gaussian_blur) | if (!GG.shaders.sep_gaussian_blur) | ||||
| GG.shaders.sep_gaussian_blur = GPU_shader_create( | GG.shaders.sep_gaussian_blur = GPU_shader_create( | ||||
| Context not available. | |||||
| NULL, NULL, "#define USE_COBA;\n", 0, 0, 0); | NULL, NULL, "#define USE_COBA;\n", 0, 0, 0); | ||||
| retval = GG.shaders.smoke_coba; | retval = GG.shaders.smoke_coba; | ||||
| break; | break; | ||||
| case GPU_SHADER_INSTANCING: | |||||
| if (!GG.shaders.instancing) | |||||
| GG.shaders.instancing = GPU_shader_create( | |||||
| datatoc_gpu_shader_basic_instancing_vert_glsl, datatoc_gpu_shader_basic_instancing_frag_glsl, | |||||
| NULL, NULL, NULL, 0, 0, 0); | |||||
| retval = GG.shaders.instancing; | |||||
| break; | |||||
| case GPU_SHADER_DRAW_FRAME_BUFFER: | |||||
| if (!GG.shaders.draw_frame_buffer) | |||||
| GG.shaders.draw_frame_buffer = GPU_shader_create( | |||||
| datatoc_gpu_shader_frame_buffer_vert_glsl, datatoc_gpu_shader_frame_buffer_frag_glsl, | |||||
| NULL, NULL, NULL, 0, 0, 0); | |||||
| retval = GG.shaders.draw_frame_buffer; | |||||
| break; | |||||
| case GPU_SHADER_STEREO_STIPPLE: | |||||
| if (!GG.shaders.stereo_stipple) | |||||
| GG.shaders.stereo_stipple = GPU_shader_create( | |||||
| datatoc_gpu_shader_frame_buffer_vert_glsl, datatoc_gpu_shader_frame_buffer_frag_glsl, | |||||
| NULL, NULL, "#define STIPPLE;\n", 0, 0, 0); | |||||
| retval = GG.shaders.stereo_stipple; | |||||
| break; | |||||
| case GPU_SHADER_STEREO_ANAGLYPH: | |||||
| if (!GG.shaders.stereo_anaglyph) | |||||
| GG.shaders.stereo_anaglyph = GPU_shader_create( | |||||
| datatoc_gpu_shader_frame_buffer_vert_glsl, datatoc_gpu_shader_frame_buffer_frag_glsl, | |||||
| NULL, NULL, "#define ANAGLYPH;\n", 0, 0, 0); | |||||
| retval = GG.shaders.stereo_anaglyph; | |||||
| break; | |||||
| } | } | ||||
| if (retval == NULL) | if (retval == NULL) | ||||
| Context not available. | |||||
| GG.shaders.smoke_coba = NULL; | GG.shaders.smoke_coba = NULL; | ||||
| } | } | ||||
| if (GG.shaders.instancing) { | |||||
| GPU_shader_free(GG.shaders.instancing); | |||||
| GG.shaders.instancing = NULL; | |||||
| } | |||||
| if (GG.shaders.draw_frame_buffer) { | |||||
| GPU_shader_free(GG.shaders.draw_frame_buffer); | |||||
| GG.shaders.draw_frame_buffer = NULL; | |||||
| } | |||||
| if (GG.shaders.stereo_stipple) { | |||||
| GPU_shader_free(GG.shaders.stereo_stipple); | |||||
| GG.shaders.stereo_stipple = NULL; | |||||
| } | |||||
| if (GG.shaders.stereo_anaglyph) { | |||||
| GPU_shader_free(GG.shaders.stereo_anaglyph); | |||||
| GG.shaders.stereo_anaglyph = NULL; | |||||
| } | |||||
| for (i = 0; i < 2 * MAX_FX_SHADERS; ++i) { | for (i = 0; i < 2 * MAX_FX_SHADERS; ++i) { | ||||
| if (GG.shaders.fx_shaders[i]) { | if (GG.shaders.fx_shaders[i]) { | ||||
| GPU_shader_free(GG.shaders.fx_shaders[i]); | GPU_shader_free(GG.shaders.fx_shaders[i]); | ||||
| Context not available. | |||||