diff options
Diffstat (limited to 'source/blender/gpu/intern')
24 files changed, 6278 insertions, 9238 deletions
diff --git a/source/blender/gpu/intern/gpu_basic_shader.c b/source/blender/gpu/intern/gpu_basic_shader.c index 5d23de78451..b720bed2d0c 100644 --- a/source/blender/gpu/intern/gpu_basic_shader.c +++ b/source/blender/gpu/intern/gpu_basic_shader.c @@ -140,114 +140,6 @@ const GLubyte stipple_checker_8px[128] = { 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255}; -const GLubyte stipple_interlace_row[128] = { - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}; - -const GLubyte stipple_interlace_row_swap[128] = { - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}; - -const GLubyte stipple_interlace_column[128] = { - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; - -const GLubyte stipple_interlace_column_swap[128] = { - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; - -const GLubyte stipple_interlace_checker[128] = { - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, - 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa}; - -const GLubyte stipple_interlace_checker_swap[128] = { - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, - 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55}; - const GLubyte stipple_hexagon[128] = { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, @@ -267,24 +159,6 @@ const GLubyte stipple_hexagon[128] = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; /* ********************************************* */ -/* GLSL State */ - -static bool USE_GLSL = false; - -/** - * \note this isn't part of the basic shader API, - * only set from the command line once on startup. - */ -void GPU_basic_shader_use_glsl_set(bool enabled) -{ - USE_GLSL = enabled; -} - -bool GPU_basic_shader_use_glsl_get(void) -{ - return USE_GLSL; -} - /* Init / exit */ void GPU_basic_shaders_init(void) @@ -316,31 +190,6 @@ static bool solid_compatible_lighting(void) return ((directional & enabled) == enabled); } -#if 0 -static int detect_options() -{ - GLint two_sided; - int options = 0; - - if (glIsEnabled(GL_TEXTURE_2D)) - options |= GPU_SHADER_TEXTURE_2D; - if (glIsEnabled(GL_TEXTURE_RECTANGLE)) - options |= GPU_SHADER_TEXTURE_RECT; - GPU_SHADER_TEXTURE_RECT - if (glIsEnabled(GL_COLOR_MATERIAL)) - options |= GPU_SHADER_USE_COLOR; - - if (glIsEnabled(GL_LIGHTING)) - options |= GPU_SHADER_LIGHTING; - - glGetIntegerv(GL_LIGHT_MODEL_TWO_SIDE, &two_sided); - if (two_sided == GL_TRUE) - options |= GPU_SHADER_TWO_SIDED; - - return options; -} -#endif - static GPUShader *gpu_basic_shader(int options) { /* glsl code */ @@ -388,7 +237,7 @@ static GPUShader *gpu_basic_shader(int options) datatoc_gpu_shader_basic_frag_glsl, geom_glsl, NULL, - defines, 0, 0, 0); + defines); if (shader) { /* set texture map to first texture unit */ @@ -420,102 +269,16 @@ static void gpu_basic_shader_uniform_autoset(GPUShader *shader, int options) void GPU_basic_shader_bind(int options) { - if (USE_GLSL) { - if (options) { - const int bound_options = GPU_MATERIAL_STATE.bound_options; - - /* texture options need to be set for basic shader too */ - if (options & GPU_SHADER_TEXTURE_2D) { - glEnable(GL_TEXTURE_2D); - } - else if (bound_options & GPU_SHADER_TEXTURE_2D) { - glDisable(GL_TEXTURE_2D); - } + if (options) { + GPUShader *shader = gpu_basic_shader(options); - if (options & GPU_SHADER_TEXTURE_RECT) { - glEnable(GL_TEXTURE_RECTANGLE); - } - else if (bound_options & GPU_SHADER_TEXTURE_RECT) { - glDisable(GL_TEXTURE_RECTANGLE); - } - - GPUShader *shader = gpu_basic_shader(options); - - if (shader) { - GPU_shader_bind(shader); - gpu_basic_shader_uniform_autoset(shader, options); - } - } - else { - GPU_shader_unbind(); + if (shader) { + GPU_shader_bind(shader); + gpu_basic_shader_uniform_autoset(shader, options); } } else { - const int bound_options = GPU_MATERIAL_STATE.bound_options; - - if (options & GPU_SHADER_LIGHTING) { - glEnable(GL_LIGHTING); - - if (options & GPU_SHADER_USE_COLOR) - glEnable(GL_COLOR_MATERIAL); - else - glDisable(GL_COLOR_MATERIAL); - - if (options & GPU_SHADER_TWO_SIDED) - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); - else - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); - } - else if (bound_options & GPU_SHADER_LIGHTING) { - glDisable(GL_LIGHTING); - glDisable(GL_COLOR_MATERIAL); - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); - } - - if (options & GPU_SHADER_TEXTURE_2D) { - GLint env_mode = (options & (GPU_SHADER_USE_COLOR | GPU_SHADER_LIGHTING)) ? GL_MODULATE : GL_REPLACE; - glEnable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env_mode); - } - else if (bound_options & GPU_SHADER_TEXTURE_2D) { - if ((options & GPU_SHADER_TEXTURE_RECT) == 0) { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - glDisable(GL_TEXTURE_2D); - } - - if (options & GPU_SHADER_TEXTURE_RECT) { - GLint env_mode = (options & (GPU_SHADER_USE_COLOR | GPU_SHADER_LIGHTING)) ? GL_MODULATE : GL_REPLACE; - glEnable(GL_TEXTURE_RECTANGLE); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env_mode); - } - else if (bound_options & GPU_SHADER_TEXTURE_RECT) { - if ((options & GPU_SHADER_TEXTURE_2D) == 0) { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - glDisable(GL_TEXTURE_RECTANGLE); - } - - if ((options & GPU_SHADER_LINE) && (options & GPU_SHADER_STIPPLE)) { - glEnable(GL_LINE_STIPPLE); - } - else if ((bound_options & GPU_SHADER_LINE) && (bound_options & GPU_SHADER_STIPPLE)) { - glDisable(GL_LINE_STIPPLE); - } - - if (((options & GPU_SHADER_LINE) == 0) && (options & GPU_SHADER_STIPPLE)) { - glEnable(GL_POLYGON_STIPPLE); - } - else if (((bound_options & GPU_SHADER_LINE) == 0) && (bound_options & GPU_SHADER_STIPPLE)) { - glDisable(GL_POLYGON_STIPPLE); - } - - if (options & GPU_SHADER_FLAT_NORMAL) { - glShadeModel(GL_FLAT); - } - else if (bound_options & GPU_SHADER_FLAT_NORMAL) { - glShadeModel(GL_SMOOTH); - } + GPU_shader_unbind(); } GPU_MATERIAL_STATE.bound_options = options; @@ -544,175 +307,37 @@ void GPU_basic_shader_colors( const float diffuse[3], const float specular[3], int shininess, float alpha) { - float gl_diffuse[4], gl_specular[4]; - - if (diffuse) - copy_v3_v3(gl_diffuse, diffuse); - else - zero_v3(gl_diffuse); - gl_diffuse[3] = alpha; - - if (specular) - copy_v3_v3(gl_specular, specular); - else - zero_v3(gl_specular); - gl_specular[3] = 1.0f; - - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, gl_diffuse); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, gl_specular); - glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, CLAMPIS(shininess, 1, 128)); + UNUSED_VARS(diffuse, specular, shininess, alpha); + return; } void GPU_basic_shader_light_set(int light_num, GPULightData *light) { - int light_bit = (1 << light_num); - - /* note that light position is affected by the current modelview matrix! */ - - GPU_MATERIAL_STATE.lights_enabled &= ~light_bit; - GPU_MATERIAL_STATE.lights_directional &= ~light_bit; - - if (light) { - float position[4], diffuse[4], specular[4]; - - glEnable(GL_LIGHT0 + light_num); - - /* position */ - if (light->type == GPU_LIGHT_SUN) { - copy_v3_v3(position, light->direction); - position[3] = 0.0f; - } - else { - copy_v3_v3(position, light->position); - position[3] = 1.0f; - } - glLightfv(GL_LIGHT0 + light_num, GL_POSITION, position); - - /* energy */ - copy_v3_v3(diffuse, light->diffuse); - copy_v3_v3(specular, light->specular); - diffuse[3] = 1.0f; - specular[3] = 1.0f; - glLightfv(GL_LIGHT0 + light_num, GL_DIFFUSE, diffuse); - glLightfv(GL_LIGHT0 + light_num, GL_SPECULAR, specular); - - /* attenuation */ - if (light->type == GPU_LIGHT_SUN) { - glLightf(GL_LIGHT0 + light_num, GL_CONSTANT_ATTENUATION, 1.0f); - glLightf(GL_LIGHT0 + light_num, GL_LINEAR_ATTENUATION, 0.0f); - glLightf(GL_LIGHT0 + light_num, GL_QUADRATIC_ATTENUATION, 0.0f); - } - else { - glLightf(GL_LIGHT0 + light_num, GL_CONSTANT_ATTENUATION, light->constant_attenuation); - glLightf(GL_LIGHT0 + light_num, GL_LINEAR_ATTENUATION, light->linear_attenuation); - glLightf(GL_LIGHT0 + light_num, GL_QUADRATIC_ATTENUATION, light->quadratic_attenuation); - } - - /* spot */ - glLightfv(GL_LIGHT0 + light_num, GL_SPOT_DIRECTION, light->direction); - if (light->type == GPU_LIGHT_SPOT) { - glLightf(GL_LIGHT0 + light_num, GL_SPOT_CUTOFF, light->spot_cutoff); - glLightf(GL_LIGHT0 + light_num, GL_SPOT_EXPONENT, light->spot_exponent); - } - else { - glLightf(GL_LIGHT0 + light_num, GL_SPOT_CUTOFF, 180.0f); - glLightf(GL_LIGHT0 + light_num, GL_SPOT_EXPONENT, 0.0f); - } - - GPU_MATERIAL_STATE.lights_enabled |= light_bit; - if (position[3] == 0.0f) - GPU_MATERIAL_STATE.lights_directional |= light_bit; - } - else { - /* TODO(sergey): Needs revisit. */ - if (USE_GLSL || true) { - /* glsl shader needs these zero to skip them */ - const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - - glLightfv(GL_LIGHT0 + light_num, GL_POSITION, zero); - glLightfv(GL_LIGHT0 + light_num, GL_DIFFUSE, zero); - glLightfv(GL_LIGHT0 + light_num, GL_SPECULAR, zero); - } - - glDisable(GL_LIGHT0 + light_num); - } + UNUSED_VARS(light_num, light); + return; } void GPU_basic_shader_light_set_viewer(bool local) { - glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, (local) ? GL_TRUE: GL_FALSE); + UNUSED_VARS(local); + return; } void GPU_basic_shader_stipple(GPUBasicShaderStipple stipple_id) { - if (USE_GLSL) { - glUniform1i(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "stipple_id"), stipple_id); - } - else { - switch (stipple_id) { - case GPU_SHADER_STIPPLE_HALFTONE: - glPolygonStipple(stipple_halftone); - return; - case GPU_SHADER_STIPPLE_QUARTTONE: - glPolygonStipple(stipple_quarttone); - return; - case GPU_SHADER_STIPPLE_CHECKER_8PX: - glPolygonStipple(stipple_checker_8px); - return; - case GPU_SHADER_STIPPLE_HEXAGON: - glPolygonStipple(stipple_hexagon); - return; - case GPU_SHADER_STIPPLE_DIAG_STRIPES_SWAP: - glPolygonStipple(stipple_diag_stripes_neg); - return; - case GPU_SHADER_STIPPLE_DIAG_STRIPES: - glPolygonStipple(stipple_diag_stripes_pos); - return; - case GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW: - glPolygonStipple(stipple_interlace_row); - return; - case GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW_SWAP: - glPolygonStipple(stipple_interlace_row_swap); - return; - case GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN: - glPolygonStipple(stipple_interlace_column); - return; - case GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN_SWAP: - glPolygonStipple(stipple_interlace_column_swap); - return; - case GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER: - glPolygonStipple(stipple_interlace_checker); - return; - case GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER_SWAP: - glPolygonStipple(stipple_interlace_checker_swap); - return; - default: - glPolygonStipple(stipple_hexagon); - return; - } - } + glUniform1i(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "stipple_id"), stipple_id); } void GPU_basic_shader_line_width(float line_width) { - if (USE_GLSL) { - GPU_MATERIAL_STATE.line_width = line_width; - if (GPU_MATERIAL_STATE.bound_options & GPU_SHADER_LINE) { - glUniform1f(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "line_width"), line_width); - } - } - else { - glLineWidth(line_width); + GPU_MATERIAL_STATE.line_width = line_width; + if (GPU_MATERIAL_STATE.bound_options & GPU_SHADER_LINE) { + glUniform1f(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "line_width"), line_width); } } void GPU_basic_shader_line_stipple(GLint stipple_factor, GLushort stipple_pattern) { - if (USE_GLSL) { - glUniform1i(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "stipple_factor"), stipple_factor); - glUniform1i(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "stipple_pattern"), stipple_pattern); - } - else { - glLineStipple(stipple_factor, stipple_pattern); - } + glUniform1i(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "stipple_factor"), stipple_factor); + glUniform1i(GPU_shader_get_uniform(gpu_basic_shader(GPU_MATERIAL_STATE.bound_options), "stipple_pattern"), stipple_pattern); } diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.c new file mode 100644 index 00000000000..59d88e81822 --- /dev/null +++ b/source/blender/gpu/intern/gpu_batch.c @@ -0,0 +1,281 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Mike Erwin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/gpu/intern/gpu_batch.c + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_rect.h" +#include "BLI_math.h" +#include "BLI_polyfill_2d.h" +#include "BLI_sort_utils.h" + + +#include "GPU_batch.h" /* own include */ +#include "gpu_shader_private.h" + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +void GWN_batch_program_set_builtin(Gwn_Batch *batch, GPUBuiltinShader shader_id) +{ + GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); + GWN_batch_program_set(batch, shader->program, shader->interface); +} + +/** \} */ + + + +/* -------------------------------------------------------------------- */ +/** \name Batch Creation + * \{ */ + +/** + * Creates triangles from a byte-array of polygons. + * + * See 'make_shape_2d_from_blend.py' utility to create data to pass to this function. + * + * \param polys_flat: Pairs of X, Y coordinates (repeating to signify closing the polygon). + * \param polys_flat_len: Length of the array (must be an even number). + * \param rect: Optional region to map the byte 0..255 coords to. When not set use -1..1. + */ +Gwn_Batch *GPU_batch_tris_from_poly_2d_encoded( + const uchar *polys_flat, uint polys_flat_len, const rctf *rect) +{ + const uchar (*polys)[2] = (const void *)polys_flat; + const uint polys_len = polys_flat_len / 2; + BLI_assert(polys_flat_len == polys_len * 2); + + /* Over alloc in both cases */ + float (*verts)[2] = MEM_mallocN(sizeof(*verts) * polys_len, __func__); + float (*verts_step)[2] = verts; + uint (*tris)[3] = MEM_mallocN(sizeof(*tris) * polys_len, __func__); + uint (*tris_step)[3] = tris; + + const float range_uchar[2] = { + (rect ? (rect->xmax - rect->xmin) : 2.0f) / 255.0f, + (rect ? (rect->ymax - rect->ymin) : 2.0f) / 255.0f, + }; + const float min_uchar[2] = { + (rect ? rect->xmin : -1.0f), + (rect ? rect->ymin : -1.0f), + }; + + uint i_poly = 0; + uint i_vert = 0; + while (i_poly != polys_len) { + for (uint j = 0; j < 2; j++) { + verts[i_vert][j] = min_uchar[j] + ((float)polys[i_poly][j] * range_uchar[j]); + } + i_vert++; + i_poly++; + if (polys[i_poly - 1][0] == polys[i_poly][0] && + polys[i_poly - 1][1] == polys[i_poly][1]) + { + const uint verts_step_len = (&verts[i_vert]) - verts_step; + BLI_assert(verts_step_len >= 3); + const uint tris_len = (verts_step_len - 2); + BLI_polyfill_calc(verts_step, verts_step_len, -1, tris_step); + /* offset indices */ + if (verts_step != verts) { + uint *t = tris_step[0]; + const uint offset = (verts_step - verts); + uint tot = tris_len * 3; + while (tot--) { + *t += offset; + t++; + } + BLI_assert(t == tris_step[tris_len]); + } + verts_step += verts_step_len; + tris_step += tris_len; + i_poly++; + /* ignore the duplicate point */ + } + } + + /* We have vertices and tris, make a batch from this. */ + static Gwn_VertFormat format = {0}; + static struct { uint pos; } attr_id; + if (format.attrib_ct == 0) { + attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + } + + const uint verts_len = (verts_step - verts); + const uint tris_len = (tris_step - tris); + Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format); + GWN_vertbuf_data_alloc(vbo, verts_len); + + Gwn_VertBufRaw pos_step; + GWN_vertbuf_attr_get_raw_data(vbo, attr_id.pos, &pos_step); + + for (uint i = 0; i < verts_len; i++) { + copy_v2_v2(GWN_vertbuf_raw_step(&pos_step), verts[i]); + } + + Gwn_IndexBufBuilder elb; + GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, tris_len, verts_len); + for (uint i = 0; i < tris_len; i++) { + GWN_indexbuf_add_tri_verts(&elb, UNPACK3(tris[i])); + } + Gwn_IndexBuf *indexbuf = GWN_indexbuf_build(&elb); + + MEM_freeN(tris); + MEM_freeN(verts); + + return GWN_batch_create_ex( + GWN_PRIM_TRIS, vbo, + indexbuf, + GWN_BATCH_OWNS_VBO | GWN_BATCH_OWNS_INDEX); +} + +Gwn_Batch *GPU_batch_wire_from_poly_2d_encoded( + const uchar *polys_flat, uint polys_flat_len, const rctf *rect) +{ + const uchar (*polys)[2] = (const void *)polys_flat; + const uint polys_len = polys_flat_len / 2; + BLI_assert(polys_flat_len == polys_len * 2); + + /* Over alloc */ + /* Lines are pairs of (x, y) byte locations packed into an int32_t. */ + int32_t *lines = MEM_mallocN(sizeof(*lines) * polys_len, __func__); + int32_t *lines_step = lines; + + const float range_uchar[2] = { + (rect ? (rect->xmax - rect->xmin) : 2.0f) / 255.0f, + (rect ? (rect->ymax - rect->ymin) : 2.0f) / 255.0f, + }; + const float min_uchar[2] = { + (rect ? rect->xmin : -1.0f), + (rect ? rect->ymin : -1.0f), + }; + + uint i_poly_prev = 0; + uint i_poly = 0; + while (i_poly != polys_len) { + i_poly++; + if (polys[i_poly - 1][0] == polys[i_poly][0] && + polys[i_poly - 1][1] == polys[i_poly][1]) + { + const uchar (*polys_step)[2] = polys + i_poly_prev; + const uint polys_step_len = i_poly - i_poly_prev; + BLI_assert(polys_step_len >= 2); + for (uint i_prev = polys_step_len - 1, i = 0; i < polys_step_len; i_prev = i++) { + union { + uint8_t as_u8[4]; + uint16_t as_u16[2]; + uint32_t as_u32; + } data; + data.as_u16[0] = *((const uint16_t *)polys_step[i_prev]); + data.as_u16[1] = *((const uint16_t *)polys_step[i]); + if (data.as_u16[0] > data.as_u16[1]) { + SWAP(uint16_t, data.as_u16[0], data.as_u16[1]); + } + *lines_step = data.as_u32; + lines_step++; + } + i_poly++; + i_poly_prev = i_poly; + /* ignore the duplicate point */ + } + } + + uint lines_len = lines_step - lines; + + /* Hide Lines (we could make optional) */ + { + qsort(lines, lines_len, sizeof(int32_t), BLI_sortutil_cmp_int); + lines_step = lines; + + if (lines[0] != lines[1]) { + *lines_step++ = lines[0]; + } + for (uint i_prev = 0, i = 1; i < lines_len; i_prev = i++) { + if (lines[i] != lines[i_prev]) { + *lines_step++ = lines[i]; + } + } + lines_len = lines_step - lines; + } + + /* We have vertices and tris, make a batch from this. */ + static Gwn_VertFormat format = {0}; + static struct { uint pos; } attr_id; + if (format.attrib_ct == 0) { + attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + } + + Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format); + const uint vbo_len_capacity = lines_len * 2; + GWN_vertbuf_data_alloc(vbo, vbo_len_capacity); + + Gwn_VertBufRaw pos_step; + GWN_vertbuf_attr_get_raw_data(vbo, attr_id.pos, &pos_step); + + for (uint i = 0; i < lines_len; i++) { + union { + uint8_t as_u8_pair[2][2]; + uint32_t as_u32; + } data; + data.as_u32 = lines[i]; + for (uint k = 0; k < 2; k++) { + float *pos_v2 = GWN_vertbuf_raw_step(&pos_step); + for (uint j = 0; j < 2; j++) { + pos_v2[j] = min_uchar[j] + ((float)data.as_u8_pair[k][j] * range_uchar[j]); + } + } + } + BLI_assert(vbo_len_capacity == GWN_vertbuf_raw_used(&pos_step)); + MEM_freeN(lines); + return GWN_batch_create_ex( + GWN_PRIM_LINES, vbo, + NULL, + GWN_BATCH_OWNS_VBO); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Init/Exit + * \{ */ + +void gpu_batch_init(void) +{ + gpu_batch_presets_init(); +} + +void gpu_batch_exit(void) +{ + gpu_batch_presets_exit(); +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_batch_presets.c b/source/blender/gpu/intern/gpu_batch_presets.c new file mode 100644 index 00000000000..10cbd16490b --- /dev/null +++ b/source/blender/gpu/intern/gpu_batch_presets.c @@ -0,0 +1,236 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Mike Erwin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/gpu/intern/gpu_batch_presets.c + * \ingroup gpu + */ + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_threads.h" +#include "BLI_listbase.h" +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" + +#include "GPU_batch.h" +#include "gpu_shader_private.h" + +/* Struct to store 3D Batches and their format */ +static struct { + struct { + Gwn_Batch *sphere_high; + Gwn_Batch *sphere_med; + Gwn_Batch *sphere_low; + Gwn_Batch *sphere_wire_low; + Gwn_Batch *sphere_wire_med; + } batch; + + Gwn_VertFormat format; + + struct { + uint pos, nor; + } attr_id; +} g_presets_3d = {{0}}; + +static ListBase presets_list = {NULL, NULL}; + +/* -------------------------------------------------------------------- */ +/** \name 3D Primitives + * \{ */ + +static Gwn_VertFormat *preset_3D_format(void) +{ + if (g_presets_3d.format.attrib_ct == 0) { + Gwn_VertFormat *format = &g_presets_3d.format; + g_presets_3d.attr_id.pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + g_presets_3d.attr_id.nor = GWN_vertformat_attr_add(format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + } + return &g_presets_3d.format; +} + +static void batch_sphere_lat_lon_vert( + Gwn_VertBufRaw *pos_step, Gwn_VertBufRaw *nor_step, + float lat, float lon) +{ + float pos[3]; + pos[0] = sinf(lat) * cosf(lon); + pos[1] = cosf(lat); + pos[2] = sinf(lat) * sinf(lon); + copy_v3_v3(GWN_vertbuf_raw_step(pos_step), pos); + copy_v3_v3(GWN_vertbuf_raw_step(nor_step), pos); +} + +/* Replacement for gluSphere */ +Gwn_Batch *gpu_batch_sphere(int lat_res, int lon_res) +{ + const float lon_inc = 2 * M_PI / lon_res; + const float lat_inc = M_PI / lat_res; + float lon, lat; + + Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(preset_3D_format()); + const uint vbo_len = (lat_res - 1) * lon_res * 6; + GWN_vertbuf_data_alloc(vbo, vbo_len); + + Gwn_VertBufRaw pos_step, nor_step; + GWN_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.pos, &pos_step); + GWN_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.nor, &nor_step); + + lon = 0.0f; + for (int i = 0; i < lon_res; i++, lon += lon_inc) { + lat = 0.0f; + for (int j = 0; j < lat_res; j++, lat += lat_inc) { + if (j != lat_res - 1) { /* Pole */ + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon + lon_inc); + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon); + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon); + } + + if (j != 0) { /* Pole */ + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon + lon_inc); + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon + lon_inc); + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon); + } + } + } + + BLI_assert(vbo_len == GWN_vertbuf_raw_used(&pos_step)); + BLI_assert(vbo_len == GWN_vertbuf_raw_used(&nor_step)); + + return GWN_batch_create_ex(GWN_PRIM_TRIS, vbo, NULL, GWN_BATCH_OWNS_VBO); +} + +static Gwn_Batch *batch_sphere_wire(int lat_res, int lon_res) +{ + const float lon_inc = 2 * M_PI / lon_res; + const float lat_inc = M_PI / lat_res; + float lon, lat; + + Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(preset_3D_format()); + const uint vbo_len = (lat_res * lon_res * 2) + ((lat_res - 1) * lon_res * 2); + GWN_vertbuf_data_alloc(vbo, vbo_len); + + Gwn_VertBufRaw pos_step, nor_step; + GWN_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.pos, &pos_step); + GWN_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.nor, &nor_step); + + lon = 0.0f; + for (int i = 0; i < lon_res; i++, lon += lon_inc) { + lat = 0.0f; + for (int j = 0; j < lat_res; j++, lat += lat_inc) { + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon); + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon); + + if (j != lat_res - 1) { /* Pole */ + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon + lon_inc); + batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon); + } + } + } + + BLI_assert(vbo_len == GWN_vertbuf_raw_used(&pos_step)); + BLI_assert(vbo_len == GWN_vertbuf_raw_used(&nor_step)); + + return GWN_batch_create_ex(GWN_PRIM_LINES, vbo, NULL, GWN_BATCH_OWNS_VBO); +} + +Gwn_Batch *GPU_batch_preset_sphere(int lod) +{ + BLI_assert(lod >= 0 && lod <= 2); + BLI_assert(BLI_thread_is_main()); + + if (lod == 0) { + return g_presets_3d.batch.sphere_low; + } + else if (lod == 1) { + return g_presets_3d.batch.sphere_med; + } + else { + return g_presets_3d.batch.sphere_high; + } +} + +Gwn_Batch *GPU_batch_preset_sphere_wire(int lod) +{ + BLI_assert(lod >= 0 && lod <= 1); + BLI_assert(BLI_thread_is_main()); + + if (lod == 0) { + return g_presets_3d.batch.sphere_wire_low; + } + else { + return g_presets_3d.batch.sphere_wire_med; + } +} + +/** \} */ + + +void gpu_batch_presets_init(void) +{ + /* Hard coded resolution */ + g_presets_3d.batch.sphere_low = gpu_batch_sphere(8, 16); + gpu_batch_presets_register(g_presets_3d.batch.sphere_low); + + g_presets_3d.batch.sphere_med = gpu_batch_sphere(16, 10); + gpu_batch_presets_register(g_presets_3d.batch.sphere_med); + + g_presets_3d.batch.sphere_high = gpu_batch_sphere(32, 24); + gpu_batch_presets_register(g_presets_3d.batch.sphere_high); + + g_presets_3d.batch.sphere_wire_low = batch_sphere_wire(6, 8); + gpu_batch_presets_register(g_presets_3d.batch.sphere_wire_low); + + g_presets_3d.batch.sphere_wire_med = batch_sphere_wire(8, 16); + gpu_batch_presets_register(g_presets_3d.batch.sphere_wire_med); +} + +void gpu_batch_presets_register(Gwn_Batch *preset_batch) +{ + BLI_addtail(&presets_list, BLI_genericNodeN(preset_batch)); +} + +void gpu_batch_presets_reset(void) +{ + /* Reset vao caches for these every time we switch opengl context. + * This way they will draw correctly for each window. */ + LinkData *link = presets_list.first; + for (link = presets_list.first; link; link = link->next) { + Gwn_Batch *preset = link->data; + gwn_batch_vao_cache_clear(preset); + } +} + +void gpu_batch_presets_exit(void) +{ + LinkData *link; + while ((link = BLI_pophead(&presets_list))) { + Gwn_Batch *preset = link->data; + GWN_batch_discard(preset); + MEM_freeN(link); + } +} diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index a35f678f8dc..df8dbb03284 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -35,8 +35,6 @@ #include <stddef.h> #include <string.h> -#include "GPU_glew.h" - #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" @@ -55,914 +53,29 @@ #include "GPU_buffers.h" #include "GPU_draw.h" -#include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_batch.h" #include "bmesh.h" -typedef enum { - GPU_BUFFER_VERTEX_STATE = (1 << 0), - GPU_BUFFER_NORMAL_STATE = (1 << 1), - GPU_BUFFER_TEXCOORD_UNIT_0_STATE = (1 << 2), - GPU_BUFFER_TEXCOORD_UNIT_2_STATE = (1 << 3), - GPU_BUFFER_COLOR_STATE = (1 << 4), - GPU_BUFFER_ELEMENT_STATE = (1 << 5), -} GPUBufferState; - -typedef struct { - GLenum gl_buffer_type; - int num_components; /* number of data components for one vertex */ -} GPUBufferTypeSettings; - - -static size_t gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type); - -static const GPUBufferTypeSettings gpu_buffer_type_settings[] = { - /* vertex */ - {GL_ARRAY_BUFFER, 3}, - /* normal */ - {GL_ARRAY_BUFFER, 4}, /* we copy 3 shorts per normal but we add a fourth for alignment */ - /* mcol */ - {GL_ARRAY_BUFFER, 4}, - /* uv */ - {GL_ARRAY_BUFFER, 2}, - /* uv for texpaint */ - {GL_ARRAY_BUFFER, 4}, - /* edge */ - {GL_ELEMENT_ARRAY_BUFFER, 2}, - /* uv edge */ - {GL_ELEMENT_ARRAY_BUFFER, 4}, - /* triangles, 1 point since we are allocating from tottriangle points, which account for all points */ - {GL_ELEMENT_ARRAY_BUFFER, 1}, -}; - -#define MAX_GPU_ATTRIB_DATA 32 - -#define BUFFER_OFFSET(n) ((GLubyte *)NULL + (n)) - -static GPUBufferState GLStates = 0; -static GPUAttrib attribData[MAX_GPU_ATTRIB_DATA] = { { -1, 0, 0 } }; - static ThreadMutex buffer_mutex = BLI_MUTEX_INITIALIZER; /* multires global buffer, can be used for many grids having the same grid size */ typedef struct GridCommonGPUBuffer { - GPUBuffer *mres_buffer; + Gwn_IndexBuf *mres_buffer; int mres_prev_gridsize; - GLenum mres_prev_index_type; unsigned mres_prev_totquad; } GridCommonGPUBuffer; -void GPU_buffer_material_finalize(GPUDrawObject *gdo, GPUBufferMaterial *matinfo, int totmat) -{ - int i, curmat, curelement; - - /* count the number of materials used by this DerivedMesh */ - for (i = 0; i < totmat; i++) { - if (matinfo[i].totelements > 0) - gdo->totmaterial++; - } - - /* allocate an array of materials used by this DerivedMesh */ - gdo->materials = MEM_mallocN(sizeof(GPUBufferMaterial) * gdo->totmaterial, - "GPUDrawObject.materials"); - - /* initialize the materials array */ - for (i = 0, curmat = 0, curelement = 0; i < totmat; i++) { - if (matinfo[i].totelements > 0) { - gdo->materials[curmat] = matinfo[i]; - gdo->materials[curmat].start = curelement; - gdo->materials[curmat].mat_nr = i; - gdo->materials[curmat].polys = MEM_mallocN(sizeof(int) * matinfo[i].totpolys, "GPUBufferMaterial.polys"); - - curelement += matinfo[i].totelements; - curmat++; - } - } - - MEM_freeN(matinfo); -} - - -/* stores recently-deleted buffers so that new buffers won't have to - * be recreated as often - * - * only one instance of this pool is created, stored in - * gpu_buffer_pool - * - * note that the number of buffers in the pool is usually limited to - * MAX_FREE_GPU_BUFFERS, but this limit may be exceeded temporarily - * when a GPUBuffer is released outside the main thread; due to OpenGL - * restrictions it cannot be immediately released - */ -typedef struct GPUBufferPool { - /* number of allocated buffers stored */ - int totbuf; - /* actual allocated length of the arrays */ - int maxsize; - GPUBuffer **buffers; -} GPUBufferPool; -#define MAX_FREE_GPU_BUFFERS 8 - -/* create a new GPUBufferPool */ -static GPUBufferPool *gpu_buffer_pool_new(void) -{ - GPUBufferPool *pool; - - pool = MEM_callocN(sizeof(GPUBufferPool), "GPUBuffer_Pool"); - - pool->maxsize = MAX_FREE_GPU_BUFFERS; - pool->buffers = MEM_mallocN(sizeof(*pool->buffers) * pool->maxsize, - "GPUBufferPool.buffers"); - return pool; -} - -/* remove a GPUBuffer from the pool (does not free the GPUBuffer) */ -static void gpu_buffer_pool_remove_index(GPUBufferPool *pool, int index) -{ - int i; - - if (!pool || index < 0 || index >= pool->totbuf) - return; - - /* shift entries down, overwriting the buffer at `index' */ - for (i = index; i < pool->totbuf - 1; i++) - pool->buffers[i] = pool->buffers[i + 1]; - - /* clear the last entry */ - if (pool->totbuf > 0) - pool->buffers[pool->totbuf - 1] = NULL; - - pool->totbuf--; -} - -/* delete the last entry in the pool */ -static void gpu_buffer_pool_delete_last(GPUBufferPool *pool) -{ - GPUBuffer *last; - - if (pool->totbuf <= 0) - return; - - /* get the last entry */ - if (!(last = pool->buffers[pool->totbuf - 1])) - return; - - /* delete the buffer's data */ - glDeleteBuffers(1, &last->id); - - /* delete the buffer and remove from pool */ - MEM_freeN(last); - pool->totbuf--; - pool->buffers[pool->totbuf] = NULL; -} - -/* free a GPUBufferPool; also frees the data in the pool's - * GPUBuffers */ -static void gpu_buffer_pool_free(GPUBufferPool *pool) -{ - if (!pool) - return; - - while (pool->totbuf) - gpu_buffer_pool_delete_last(pool); - - MEM_freeN(pool->buffers); - MEM_freeN(pool); -} - -static void gpu_buffer_pool_free_unused(GPUBufferPool *pool) -{ - if (!pool) - return; - - BLI_mutex_lock(&buffer_mutex); - - while (pool->totbuf) - gpu_buffer_pool_delete_last(pool); - - BLI_mutex_unlock(&buffer_mutex); -} - -static GPUBufferPool *gpu_buffer_pool = NULL; -static GPUBufferPool *gpu_get_global_buffer_pool(void) -{ - /* initialize the pool */ - if (!gpu_buffer_pool) - gpu_buffer_pool = gpu_buffer_pool_new(); - - return gpu_buffer_pool; -} - -void GPU_global_buffer_pool_free(void) -{ - gpu_buffer_pool_free(gpu_buffer_pool); - gpu_buffer_pool = NULL; -} - -void GPU_global_buffer_pool_free_unused(void) -{ - gpu_buffer_pool_free_unused(gpu_buffer_pool); -} - -/* get a GPUBuffer of at least `size' bytes; uses one from the buffer - * pool if possible, otherwise creates a new one - * - * Thread-unsafe version for internal usage only. - */ -static GPUBuffer *gpu_buffer_alloc_intern(size_t size) -{ - GPUBufferPool *pool; - GPUBuffer *buf; - int i, bestfit = -1; - size_t bufsize; - - /* bad case, leads to leak of buf since buf->pointer will allocate - * NULL, leading to return without cleanup. In any case better detect early - * psy-fi */ - if (size == 0) - return NULL; - - pool = gpu_get_global_buffer_pool(); - - /* not sure if this buffer pool code has been profiled much, - * seems to me that the graphics driver and system memory - * management might do this stuff anyway. --nicholas - */ - - /* check the global buffer pool for a recently-deleted buffer - * that is at least as big as the request, but not more than - * twice as big */ - for (i = 0; i < pool->totbuf; i++) { - bufsize = pool->buffers[i]->size; - - /* check for an exact size match */ - if (bufsize == size) { - bestfit = i; - break; - } - /* smaller buffers won't fit data and buffers at least - * twice as big are a waste of memory */ - else if (bufsize > size && size > (bufsize / 2)) { - /* is it closer to the required size than the - * last appropriate buffer found. try to save - * memory */ - if (bestfit == -1 || pool->buffers[bestfit]->size > bufsize) { - bestfit = i; - } - } - } - - /* if an acceptable buffer was found in the pool, remove it - * from the pool and return it */ - if (bestfit != -1) { - buf = pool->buffers[bestfit]; - gpu_buffer_pool_remove_index(pool, bestfit); - return buf; - } - - /* no acceptable buffer found in the pool, create a new one */ - buf = MEM_callocN(sizeof(GPUBuffer), "GPUBuffer"); - buf->size = size; - - glGenBuffers(1, &buf->id); - glBindBuffer(GL_ARRAY_BUFFER, buf->id); - glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - return buf; -} - -/* Same as above, but safe for threading. */ -GPUBuffer *GPU_buffer_alloc(size_t size) -{ - GPUBuffer *buffer; - - if (size == 0) { - /* Early out, no lock needed in this case. */ - return NULL; - } - - BLI_mutex_lock(&buffer_mutex); - buffer = gpu_buffer_alloc_intern(size); - BLI_mutex_unlock(&buffer_mutex); - - return buffer; -} - -/* release a GPUBuffer; does not free the actual buffer or its data, - * but rather moves it to the pool of recently-freed buffers for - * possible re-use - * - * Thread-unsafe version for internal usage only. - */ -static void gpu_buffer_free_intern(GPUBuffer *buffer) -{ - GPUBufferPool *pool; - int i; - - if (!buffer) - return; - - pool = gpu_get_global_buffer_pool(); - - /* free the last used buffer in the queue if no more space, but only - * if we are in the main thread. for e.g. rendering or baking it can - * happen that we are in other thread and can't call OpenGL, in that - * case cleanup will be done GPU_buffer_pool_free_unused */ - if (BLI_thread_is_main()) { - /* in main thread, safe to decrease size of pool back - * down to MAX_FREE_GPU_BUFFERS */ - while (pool->totbuf >= MAX_FREE_GPU_BUFFERS) - gpu_buffer_pool_delete_last(pool); - } - else { - /* outside of main thread, can't safely delete the - * buffer, so increase pool size */ - if (pool->maxsize == pool->totbuf) { - pool->maxsize += MAX_FREE_GPU_BUFFERS; - pool->buffers = MEM_reallocN(pool->buffers, - sizeof(GPUBuffer *) * pool->maxsize); - } - } - - /* shift pool entries up by one */ - for (i = pool->totbuf; i > 0; i--) - pool->buffers[i] = pool->buffers[i - 1]; - - /* insert the buffer into the beginning of the pool */ - pool->buffers[0] = buffer; - pool->totbuf++; -} - -/* Same as above, but safe for threading. */ -void GPU_buffer_free(GPUBuffer *buffer) -{ - if (!buffer) { - /* Early output, no need to lock in this case, */ - return; - } - - BLI_mutex_lock(&buffer_mutex); - gpu_buffer_free_intern(buffer); - BLI_mutex_unlock(&buffer_mutex); -} - -void GPU_drawobject_free(DerivedMesh *dm) -{ - GPUDrawObject *gdo; - int i; - - if (!dm || !(gdo = dm->drawObject)) - return; - - for (i = 0; i < gdo->totmaterial; i++) { - if (gdo->materials[i].polys) - MEM_freeN(gdo->materials[i].polys); - } - - MEM_freeN(gdo->materials); - if (gdo->vert_points) - MEM_freeN(gdo->vert_points); -#ifdef USE_GPU_POINT_LINK - MEM_freeN(gdo->vert_points_mem); -#endif - GPU_buffer_free(gdo->points); - GPU_buffer_free(gdo->normals); - GPU_buffer_free(gdo->uv); - GPU_buffer_free(gdo->uv_tex); - GPU_buffer_free(gdo->colors); - GPU_buffer_free(gdo->edges); - GPU_buffer_free(gdo->uvedges); - GPU_buffer_free(gdo->triangles); - - MEM_freeN(gdo); - dm->drawObject = NULL; -} - -static GPUBuffer *gpu_try_realloc(GPUBufferPool *pool, GPUBuffer *buffer, size_t size) -{ - /* try freeing an entry from the pool - * and reallocating the buffer */ - gpu_buffer_free_intern(buffer); - - buffer = NULL; - - while (pool->totbuf && !buffer) { - gpu_buffer_pool_delete_last(pool); - buffer = gpu_buffer_alloc_intern(size); - } - - return buffer; -} - -static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object, - int type, void *user, GPUBuffer *buffer) -{ - GPUBufferPool *pool; - float *varray; - int *mat_orig_to_new; - int i; - const GPUBufferTypeSettings *ts = &gpu_buffer_type_settings[type]; - GLenum target = ts->gl_buffer_type; - size_t size = gpu_buffer_size_from_type(dm, type); - GLboolean uploaded; - - pool = gpu_get_global_buffer_pool(); - - BLI_mutex_lock(&buffer_mutex); - - /* alloc a GPUBuffer; fall back to legacy mode on failure */ - if (!buffer) { - if (!(buffer = gpu_buffer_alloc_intern(size))) { - BLI_mutex_unlock(&buffer_mutex); - return NULL; - } - } - - mat_orig_to_new = MEM_mallocN(sizeof(*mat_orig_to_new) * dm->totmat, - "GPU_buffer_setup.mat_orig_to_new"); - for (i = 0; i < object->totmaterial; i++) { - /* map from original material index to new - * GPUBufferMaterial index */ - mat_orig_to_new[object->materials[i].mat_nr] = i; - } - - /* bind the buffer and discard previous data, - * avoids stalling gpu */ - glBindBuffer(target, buffer->id); - glBufferData(target, buffer->size, NULL, GL_STATIC_DRAW); - - /* attempt to map the buffer */ - if (!(varray = glMapBuffer(target, GL_WRITE_ONLY))) { - buffer = gpu_try_realloc(pool, buffer, size); - - /* allocation still failed; unfortunately we need to exit */ - if (!(buffer && (varray = glMapBuffer(target, GL_WRITE_ONLY)))) { - if (buffer) - gpu_buffer_free_intern(buffer); - BLI_mutex_unlock(&buffer_mutex); - return NULL; - } - } - - uploaded = GL_FALSE; - - /* attempt to upload the data to the VBO */ - while (uploaded == GL_FALSE) { - dm->copy_gpu_data(dm, type, varray, mat_orig_to_new, user); - /* glUnmapBuffer returns GL_FALSE if - * the data store is corrupted; retry - * in that case */ - uploaded = glUnmapBuffer(target); - } - glBindBuffer(target, 0); - - MEM_freeN(mat_orig_to_new); - - BLI_mutex_unlock(&buffer_mutex); - - return buffer; -} - -/* get the GPUDrawObject buffer associated with a type */ -static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBufferType type) -{ - switch (type) { - case GPU_BUFFER_VERTEX: - return &gdo->points; - case GPU_BUFFER_NORMAL: - return &gdo->normals; - case GPU_BUFFER_COLOR: - return &gdo->colors; - case GPU_BUFFER_UV: - return &gdo->uv; - case GPU_BUFFER_UV_TEXPAINT: - return &gdo->uv_tex; - case GPU_BUFFER_EDGE: - return &gdo->edges; - case GPU_BUFFER_UVEDGE: - return &gdo->uvedges; - case GPU_BUFFER_TRIANGLES: - return &gdo->triangles; - default: - return NULL; - } -} - -/* get the amount of space to allocate for a buffer of a particular type */ -static size_t gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type) -{ - const int components = gpu_buffer_type_settings[type].num_components; - switch (type) { - case GPU_BUFFER_VERTEX: - return sizeof(float) * components * (dm->drawObject->tot_loop_verts + dm->drawObject->tot_loose_point); - case GPU_BUFFER_NORMAL: - return sizeof(short) * components * dm->drawObject->tot_loop_verts; - case GPU_BUFFER_COLOR: - return sizeof(char) * components * dm->drawObject->tot_loop_verts; - case GPU_BUFFER_UV: - return sizeof(float) * components * dm->drawObject->tot_loop_verts; - case GPU_BUFFER_UV_TEXPAINT: - return sizeof(float) * components * dm->drawObject->tot_loop_verts; - case GPU_BUFFER_EDGE: - return sizeof(int) * components * dm->drawObject->totedge; - case GPU_BUFFER_UVEDGE: - return sizeof(int) * components * dm->drawObject->tot_loop_verts; - case GPU_BUFFER_TRIANGLES: - return sizeof(int) * components * dm->drawObject->tot_triangle_point; - default: - return -1; - } -} - -/* call gpu_buffer_setup with settings for a particular type of buffer */ -static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type, GPUBuffer *buf) -{ - void *user_data = NULL; - - /* special handling for MCol and UV buffers */ - if (type == GPU_BUFFER_COLOR) { - if (!(user_data = DM_get_loop_data_layer(dm, dm->drawObject->colType))) - return NULL; - } - else if (ELEM(type, GPU_BUFFER_UV, GPU_BUFFER_UV_TEXPAINT)) { - if (!DM_get_loop_data_layer(dm, CD_MLOOPUV)) - return NULL; - } - - buf = gpu_buffer_setup(dm, dm->drawObject, type, user_data, buf); - - return buf; -} - -/* get the buffer of `type', initializing the GPUDrawObject and - * buffer if needed */ -static GPUBuffer *gpu_buffer_setup_common(DerivedMesh *dm, GPUBufferType type, bool update) -{ - GPUBuffer **buf; - - if (!dm->drawObject) - dm->drawObject = dm->gpuObjectNew(dm); - - buf = gpu_drawobject_buffer_from_type(dm->drawObject, type); - if (!(*buf)) - *buf = gpu_buffer_setup_type(dm, type, NULL); - else if (update) - *buf = gpu_buffer_setup_type(dm, type, *buf); - - return *buf; -} - -void GPU_vertex_setup(DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_VERTEX, false)) - return; - - glEnableClientState(GL_VERTEX_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->points->id); - glVertexPointer(3, GL_FLOAT, 0, 0); - - GLStates |= GPU_BUFFER_VERTEX_STATE; -} - -void GPU_normal_setup(DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_NORMAL, false)) - return; - - glEnableClientState(GL_NORMAL_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->normals->id); - glNormalPointer(GL_SHORT, 4 * sizeof(short), 0); - - GLStates |= GPU_BUFFER_NORMAL_STATE; -} - -void GPU_uv_setup(DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_UV, false)) - return; - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->uv->id); - glTexCoordPointer(2, GL_FLOAT, 0, 0); - - GLStates |= GPU_BUFFER_TEXCOORD_UNIT_0_STATE; -} - -void GPU_texpaint_uv_setup(DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_UV_TEXPAINT, false)) - return; - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->uv_tex->id); - glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), 0); - glClientActiveTexture(GL_TEXTURE2); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), BUFFER_OFFSET(2 * sizeof(float))); - glClientActiveTexture(GL_TEXTURE0); - - GLStates |= GPU_BUFFER_TEXCOORD_UNIT_0_STATE | GPU_BUFFER_TEXCOORD_UNIT_2_STATE; -} - - -void GPU_color_setup(DerivedMesh *dm, int colType) -{ - bool update = false; - - if (!dm->drawObject) { - /* XXX Not really nice, but we need a valid gpu draw object to set the colType... - * Else we would have to add a new param to gpu_buffer_setup_common. */ - dm->drawObject = dm->gpuObjectNew(dm); - dm->dirty &= ~DM_DIRTY_MCOL_UPDATE_DRAW; - dm->drawObject->colType = colType; - } - /* In paint mode, dm may stay the same during stroke, however we still want to update colors! - * Also check in case we changed color type (i.e. which MCol cdlayer we use). */ - else if ((dm->dirty & DM_DIRTY_MCOL_UPDATE_DRAW) || (colType != dm->drawObject->colType)) { - update = true; - dm->dirty &= ~DM_DIRTY_MCOL_UPDATE_DRAW; - dm->drawObject->colType = colType; - } - - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_COLOR, update)) - return; - - glEnableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->colors->id); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, 0); - - GLStates |= GPU_BUFFER_COLOR_STATE; -} - -void GPU_buffer_bind_as_color(GPUBuffer *buffer) -{ - glEnableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, buffer->id); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, 0); - - GLStates |= GPU_BUFFER_COLOR_STATE; -} - - -void GPU_edge_setup(DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_EDGE, false)) - return; - - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_VERTEX, false)) - return; - - glEnableClientState(GL_VERTEX_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->points->id); - glVertexPointer(3, GL_FLOAT, 0, 0); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dm->drawObject->edges->id); - - GLStates |= (GPU_BUFFER_VERTEX_STATE | GPU_BUFFER_ELEMENT_STATE); -} - -void GPU_uvedge_setup(DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_UVEDGE, false)) - return; - - glEnableClientState(GL_VERTEX_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, dm->drawObject->uvedges->id); - glVertexPointer(2, GL_FLOAT, 0, 0); - - GLStates |= GPU_BUFFER_VERTEX_STATE; -} - -void GPU_triangle_setup(struct DerivedMesh *dm) -{ - if (!gpu_buffer_setup_common(dm, GPU_BUFFER_TRIANGLES, false)) - return; - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dm->drawObject->triangles->id); - GLStates |= GPU_BUFFER_ELEMENT_STATE; -} - -static int gpu_typesize(int type) -{ - switch (type) { - case GL_FLOAT: - return sizeof(float); - case GL_INT: - return sizeof(int); - case GL_UNSIGNED_INT: - return sizeof(unsigned int); - case GL_BYTE: - return sizeof(char); - case GL_UNSIGNED_BYTE: - return sizeof(unsigned char); - default: - return 0; - } -} - -int GPU_attrib_element_size(GPUAttrib data[], int numdata) -{ - int i, elementsize = 0; - - for (i = 0; i < numdata; i++) { - int typesize = gpu_typesize(data[i].type); - if (typesize != 0) - elementsize += typesize * data[i].size; - } - return elementsize; -} - -void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata, int element_size) -{ - int i; - int elementsize; - size_t offset = 0; - - for (i = 0; i < MAX_GPU_ATTRIB_DATA; i++) { - if (attribData[i].index != -1) { - glDisableVertexAttribArray(attribData[i].index); - } - else - break; - } - if (element_size == 0) - elementsize = GPU_attrib_element_size(data, numdata); - else - elementsize = element_size; - - glBindBuffer(GL_ARRAY_BUFFER, buffer->id); - - for (i = 0; i < numdata; i++) { - glEnableVertexAttribArray(data[i].index); - int info = 0; - if (data[i].type == GL_UNSIGNED_BYTE) { - info |= GPU_ATTR_INFO_SRGB; - } - glUniform1i(data[i].info_index, info); - - glVertexAttribPointer(data[i].index, data[i].size, data[i].type, - GL_TRUE, elementsize, BUFFER_OFFSET(offset)); - offset += data[i].size * gpu_typesize(data[i].type); - - attribData[i].index = data[i].index; - attribData[i].size = data[i].size; - attribData[i].type = data[i].type; - } - - attribData[numdata].index = -1; -} - -void GPU_interleaved_attrib_unbind(void) -{ - int i; - for (i = 0; i < MAX_GPU_ATTRIB_DATA; i++) { - if (attribData[i].index != -1) { - glDisableVertexAttribArray(attribData[i].index); - } - else - break; - } - attribData[0].index = -1; -} - -void GPU_buffers_unbind(void) -{ - int i; - - if (GLStates & GPU_BUFFER_VERTEX_STATE) - glDisableClientState(GL_VERTEX_ARRAY); - if (GLStates & GPU_BUFFER_NORMAL_STATE) - glDisableClientState(GL_NORMAL_ARRAY); - if (GLStates & GPU_BUFFER_TEXCOORD_UNIT_0_STATE) - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - if (GLStates & GPU_BUFFER_TEXCOORD_UNIT_2_STATE) { - glClientActiveTexture(GL_TEXTURE2); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glClientActiveTexture(GL_TEXTURE0); - } - if (GLStates & GPU_BUFFER_COLOR_STATE) - glDisableClientState(GL_COLOR_ARRAY); - if (GLStates & GPU_BUFFER_ELEMENT_STATE) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - GLStates &= ~(GPU_BUFFER_VERTEX_STATE | GPU_BUFFER_NORMAL_STATE | - GPU_BUFFER_TEXCOORD_UNIT_0_STATE | GPU_BUFFER_TEXCOORD_UNIT_2_STATE | - GPU_BUFFER_COLOR_STATE | GPU_BUFFER_ELEMENT_STATE); - - for (i = 0; i < MAX_GPU_ATTRIB_DATA; i++) { - if (attribData[i].index != -1) { - glDisableVertexAttribArray(attribData[i].index); - } - else - break; - } - attribData[0].index = -1; - - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -void GPU_color_switch(int mode) -{ - if (mode) { - if (!(GLStates & GPU_BUFFER_COLOR_STATE)) - glEnableClientState(GL_COLOR_ARRAY); - GLStates |= GPU_BUFFER_COLOR_STATE; - } - else { - if (GLStates & GPU_BUFFER_COLOR_STATE) - glDisableClientState(GL_COLOR_ARRAY); - GLStates &= ~GPU_BUFFER_COLOR_STATE; - } -} - -static int gpu_binding_type_gl[] = -{ - GL_ARRAY_BUFFER, - GL_ELEMENT_ARRAY_BUFFER -}; - -void *GPU_buffer_lock(GPUBuffer *buffer, GPUBindingType binding) -{ - float *varray; - int bindtypegl; - - if (!buffer) - return 0; - - bindtypegl = gpu_binding_type_gl[binding]; - glBindBuffer(bindtypegl, buffer->id); - varray = glMapBuffer(bindtypegl, GL_WRITE_ONLY); - return varray; -} - -void *GPU_buffer_lock_stream(GPUBuffer *buffer, GPUBindingType binding) -{ - float *varray; - int bindtypegl; - - if (!buffer) - return 0; - - bindtypegl = gpu_binding_type_gl[binding]; - glBindBuffer(bindtypegl, buffer->id); - /* discard previous data, avoid stalling gpu */ - glBufferData(bindtypegl, buffer->size, 0, GL_STREAM_DRAW); - varray = glMapBuffer(bindtypegl, GL_WRITE_ONLY); - return varray; -} - -void GPU_buffer_unlock(GPUBuffer *UNUSED(buffer), GPUBindingType binding) -{ - int bindtypegl = gpu_binding_type_gl[binding]; - /* note: this operation can fail, could return - * an error code from this function? */ - glUnmapBuffer(bindtypegl); - glBindBuffer(bindtypegl, 0); -} - -void GPU_buffer_bind(GPUBuffer *buffer, GPUBindingType binding) -{ - int bindtypegl = gpu_binding_type_gl[binding]; - glBindBuffer(bindtypegl, buffer->id); -} - -void GPU_buffer_unbind(GPUBuffer *UNUSED(buffer), GPUBindingType binding) -{ - int bindtypegl = gpu_binding_type_gl[binding]; - glBindBuffer(bindtypegl, 0); -} - -/* used for drawing edges */ -void GPU_buffer_draw_elements(GPUBuffer *UNUSED(elements), unsigned int mode, int start, int count) -{ - glDrawElements(mode, count, GL_UNSIGNED_INT, BUFFER_OFFSET(start * sizeof(unsigned int))); -} - - /* XXX: the rest of the code in this file is used for optimized PBVH * drawing and doesn't interact at all with the buffer code above */ -/* Convenience struct for building the VBO. */ -typedef struct { - float co[3]; - short no[3]; - - /* inserting this to align the 'color' field to a four-byte - * boundary; drastically increases viewport performance on my - * drivers (Gallium/Radeon) --nicholasbishop */ - char pad[2]; - - unsigned char color[3]; -} VertexBufferFormat; - struct GPU_PBVH_Buffers { - /* opengl buffer handles */ - GPUBuffer *vert_buf, *index_buf, *index_buf_fast; - GLenum index_type; + Gwn_IndexBuf *index_buf, *index_buf_fast; + Gwn_VertBuf *vert_buf; - int *baseelemarray; - void **baseindex; + Gwn_Batch *triangles; + Gwn_Batch *triangles_fast; /* mesh pointers in case buffer allocation fails */ const MPoly *mpoly; @@ -995,10 +108,73 @@ struct GPU_PBVH_Buffers { bool show_diffuse_color; bool show_mask; - bool use_matcaps; float diffuse_color[4]; }; +static struct { + uint pos, nor, col; +} g_vbo_id = {0}; + +static void gpu_material_diffuse_get(int UNUSED(nr), float diff[4]) +{ + /* TODO: sculpt diffuse color option not supported in 2.8 yet. */ + diff[0] = 0.8f; + diff[1] = 0.8f; + diff[2] = 0.8f; + diff[3] = 1.0f; +} + +/* Allocates a non-initialized buffer to be sent to GPU. + * Return is false it indicates that the memory map failed. */ +static bool gpu_pbvh_vert_buf_data_set(GPU_PBVH_Buffers *buffers, unsigned int vert_ct) +{ + if (buffers->vert_buf == NULL) { + /* Initialize vertex buffer */ + /* match 'VertexBufferFormat' */ + + static Gwn_VertFormat format = {0}; + if (format.attrib_ct == 0) { + g_vbo_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + g_vbo_id.nor = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_I16, 3, GWN_FETCH_INT_TO_FLOAT_UNIT); + g_vbo_id.col = GWN_vertformat_attr_add(&format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT); + } +#if 0 + buffers->vert_buf = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_DYNAMIC); + GWN_vertbuf_data_alloc(buffers->vert_buf, vert_ct); + } + else if (vert_ct != buffers->vert_buf->vertex_ct) { + GWN_vertbuf_data_resize(buffers->vert_buf, vert_ct); + } +#else + buffers->vert_buf = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_STATIC); + } + GWN_vertbuf_data_alloc(buffers->vert_buf, vert_ct); +#endif + return buffers->vert_buf->data != NULL; +} + +static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers) +{ + /* force flushing to the GPU */ + if (buffers->vert_buf->data) { + GWN_vertbuf_use(buffers->vert_buf); + } + + if (buffers->triangles == NULL) { + buffers->triangles = GWN_batch_create( + GWN_PRIM_TRIS, buffers->vert_buf, + /* can be NULL */ + buffers->index_buf); + } + + if ((buffers->triangles_fast == NULL) && buffers->index_buf_fast) { + buffers->triangles_fast = GWN_batch_create( + GWN_PRIM_TRIS, buffers->vert_buf, + /* can be NULL */ + buffers->index_buf_fast); + } +} + static float gpu_color_from_mask(float mask) { return 1.0f - mask * 0.75f; @@ -1040,25 +216,20 @@ void GPU_pbvh_mesh_buffers_update( { const bool show_diffuse_color = (update_flags & GPU_PBVH_BUFFERS_SHOW_DIFFUSE_COLOR) != 0; const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; - VertexBufferFormat *vert_data; - int i; buffers->vmask = vmask; buffers->show_diffuse_color = show_diffuse_color; buffers->show_mask = show_mask; - buffers->use_matcaps = GPU_material_use_matcaps_get(); { int totelem = (buffers->smooth ? totvert : (buffers->tot_tri * 3)); float diffuse_color[4] = {0.8f, 0.8f, 0.8f, 0.8f}; - if (buffers->use_matcaps) - diffuse_color[0] = diffuse_color[1] = diffuse_color[2] = 1.0; - else if (show_diffuse_color) { + if (show_diffuse_color) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[0]]; const MPoly *mp = &buffers->mpoly[lt->poly]; - GPU_material_diffuse_get(mp->mat_nr + 1, diffuse_color); + gpu_material_diffuse_get(mp->mat_nr + 1, diffuse_color); } copy_v4_v4(buffers->diffuse_color, diffuse_color); @@ -1067,35 +238,29 @@ void GPU_pbvh_mesh_buffers_update( rgba_float_to_uchar(diffuse_color_ub, diffuse_color); /* Build VBO */ - if (buffers->vert_buf) - GPU_buffer_free(buffers->vert_buf); - buffers->vert_buf = GPU_buffer_alloc(sizeof(VertexBufferFormat) * totelem); - vert_data = GPU_buffer_lock(buffers->vert_buf, GPU_BINDING_ARRAY); - - if (vert_data) { + if (gpu_pbvh_vert_buf_data_set(buffers, totelem)) { /* Vertex data is shared if smooth-shaded, but separate * copies are made for flat shading because normals * shouldn't be shared. */ if (buffers->smooth) { - for (i = 0; i < totvert; ++i) { + for (uint i = 0; i < totvert; ++i) { const MVert *v = &mvert[vert_indices[i]]; - VertexBufferFormat *out = vert_data + i; - - copy_v3_v3(out->co, v->co); - memcpy(out->no, v->no, sizeof(short) * 3); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, i, v->co); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, i, v->no); } - for (i = 0; i < buffers->face_indices_len; i++) { + for (uint i = 0; i < buffers->face_indices_len; i++) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]]; for (uint j = 0; j < 3; j++) { - VertexBufferFormat *out = vert_data + face_vert_indices[i][j]; - + int vidx = face_vert_indices[i][j]; if (vmask && show_mask) { - uint v_index = buffers->mloop[lt->tri[j]].v; - gpu_color_from_mask_copy(vmask[v_index], diffuse_color, out->color); + int v_index = buffers->mloop[lt->tri[j]].v; + uchar color_ub[3]; + gpu_color_from_mask_copy(vmask[v_index], diffuse_color, color_ub); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, color_ub); } else { - copy_v3_v3_uchar(out->color, diffuse_color_ub); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, diffuse_color_ub); } } } @@ -1104,8 +269,9 @@ void GPU_pbvh_mesh_buffers_update( /* calculate normal for each polygon only once */ unsigned int mpoly_prev = UINT_MAX; short no[3]; + int vbo_index = 0; - for (i = 0; i < buffers->face_indices_len; ++i) { + for (uint i = 0; i < buffers->face_indices_len; i++) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]]; const unsigned int vtri[3] = { buffers->mloop[lt->tri[0]].v, @@ -1136,22 +302,17 @@ void GPU_pbvh_mesh_buffers_update( for (uint j = 0; j < 3; j++) { const MVert *v = &mvert[vtri[j]]; - VertexBufferFormat *out = vert_data; - copy_v3_v3(out->co, v->co); - copy_v3_v3_short(out->no, no); - copy_v3_v3_uchar(out->color, color_ub); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, vbo_index, v->co); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub); - vert_data++; + vbo_index++; } } } - GPU_buffer_unlock(buffers->vert_buf, GPU_BINDING_ARRAY); - } - else { - GPU_buffer_free(buffers->vert_buf); - buffers->vert_buf = NULL; + gpu_pbvh_batch_init(buffers); } } @@ -1166,16 +327,20 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build( const int face_indices_len) { GPU_PBVH_Buffers *buffers; - unsigned short *tri_data; - int i, j, tottri; + int i, tottri; buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers"); - buffers->index_type = GL_UNSIGNED_SHORT; + + /* smooth or flat for all */ +#if 0 buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH; +#else + /* for DrawManager we dont support mixed smooth/flat */ + buffers->smooth = (mpoly[0].flag & ME_SMOOTH) != 0; +#endif buffers->show_diffuse_color = false; buffers->show_mask = true; - buffers->use_matcaps = false; /* Count the number of visible triangles */ for (i = 0, tottri = 0; i < face_indices_len; ++i) { @@ -1200,35 +365,28 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build( * shading requires separate vertex normals so an index buffer is * can't be used there. */ if (buffers->smooth) { - buffers->index_buf = GPU_buffer_alloc(sizeof(unsigned short) * tottri * 3); - buffers->is_index_buf_global = false; - } - - if (buffers->index_buf) { /* Fill the triangle buffer */ - tri_data = GPU_buffer_lock(buffers->index_buf, GPU_BINDING_INDEX); - if (tri_data) { - for (i = 0; i < face_indices_len; ++i) { - const MLoopTri *lt = &looptri[face_indices[i]]; - - /* Skip hidden faces */ - if (paint_is_face_hidden(lt, mvert, mloop)) - continue; - - for (j = 0; j < 3; ++j) { - *tri_data = face_vert_indices[i][j]; - tri_data++; - } - } - GPU_buffer_unlock(buffers->index_buf, GPU_BINDING_INDEX); + buffers->index_buf = NULL; + Gwn_IndexBufBuilder elb; + GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, tottri, INT_MAX); + + for (i = 0; i < face_indices_len; ++i) { + const MLoopTri *lt = &looptri[face_indices[i]]; + + /* Skip hidden faces */ + if (paint_is_face_hidden(lt, mvert, mloop)) + continue; + + GWN_indexbuf_add_tri_verts(&elb, UNPACK3(face_vert_indices[i])); } - else { - if (!buffers->is_index_buf_global) { - GPU_buffer_free(buffers->index_buf); - } - buffers->index_buf = NULL; - buffers->is_index_buf_global = false; + buffers->index_buf = GWN_indexbuf_build(&elb); + } + else { + if (!buffers->is_index_buf_global) { + GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf); } + buffers->index_buf = NULL; + buffers->is_index_buf_global = false; } buffers->tot_tri = tottri; @@ -1251,61 +409,59 @@ void GPU_pbvh_grid_buffers_update( { const bool show_diffuse_color = (update_flags & GPU_PBVH_BUFFERS_SHOW_DIFFUSE_COLOR) != 0; const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; - VertexBufferFormat *vert_data; int i, j, k, x, y; buffers->show_diffuse_color = show_diffuse_color; buffers->show_mask = show_mask; - buffers->use_matcaps = GPU_material_use_matcaps_get(); buffers->smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH; /* Build VBO */ - if (buffers->vert_buf) { + if (buffers->index_buf) { const int has_mask = key->has_mask; float diffuse_color[4] = {0.8f, 0.8f, 0.8f, 1.0f}; - if (buffers->use_matcaps) - diffuse_color[0] = diffuse_color[1] = diffuse_color[2] = 1.0; - else if (show_diffuse_color) { + if (show_diffuse_color) { const DMFlagMat *flags = &grid_flag_mats[grid_indices[0]]; - GPU_material_diffuse_get(flags->mat_nr + 1, diffuse_color); + gpu_material_diffuse_get(flags->mat_nr + 1, diffuse_color); } copy_v4_v4(buffers->diffuse_color, diffuse_color); - vert_data = GPU_buffer_lock_stream(buffers->vert_buf, GPU_BINDING_ARRAY); - if (vert_data) { + uint vbo_index_offset = 0; + /* Build VBO */ + if (gpu_pbvh_vert_buf_data_set(buffers, totgrid * key->grid_area)) { for (i = 0; i < totgrid; ++i) { - VertexBufferFormat *vd = vert_data; CCGElem *grid = grids[grid_indices[i]]; + int vbo_index = vbo_index_offset; for (y = 0; y < key->grid_size; y++) { for (x = 0; x < key->grid_size; x++) { CCGElem *elem = CCG_grid_elem(key, grid, x, y); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, vbo_index, CCG_elem_co(key, elem)); - copy_v3_v3(vd->co, CCG_elem_co(key, elem)); if (buffers->smooth) { - normal_float_to_short_v3(vd->no, CCG_elem_no(key, elem)); + short no_short[3]; + normal_float_to_short_v3(no_short, CCG_elem_no(key, elem)); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no_short); if (has_mask) { + uchar color_ub[3]; if (show_mask) { gpu_color_from_mask_copy(*CCG_elem_mask(key, elem), - diffuse_color, vd->color); + diffuse_color, color_ub); } else { - unit_float_to_uchar_clamp_v3(vd->color, diffuse_color); + unit_float_to_uchar_clamp_v3(color_ub, diffuse_color); } + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub); } } - vd++; + vbo_index += 1; } } if (!buffers->smooth) { - /* for flat shading, recalc normals and set the last vertex of - * each triangle in the index buffer to have the flat normal as - * that is what opengl will use */ for (j = 0; j < key->grid_size - 1; j++) { for (k = 0; k < key->grid_size - 1; k++) { CCGElem *elems[4] = { @@ -1322,10 +478,13 @@ void GPU_pbvh_grid_buffers_update( CCG_elem_co(key, elems[2]), CCG_elem_co(key, elems[3])); - vd = vert_data + (j + 1) * key->grid_size + k; - normal_float_to_short_v3(vd->no, fno); + vbo_index = vbo_index_offset + ((j + 1) * key->grid_size + k); + short no_short[3]; + normal_float_to_short_v3(no_short, fno); + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no_short); if (has_mask) { + uchar color_ub[3]; if (show_mask) { gpu_color_from_mask_quad_copy(key, elems[0], @@ -1333,24 +492,21 @@ void GPU_pbvh_grid_buffers_update( elems[2], elems[3], diffuse_color, - vd->color); + color_ub); } else { - unit_float_to_uchar_clamp_v3(vd->color, diffuse_color); + unit_float_to_uchar_clamp_v3(color_ub, diffuse_color); } + GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub); } } } } - vert_data += key->grid_area; + vbo_index_offset += key->grid_area; } - GPU_buffer_unlock(buffers->vert_buf, GPU_BINDING_ARRAY); - } - else { - GPU_buffer_free(buffers->vert_buf); - buffers->vert_buf = NULL; + gpu_pbvh_batch_init(buffers); } } @@ -1365,57 +521,54 @@ void GPU_pbvh_grid_buffers_update( /* Build the element array buffer of grid indices using either * unsigned shorts or unsigned ints. */ -#define FILL_QUAD_BUFFER(type_, tot_quad_, buffer_) \ +#define FILL_QUAD_BUFFER(max_vert_, tot_quad_, buffer_) \ { \ - type_ *tri_data; \ int offset = 0; \ int i, j, k; \ - buffer_ = GPU_buffer_alloc(sizeof(type_) * (tot_quad_) * 6); \ + \ + Gwn_IndexBufBuilder elb; \ + GWN_indexbuf_init( \ + &elb, GWN_PRIM_TRIS, tot_quad_ * 2, max_vert_); \ \ /* Fill the buffer */ \ - tri_data = GPU_buffer_lock(buffer_, GPU_BINDING_INDEX); \ - if (tri_data) { \ - for (i = 0; i < totgrid; ++i) { \ - BLI_bitmap *gh = NULL; \ - if (grid_hidden) \ - gh = grid_hidden[(grid_indices)[i]]; \ + for (i = 0; i < totgrid; ++i) { \ + BLI_bitmap *gh = NULL; \ + if (grid_hidden) \ + gh = grid_hidden[(grid_indices)[i]]; \ \ - for (j = 0; j < gridsize - 1; ++j) { \ - for (k = 0; k < gridsize - 1; ++k) { \ - /* Skip hidden grid face */ \ - if (gh && \ - paint_is_grid_face_hidden(gh, \ - gridsize, k, j)) \ - continue; \ - \ - *(tri_data++) = offset + j * gridsize + k + 1; \ - *(tri_data++) = offset + j * gridsize + k; \ - *(tri_data++) = offset + (j + 1) * gridsize + k; \ - \ - *(tri_data++) = offset + (j + 1) * gridsize + k + 1; \ - *(tri_data++) = offset + j * gridsize + k + 1; \ - *(tri_data++) = offset + (j + 1) * gridsize + k; \ - } \ - } \ - \ - offset += gridsize * gridsize; \ + for (j = 0; j < gridsize - 1; ++j) { \ + for (k = 0; k < gridsize - 1; ++k) { \ + /* Skip hidden grid face */ \ + if (gh && paint_is_grid_face_hidden( \ + gh, gridsize, k, j)) \ + { \ + continue; \ + } \ + GWN_indexbuf_add_generic_vert(&elb, offset + j * gridsize + k + 1); \ + GWN_indexbuf_add_generic_vert(&elb, offset + j * gridsize + k); \ + GWN_indexbuf_add_generic_vert(&elb, offset + (j + 1) * gridsize + k); \ + \ + GWN_indexbuf_add_generic_vert(&elb, offset + (j + 1) * gridsize + k + 1); \ + GWN_indexbuf_add_generic_vert(&elb, offset + j * gridsize + k + 1); \ + GWN_indexbuf_add_generic_vert(&elb, offset + (j + 1) * gridsize + k); \ + } \ } \ - GPU_buffer_unlock(buffer_, GPU_BINDING_INDEX); \ - } \ - else { \ - GPU_buffer_free(buffer_); \ - (buffer_) = NULL; \ + \ + offset += gridsize * gridsize; \ } \ + buffer_ = GWN_indexbuf_build(&elb); \ } (void)0 /* end FILL_QUAD_BUFFER */ -static GPUBuffer *gpu_get_grid_buffer( - int gridsize, GLenum *index_type, unsigned *totquad, GridCommonGPUBuffer **grid_common_gpu_buffer) +static Gwn_IndexBuf *gpu_get_grid_buffer( + int gridsize, unsigned *totquad, GridCommonGPUBuffer **grid_common_gpu_buffer, + /* remove this arg when gawain gets base-vertex support! */ + int totgrid) { /* used in the FILL_QUAD_BUFFER macro */ BLI_bitmap * const *grid_hidden = NULL; const int *grid_indices = NULL; - int totgrid = 1; + // int totgrid = 1; GridCommonGPUBuffer *gridbuff = *grid_common_gpu_buffer; @@ -1423,65 +576,48 @@ static GPUBuffer *gpu_get_grid_buffer( *grid_common_gpu_buffer = gridbuff = MEM_mallocN(sizeof(GridCommonGPUBuffer), __func__); gridbuff->mres_buffer = NULL; gridbuff->mres_prev_gridsize = -1; - gridbuff->mres_prev_index_type = 0; gridbuff->mres_prev_totquad = 0; } /* VBO is already built */ if (gridbuff->mres_buffer && gridbuff->mres_prev_gridsize == gridsize) { - *index_type = gridbuff->mres_prev_index_type; *totquad = gridbuff->mres_prev_totquad; return gridbuff->mres_buffer; } /* we can't reuse old, delete the existing buffer */ else if (gridbuff->mres_buffer) { - GPU_buffer_free(gridbuff->mres_buffer); + GWN_indexbuf_discard(gridbuff->mres_buffer); + gridbuff->mres_buffer = NULL; } /* Build new VBO */ - *totquad = (gridsize - 1) * (gridsize - 1); + *totquad = (gridsize - 1) * (gridsize - 1) * totgrid; + int max_vert = gridsize * gridsize * totgrid; - if (gridsize * gridsize < USHRT_MAX) { - *index_type = GL_UNSIGNED_SHORT; - FILL_QUAD_BUFFER(unsigned short, *totquad, gridbuff->mres_buffer); - } - else { - *index_type = GL_UNSIGNED_INT; - FILL_QUAD_BUFFER(unsigned int, *totquad, gridbuff->mres_buffer); - } + FILL_QUAD_BUFFER(max_vert, *totquad, gridbuff->mres_buffer); gridbuff->mres_prev_gridsize = gridsize; - gridbuff->mres_prev_index_type = *index_type; gridbuff->mres_prev_totquad = *totquad; return gridbuff->mres_buffer; } -#define FILL_FAST_BUFFER(type_) \ +#define FILL_FAST_BUFFER() \ { \ - type_ *buffer; \ - buffers->index_buf_fast = GPU_buffer_alloc(sizeof(type_) * 6 * totgrid); \ - buffer = GPU_buffer_lock(buffers->index_buf_fast, GPU_BINDING_INDEX); \ - if (buffer) { \ - int i; \ - for (i = 0; i < totgrid; i++) { \ - int currentquad = i * 6; \ - buffer[currentquad] = i * gridsize * gridsize + gridsize - 1; \ - buffer[currentquad + 1] = i * gridsize * gridsize; \ - buffer[currentquad + 2] = (i + 1) * gridsize * gridsize - gridsize; \ - buffer[currentquad + 3] = (i + 1) * gridsize * gridsize - 1; \ - buffer[currentquad + 4] = i * gridsize * gridsize + gridsize - 1; \ - buffer[currentquad + 5] = (i + 1) * gridsize * gridsize - gridsize; \ - } \ - GPU_buffer_unlock(buffers->index_buf_fast, GPU_BINDING_INDEX); \ - } \ - else { \ - GPU_buffer_free(buffers->index_buf_fast); \ - buffers->index_buf_fast = NULL; \ + Gwn_IndexBufBuilder elb; \ + GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, 6 * totgrid, INT_MAX); \ + for (int i = 0; i < totgrid; i++) { \ + GWN_indexbuf_add_generic_vert(&elb, i * gridsize * gridsize + gridsize - 1); \ + GWN_indexbuf_add_generic_vert(&elb, i * gridsize * gridsize); \ + GWN_indexbuf_add_generic_vert(&elb, (i + 1) * gridsize * gridsize - gridsize); \ + GWN_indexbuf_add_generic_vert(&elb, (i + 1) * gridsize * gridsize - 1); \ + GWN_indexbuf_add_generic_vert(&elb, i * gridsize * gridsize + gridsize - 1); \ + GWN_indexbuf_add_generic_vert(&elb, (i + 1) * gridsize * gridsize - gridsize); \ } \ + buffers->index_buf_fast = GWN_indexbuf_build(&elb); \ } (void)0 GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build( - int *grid_indices, int totgrid, BLI_bitmap **grid_hidden, int gridsize, const CCGKey *key, + int *grid_indices, int totgrid, BLI_bitmap **grid_hidden, int gridsize, const CCGKey *UNUSED(key), GridCommonGPUBuffer **grid_common_gpu_buffer) { GPU_PBVH_Buffers *buffers; @@ -1494,7 +630,6 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build( buffers->show_diffuse_color = false; buffers->show_mask = true; - buffers->use_matcaps = false; /* Count the number of quads */ totquad = BKE_pbvh_count_grid_quads(grid_hidden, grid_indices, totgrid, gridsize); @@ -1504,39 +639,26 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build( return buffers; /* create and fill indices of the fast buffer too */ - if (totgrid * gridsize * gridsize < USHRT_MAX) { - FILL_FAST_BUFFER(unsigned short); - } - else { - FILL_FAST_BUFFER(unsigned int); - } + FILL_FAST_BUFFER(); if (totquad == fully_visible_totquad) { buffers->index_buf = gpu_get_grid_buffer( - gridsize, &buffers->index_type, &buffers->tot_quad, grid_common_gpu_buffer); + gridsize, &buffers->tot_quad, grid_common_gpu_buffer, totgrid); buffers->has_hidden = false; buffers->is_index_buf_global = true; } else { + uint max_vert = totgrid * gridsize * gridsize; buffers->tot_quad = totquad; - if (totgrid * gridsize * gridsize < USHRT_MAX) { - buffers->index_type = GL_UNSIGNED_SHORT; - FILL_QUAD_BUFFER(unsigned short, totquad, buffers->index_buf); - } - else { - buffers->index_type = GL_UNSIGNED_INT; - FILL_QUAD_BUFFER(unsigned int, totquad, buffers->index_buf); - } + FILL_QUAD_BUFFER(max_vert, totquad, buffers->index_buf); - buffers->has_hidden = true; + buffers->has_hidden = false; buffers->is_index_buf_global = false; } +#ifdef USE_BASE_ELEM /* Build coord/normal VBO */ - if (buffers->index_buf) - buffers->vert_buf = GPU_buffer_alloc(sizeof(VertexBufferFormat) * totgrid * key->grid_area); - if (GLEW_ARB_draw_elements_base_vertex /* 3.2 */) { int i; buffers->baseelemarray = MEM_mallocN(sizeof(int) * totgrid * 2, "GPU_PBVH_Buffers.baseelemarray"); @@ -1547,6 +669,7 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build( buffers->baseindex[i] = NULL; } } +#endif return buffers; } @@ -1559,32 +682,44 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build( * index '*v_index' in the 'vert_data' array and '*v_index' is * incremented. */ -static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, - VertexBufferFormat *vert_data, - int *v_index, - const float fno[3], - const float *fmask, - const int cd_vert_mask_offset, - const float diffuse_color[4], - const bool show_mask) +static void gpu_bmesh_vert_to_buffer_copy__gwn( + BMVert *v, + Gwn_VertBuf *vert_buf, + int *v_index, + const float fno[3], + const float *fmask, + const int cd_vert_mask_offset, + const float diffuse_color[4], + const bool show_mask) { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - VertexBufferFormat *vd = &vert_data[*v_index]; /* Set coord, normal, and mask */ - copy_v3_v3(vd->co, v->co); - normal_float_to_short_v3(vd->no, fno ? fno : v->no); + GWN_vertbuf_attr_set(vert_buf, g_vbo_id.pos, *v_index, v->co); - float effective_mask; - if (show_mask) { - effective_mask = fmask ? *fmask - : BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset); - } - else { - effective_mask = 0.0f; + { + short no_short[3]; + normal_float_to_short_v3(no_short, fno ? fno : v->no); + GWN_vertbuf_attr_set(vert_buf, g_vbo_id.nor, *v_index, no_short); } - gpu_color_from_mask_copy(effective_mask, diffuse_color, vd->color); + { + uchar color_ub[3]; + float effective_mask; + if (show_mask) { + effective_mask = fmask ? *fmask + : BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset); + } + else { + effective_mask = 0.0f; + } + + gpu_color_from_mask_copy( + effective_mask, + diffuse_color, + color_ub); + GWN_vertbuf_attr_set(vert_buf, g_vbo_id.col, *v_index, color_ub); + } /* Assign index for use in the triangle index buffer */ /* note: caller must set: bm->elem_index_dirty |= BM_VERT; */ @@ -1643,8 +778,6 @@ void GPU_pbvh_bmesh_buffers_update( { const bool show_diffuse_color = (update_flags & GPU_PBVH_BUFFERS_SHOW_DIFFUSE_COLOR) != 0; const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; - VertexBufferFormat *vert_data; - void *tri_data; int tottri, totvert, maxvert = 0; float diffuse_color[4] = {0.8f, 0.8f, 0.8f, 1.0f}; @@ -1653,7 +786,6 @@ void GPU_pbvh_bmesh_buffers_update( buffers->show_diffuse_color = show_diffuse_color; buffers->show_mask = show_mask; - buffers->use_matcaps = GPU_material_use_matcaps_get(); /* Count visible triangles */ tottri = gpu_bmesh_face_visible_count(bm_faces); @@ -1670,27 +802,19 @@ void GPU_pbvh_bmesh_buffers_update( return; } - if (buffers->use_matcaps) - diffuse_color[0] = diffuse_color[1] = diffuse_color[2] = 1.0; - else if (show_diffuse_color) { + if (show_diffuse_color) { /* due to dynamic nature of dyntopo, only get first material */ GSetIterator gs_iter; BMFace *f; BLI_gsetIterator_init(&gs_iter, bm_faces); f = BLI_gsetIterator_getKey(&gs_iter); - GPU_material_diffuse_get(f->mat_nr + 1, diffuse_color); + gpu_material_diffuse_get(f->mat_nr + 1, diffuse_color); } copy_v4_v4(buffers->diffuse_color, diffuse_color); - /* Initialize vertex buffer */ - if (buffers->vert_buf) - GPU_buffer_free(buffers->vert_buf); - buffers->vert_buf = GPU_buffer_alloc(sizeof(VertexBufferFormat) * totvert); - /* Fill vertex buffer */ - vert_data = GPU_buffer_lock(buffers->vert_buf, GPU_BINDING_ARRAY); - if (vert_data) { + if (gpu_pbvh_vert_buf_data_set(buffers, totvert)) { int v_index = 0; if (buffers->smooth) { @@ -1701,17 +825,19 @@ void GPU_pbvh_bmesh_buffers_update( bm->elem_index_dirty |= BM_VERT; GSET_ITER (gs_iter, bm_unique_verts) { - gpu_bmesh_vert_to_buffer_copy(BLI_gsetIterator_getKey(&gs_iter), - vert_data, &v_index, NULL, NULL, - cd_vert_mask_offset, diffuse_color, - show_mask); + gpu_bmesh_vert_to_buffer_copy__gwn( + BLI_gsetIterator_getKey(&gs_iter), + buffers->vert_buf, &v_index, NULL, NULL, + cd_vert_mask_offset, diffuse_color, + show_mask); } GSET_ITER (gs_iter, bm_other_verts) { - gpu_bmesh_vert_to_buffer_copy(BLI_gsetIterator_getKey(&gs_iter), - vert_data, &v_index, NULL, NULL, - cd_vert_mask_offset, diffuse_color, - show_mask); + gpu_bmesh_vert_to_buffer_copy__gwn( + BLI_gsetIterator_getKey(&gs_iter), + buffers->vert_buf, &v_index, NULL, NULL, + cd_vert_mask_offset, diffuse_color, + show_mask); } maxvert = v_index; @@ -1741,10 +867,11 @@ void GPU_pbvh_bmesh_buffers_update( fmask /= 3.0f; for (i = 0; i < 3; i++) { - gpu_bmesh_vert_to_buffer_copy(v[i], vert_data, - &v_index, f->no, &fmask, - cd_vert_mask_offset, diffuse_color, - show_mask); + gpu_bmesh_vert_to_buffer_copy__gwn( + v[i], buffers->vert_buf, + &v_index, f->no, &fmask, + cd_vert_mask_offset, diffuse_color, + show_mask); } } } @@ -1752,32 +879,26 @@ void GPU_pbvh_bmesh_buffers_update( buffers->tot_tri = tottri; } - GPU_buffer_unlock(buffers->vert_buf, GPU_BINDING_ARRAY); - /* gpu_bmesh_vert_to_buffer_copy sets dirty index values */ bm->elem_index_dirty |= BM_VERT; } else { /* Memory map failed */ - GPU_buffer_free(buffers->vert_buf); - buffers->vert_buf = NULL; return; } if (buffers->smooth) { - const int use_short = (maxvert < USHRT_MAX); + /* Fill the triangle buffer */ + buffers->index_buf = NULL; + Gwn_IndexBufBuilder elb; + GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, tottri, maxvert); /* Initialize triangle index buffer */ - if (buffers->index_buf && !buffers->is_index_buf_global) - GPU_buffer_free(buffers->index_buf); buffers->is_index_buf_global = false; - buffers->index_buf = GPU_buffer_alloc((use_short ? - sizeof(unsigned short) : - sizeof(unsigned int)) * 3 * tottri); /* Fill triangle index buffer */ - tri_data = GPU_buffer_lock(buffers->index_buf, GPU_BINDING_INDEX); - if (tri_data) { + + { GSetIterator gs_iter; GSET_ITER (gs_iter, bm_faces) { @@ -1789,46 +910,30 @@ void GPU_pbvh_bmesh_buffers_update( l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { - BMVert *v = l_iter->v; - if (use_short) { - unsigned short *elem = tri_data; - (*elem) = BM_elem_index_get(v); - elem++; - tri_data = elem; - } - else { - unsigned int *elem = tri_data; - (*elem) = BM_elem_index_get(v); - elem++; - tri_data = elem; - } + GWN_indexbuf_add_generic_vert(&elb, BM_elem_index_get(l_iter->v)); } while ((l_iter = l_iter->next) != l_first); } } - GPU_buffer_unlock(buffers->index_buf, GPU_BINDING_INDEX); - buffers->tot_tri = tottri; - buffers->index_type = (use_short ? - GL_UNSIGNED_SHORT : - GL_UNSIGNED_INT); - } - else { - /* Memory map failed */ - if (!buffers->is_index_buf_global) { - GPU_buffer_free(buffers->index_buf); + + if (buffers->index_buf == NULL) { + buffers->index_buf = GWN_indexbuf_build(&elb); + } + else { + GWN_indexbuf_build_in_place(&elb, buffers->index_buf); } - buffers->index_buf = NULL; - buffers->is_index_buf_global = false; } } else if (buffers->index_buf) { if (!buffers->is_index_buf_global) { - GPU_buffer_free(buffers->index_buf); + GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf); } buffers->index_buf = NULL; buffers->is_index_buf_global = false; } + + gpu_pbvh_batch_init(buffers); } GPU_PBVH_Buffers *GPU_pbvh_bmesh_buffers_build(bool smooth_shading) @@ -1840,172 +945,31 @@ GPU_PBVH_Buffers *GPU_pbvh_bmesh_buffers_build(bool smooth_shading) buffers->smooth = smooth_shading; buffers->show_diffuse_color = false; buffers->show_mask = true; - buffers->use_matcaps = false; return buffers; } -void GPU_pbvh_buffers_draw( - GPU_PBVH_Buffers *buffers, DMSetMaterial setMaterial, - bool wireframe, bool fast) +Gwn_Batch *GPU_pbvh_buffers_batch_get(GPU_PBVH_Buffers *buffers, bool fast) { - bool do_fast = fast && buffers->index_buf_fast; - /* sets material from the first face, to solve properly face would need to - * be sorted in buckets by materials */ - if (setMaterial) { - if (buffers->face_indices_len) { - const MLoopTri *lt = &buffers->looptri[buffers->face_indices[0]]; - const MPoly *mp = &buffers->mpoly[lt->poly]; - if (!setMaterial(mp->mat_nr + 1, NULL)) - return; - } - else if (buffers->totgrid) { - const DMFlagMat *f = &buffers->grid_flag_mats[buffers->grid_indices[0]]; - if (!setMaterial(f->mat_nr + 1, NULL)) - return; - } - else { - if (!setMaterial(1, NULL)) - return; - } - } - - if (buffers->vert_buf) { - char *base = NULL; - char *index_base = NULL; - /* weak inspection of bound options, should not be necessary ideally */ - const int bound_options_old = GPU_basic_shader_bound_options(); - int bound_options_new = 0; - glEnableClientState(GL_VERTEX_ARRAY); - if (!wireframe) { - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - bound_options_new |= GPU_SHADER_USE_COLOR; - } - - GPU_buffer_bind(buffers->vert_buf, GPU_BINDING_ARRAY); - - if (do_fast) { - GPU_buffer_bind(buffers->index_buf_fast, GPU_BINDING_INDEX); - } - else if (buffers->index_buf) { - GPU_buffer_bind(buffers->index_buf, GPU_BINDING_INDEX); - } - - if (wireframe) { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - } - else { - if ((buffers->smooth == false) && (buffers->face_indices_len == 0)) { - bound_options_new |= GPU_SHADER_FLAT_NORMAL; - } - } - - if (bound_options_new & ~bound_options_old) { - GPU_basic_shader_bind(bound_options_old | bound_options_new); - } - - if (buffers->tot_quad) { - const char *offset = base; - const bool drawall = !(buffers->has_hidden || do_fast); - - if (GLEW_ARB_draw_elements_base_vertex && drawall) { - - glVertexPointer(3, GL_FLOAT, sizeof(VertexBufferFormat), - offset + offsetof(VertexBufferFormat, co)); - if (!wireframe) { - glNormalPointer(GL_SHORT, sizeof(VertexBufferFormat), - offset + offsetof(VertexBufferFormat, no)); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VertexBufferFormat), - offset + offsetof(VertexBufferFormat, color)); - } - - glMultiDrawElementsBaseVertex(GL_TRIANGLES, buffers->baseelemarray, buffers->index_type, - (const void * const *)buffers->baseindex, - buffers->totgrid, &buffers->baseelemarray[buffers->totgrid]); - } - else { - int i, last = drawall ? buffers->totgrid : 1; - - /* we could optimize this to one draw call, but it would need more memory */ - for (i = 0; i < last; i++) { - glVertexPointer(3, GL_FLOAT, sizeof(VertexBufferFormat), - offset + offsetof(VertexBufferFormat, co)); - if (!wireframe) { - glNormalPointer(GL_SHORT, sizeof(VertexBufferFormat), - offset + offsetof(VertexBufferFormat, no)); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VertexBufferFormat), - offset + offsetof(VertexBufferFormat, color)); - } - - if (do_fast) - glDrawElements(GL_TRIANGLES, buffers->totgrid * 6, buffers->index_type, index_base); - else - glDrawElements(GL_TRIANGLES, buffers->tot_quad * 6, buffers->index_type, index_base); - - offset += buffers->gridkey.grid_area * sizeof(VertexBufferFormat); - } - } - } - else if (buffers->tot_tri) { - int totelem = buffers->tot_tri * 3; - - glVertexPointer(3, GL_FLOAT, sizeof(VertexBufferFormat), - (void *)(base + offsetof(VertexBufferFormat, co))); - - if (!wireframe) { - glNormalPointer(GL_SHORT, sizeof(VertexBufferFormat), - (void *)(base + offsetof(VertexBufferFormat, no))); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VertexBufferFormat), - (void *)(base + offsetof(VertexBufferFormat, color))); - } - - if (buffers->index_buf) - glDrawElements(GL_TRIANGLES, totelem, buffers->index_type, index_base); - else - glDrawArrays(GL_TRIANGLES, 0, totelem); - } - - if (wireframe) - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - GPU_buffer_unbind(buffers->vert_buf, GPU_BINDING_ARRAY); - if (buffers->index_buf || do_fast) - GPU_buffer_unbind(do_fast ? buffers->index_buf_fast : buffers->index_buf, GPU_BINDING_INDEX); - - glDisableClientState(GL_VERTEX_ARRAY); - if (!wireframe) { - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - } - - if (bound_options_new & ~bound_options_old) { - GPU_basic_shader_bind(bound_options_old); - } - } + return (fast && buffers->triangles_fast) ? + buffers->triangles_fast : buffers->triangles; } -bool GPU_pbvh_buffers_diffuse_changed( - GPU_PBVH_Buffers *buffers, GSet *bm_faces, bool show_diffuse_color) +bool GPU_pbvh_buffers_diffuse_changed(GPU_PBVH_Buffers *buffers, GSet *bm_faces, bool show_diffuse_color) { float diffuse_color[4]; - bool use_matcaps = GPU_material_use_matcaps_get(); if (buffers->show_diffuse_color != show_diffuse_color) return true; - if (buffers->use_matcaps != use_matcaps) - return true; - - if ((buffers->show_diffuse_color == false) || use_matcaps) + if (buffers->show_diffuse_color == false) return false; if (buffers->looptri) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[0]]; const MPoly *mp = &buffers->mpoly[lt->poly]; - GPU_material_diffuse_get(mp->mat_nr + 1, diffuse_color); + gpu_material_diffuse_get(mp->mat_nr + 1, diffuse_color); } else if (buffers->use_bmesh) { /* due to dynamic nature of dyntopo, only get first material */ @@ -2015,7 +979,7 @@ bool GPU_pbvh_buffers_diffuse_changed( BLI_gsetIterator_init(&gs_iter, bm_faces); f = BLI_gsetIterator_getKey(&gs_iter); - GPU_material_diffuse_get(f->mat_nr + 1, diffuse_color); + gpu_material_diffuse_get(f->mat_nr + 1, diffuse_color); } else { return false; @@ -2024,7 +988,7 @@ bool GPU_pbvh_buffers_diffuse_changed( else { const DMFlagMat *flags = &buffers->grid_flag_mats[buffers->grid_indices[0]]; - GPU_material_diffuse_get(flags->mat_nr + 1, diffuse_color); + gpu_material_diffuse_get(flags->mat_nr + 1, diffuse_color); } return !equals_v3v3(diffuse_color, buffers->diffuse_color); @@ -2038,16 +1002,20 @@ bool GPU_pbvh_buffers_mask_changed(GPU_PBVH_Buffers *buffers, bool show_mask) void GPU_pbvh_buffers_free(GPU_PBVH_Buffers *buffers) { if (buffers) { - if (buffers->vert_buf) - GPU_buffer_free(buffers->vert_buf); - if (buffers->index_buf && !buffers->is_index_buf_global) - GPU_buffer_free(buffers->index_buf); - if (buffers->index_buf_fast) - GPU_buffer_free(buffers->index_buf_fast); + GWN_BATCH_DISCARD_SAFE(buffers->triangles); + GWN_BATCH_DISCARD_SAFE(buffers->triangles_fast); + if (!buffers->is_index_buf_global) { + GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf); + } + GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf_fast); + GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf); + +#ifdef USE_BASE_ELEM if (buffers->baseelemarray) MEM_freeN(buffers->baseelemarray); if (buffers->baseindex) MEM_freeN(buffers->baseindex); +#endif MEM_freeN(buffers); } @@ -2060,7 +1028,7 @@ void GPU_pbvh_multires_buffers_free(GridCommonGPUBuffer **grid_common_gpu_buffer if (gridbuff) { if (gridbuff->mres_buffer) { BLI_mutex_lock(&buffer_mutex); - gpu_buffer_free_intern(gridbuff->mres_buffer); + GWN_INDEXBUF_DISCARD_SAFE(gridbuff->mres_buffer); BLI_mutex_unlock(&buffer_mutex); } MEM_freeN(gridbuff); @@ -2069,59 +1037,61 @@ void GPU_pbvh_multires_buffers_free(GridCommonGPUBuffer **grid_common_gpu_buffer } /* debug function, draws the pbvh BB */ -void GPU_pbvh_BB_draw(float min[3], float max[3], bool leaf) +void GPU_pbvh_BB_draw(float min[3], float max[3], bool leaf, unsigned int pos) { - const float quads[4][4][3] = { - { - {min[0], min[1], min[2]}, - {max[0], min[1], min[2]}, - {max[0], min[1], max[2]}, - {min[0], min[1], max[2]} - }, + if (leaf) + immUniformColor4f(0.0, 1.0, 0.0, 0.5); + else + immUniformColor4f(1.0, 0.0, 0.0, 0.5); - { - {min[0], min[1], min[2]}, - {min[0], max[1], min[2]}, - {min[0], max[1], max[2]}, - {min[0], min[1], max[2]} - }, + /* TODO(merwin): revisit this after we have mutable VertexBuffers + * could keep a static batch & index buffer, change the VBO contents per draw + */ - { - {max[0], max[1], min[2]}, - {max[0], min[1], min[2]}, - {max[0], min[1], max[2]}, - {max[0], max[1], max[2]} - }, + immBegin(GWN_PRIM_LINES, 24); - { - {max[0], max[1], min[2]}, - {min[0], max[1], min[2]}, - {min[0], max[1], max[2]}, - {max[0], max[1], max[2]} - }, - }; + /* top */ + immVertex3f(pos, min[0], min[1], max[2]); + immVertex3f(pos, min[0], max[1], max[2]); - if (leaf) - glColor4f(0.0, 1.0, 0.0, 0.5); - else - glColor4f(1.0, 0.0, 0.0, 0.5); + immVertex3f(pos, min[0], max[1], max[2]); + immVertex3f(pos, max[0], max[1], max[2]); - glVertexPointer(3, GL_FLOAT, 0, &quads[0][0][0]); - glDrawArrays(GL_QUADS, 0, 16); -} + immVertex3f(pos, max[0], max[1], max[2]); + immVertex3f(pos, max[0], min[1], max[2]); -void GPU_pbvh_BB_draw_init(void) -{ - glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_CULL_FACE); - glEnableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glEnable(GL_BLEND); + immVertex3f(pos, max[0], min[1], max[2]); + immVertex3f(pos, min[0], min[1], max[2]); + + /* bottom */ + immVertex3f(pos, min[0], min[1], min[2]); + immVertex3f(pos, min[0], max[1], min[2]); + + immVertex3f(pos, min[0], max[1], min[2]); + immVertex3f(pos, max[0], max[1], min[2]); + + immVertex3f(pos, max[0], max[1], min[2]); + immVertex3f(pos, max[0], min[1], min[2]); + + immVertex3f(pos, max[0], min[1], min[2]); + immVertex3f(pos, min[0], min[1], min[2]); + + /* sides */ + immVertex3f(pos, min[0], min[1], min[2]); + immVertex3f(pos, min[0], min[1], max[2]); + + immVertex3f(pos, min[0], max[1], min[2]); + immVertex3f(pos, min[0], max[1], max[2]); + + immVertex3f(pos, max[0], max[1], min[2]); + immVertex3f(pos, max[0], max[1], max[2]); + + immVertex3f(pos, max[0], min[1], min[2]); + immVertex3f(pos, max[0], min[1], max[2]); + + immEnd(); } -void GPU_pbvh_BB_draw_end(void) +void GPU_pbvh_fix_linking() { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glPopAttrib(); } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index a9cef0b5ad3..22665e2c0bf 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -36,17 +36,23 @@ #include "DNA_customdata_types.h" #include "DNA_image_types.h" #include "DNA_material_types.h" +#include "DNA_node_types.h" #include "BLI_blenlib.h" +#include "BLI_hash_mm2a.h" +#include "BLI_linklist.h" #include "BLI_utildefines.h" #include "BLI_dynstr.h" #include "BLI_ghash.h" +#include "PIL_time.h" + #include "GPU_extensions.h" #include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_uniformbuffer.h" #include "BLI_sys_types.h" /* for intptr_t support */ @@ -62,6 +68,50 @@ extern char datatoc_gpu_shader_geometry_glsl[]; static char *glsl_material_library = NULL; +/* -------------------- GPUPass Cache ------------------ */ +/** + * Internal shader cache: This prevent the shader recompilation / stall when + * using undo/redo AND also allows for GPUPass reuse if the Shader code is the + * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. + **/ + +static LinkNode *pass_cache = NULL; /* GPUPass */ + +static uint32_t gpu_pass_hash(const char *vert, const char *geom, const char *frag, const char *defs) +{ + BLI_HashMurmur2A hm2a; + BLI_hash_mm2a_init(&hm2a, 0); + BLI_hash_mm2a_add(&hm2a, (unsigned char *)frag, strlen(frag)); + BLI_hash_mm2a_add(&hm2a, (unsigned char *)vert, strlen(vert)); + if (defs) + BLI_hash_mm2a_add(&hm2a, (unsigned char *)defs, strlen(defs)); + if (geom) + BLI_hash_mm2a_add(&hm2a, (unsigned char *)geom, strlen(geom)); + + return BLI_hash_mm2a_end(&hm2a); +} + +/* Search by hash then by exact string match. */ +static GPUPass *gpu_pass_cache_lookup( + const char *vert, const char *geom, const char *frag, const char *defs, uint32_t hash) +{ + for (LinkNode *ln = pass_cache; ln; ln = ln->next) { + GPUPass *pass = (GPUPass *)ln->link; + if (pass->hash == hash) { + /* Note: Could be made faster if that becomes a real bottleneck. */ + if ((defs != NULL) && (strcmp(pass->defines, defs) != 0)) { /* Pass */ } + else if ((geom != NULL) && (strcmp(pass->geometrycode, geom) != 0)) { /* Pass */ } + else if ((strcmp(pass->fragmentcode, frag) == 0) && + (strcmp(pass->vertexcode, vert) == 0)) + { + return pass; + } + } + } + return NULL; +} + +/* -------------------- GPU Codegen ------------------ */ /* type definitions and constants */ @@ -88,7 +138,7 @@ typedef struct GPUFunction { /* Indices match the GPUType enum */ static const char *GPU_DATATYPE_STR[17] = { "", "float", "vec2", "vec3", "vec4", - NULL, NULL, NULL, NULL, "mat3", NULL, NULL, NULL, NULL, NULL, NULL, "mat4", + NULL, NULL, NULL, NULL, "mat3", NULL, NULL, NULL, NULL, NULL, NULL, "mat4" }; /* GLSL code parsing for finding function definitions. @@ -172,7 +222,7 @@ static void gpu_parse_functions_string(GHash *hash, char *code) /* test for type */ type = GPU_NONE; - for (i = 1; i <= 16; i++) { + for (i = 1; i < ARRAY_SIZE(GPU_DATATYPE_STR); i++) { if (GPU_DATATYPE_STR[i] && gpu_str_prefix(code, GPU_DATATYPE_STR[i])) { type = i; break; @@ -188,6 +238,13 @@ static void gpu_parse_functions_string(GHash *hash, char *code) if (!type && gpu_str_prefix(code, "sampler2D")) { type = GPU_TEX2D; } + if (!type && gpu_str_prefix(code, "sampler3D")) { + type = GPU_TEX3D; + } + + if (!type && gpu_str_prefix(code, "Closure")) { + type = GPU_CLOSURE; + } if (type) { /* add parameter */ @@ -350,6 +407,8 @@ static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *t BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name); else if (from == GPU_FLOAT) BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name); + else /* can happen with closure */ + BLI_dynstr_append(ds, name); } } @@ -412,6 +471,12 @@ const char *GPU_builtin_name(GPUBuiltin builtin) return "unfparticleangvel"; else if (builtin == GPU_OBJECT_INFO) return "unfobjectinfo"; + else if (builtin == GPU_VOLUME_DENSITY) + return "sampdensity"; + else if (builtin == GPU_VOLUME_FLAME) + return "sampflame"; + else if (builtin == GPU_VOLUME_TEMPERATURE) + return "unftemperature"; else return ""; } @@ -500,12 +565,16 @@ static void codegen_set_unique_ids(ListBase *nodes) BLI_ghash_free(definehash, NULL, NULL); } -static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes) +/** + * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO. + */ +static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, ListBase *nodes) { GPUNode *node; GPUInput *input; const char *name; int builtins = 0; + ListBase ubo_inputs = {NULL, NULL}; /* print uniforms */ for (node = nodes->first; node; node = node->next) { @@ -525,7 +594,14 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes) builtins |= input->builtin; name = GPU_builtin_name(input->builtin); - if (gpu_str_prefix(name, "unf")) { + if (gpu_str_prefix(name, "samp")) { + if ((input->builtin == GPU_VOLUME_DENSITY) || + (input->builtin == GPU_VOLUME_FLAME)) + { + BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", name); + } + } + else if (gpu_str_prefix(name, "unf")) { BLI_dynstr_appendf(ds, "uniform %s %s;\n", GPU_DATATYPE_STR[input->type], name); } @@ -536,14 +612,23 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes) } } } + else if (input->source == GPU_SOURCE_STRUCT) { + /* Add other struct here if needed. */ + BLI_dynstr_appendf(ds, "Closure strct%d = CLOSURE_DEFAULT;\n", input->id); + } else if (input->source == GPU_SOURCE_VEC_UNIFORM) { - if (input->dynamicvec) { + if (input->dynamictype == GPU_DYNAMIC_UBO) { + if (!input->link) { + /* We handle the UBOuniforms separately. */ + BLI_addtail(&ubo_inputs, BLI_genericNodeN(input)); + } + } + else if (input->dynamicvec) { /* only create uniforms for dynamic vectors */ BLI_dynstr_appendf(ds, "uniform %s unf%d;\n", GPU_DATATYPE_STR[input->type], input->id); } else { - /* for others use const so the compiler can do folding */ BLI_dynstr_appendf(ds, "const %s cons%d = ", GPU_DATATYPE_STR[input->type], input->id); codegen_print_datatype(ds, input->type, input->vec); @@ -569,6 +654,22 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes) } } + /* Handle the UBO block separately. */ + if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) { + GPU_material_create_uniform_buffer(material, &ubo_inputs); + + /* Inputs are sorted */ + BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); + + for (LinkData *link = ubo_inputs.first; link; link = link->next) { + input = link->data; + BLI_dynstr_appendf(ds, "\t%s unf%d;\n", + GPU_DATATYPE_STR[input->type], input->id); + } + BLI_dynstr_append(ds, "};\n"); + BLI_freelistN(&ubo_inputs); + } + BLI_dynstr_append(ds, "\n"); return builtins; @@ -594,8 +695,13 @@ static void codegen_declare_tmps(DynStr *ds, ListBase *nodes) /* declare temporary variables for node output storage */ for (output = node->outputs.first; output; output = output->next) { - BLI_dynstr_appendf(ds, "\t%s tmp%d;\n", - GPU_DATATYPE_STR[output->type], output->id); + if (output->type == GPU_CLOSURE) { + BLI_dynstr_appendf(ds, "\tClosure tmp%d;\n", output->id); + } + else { + BLI_dynstr_appendf(ds, "\t%s tmp%d;\n", + GPU_DATATYPE_STR[output->type], output->id); + } } } @@ -622,11 +728,26 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final "tmp", input->link->output->id); } else if (input->source == GPU_SOURCE_BUILTIN) { - if (input->builtin == GPU_VIEW_NORMAL) + if (input->builtin == GPU_INVERSE_VIEW_MATRIX) + BLI_dynstr_append(ds, "viewinv"); + else if (input->builtin == GPU_VIEW_MATRIX) + BLI_dynstr_append(ds, "viewmat"); + else if (input->builtin == GPU_CAMERA_TEXCO_FACTORS) + BLI_dynstr_append(ds, "camtexfac"); + else if (input->builtin == GPU_OBJECT_MATRIX) + BLI_dynstr_append(ds, "objmat"); + else if (input->builtin == GPU_INVERSE_OBJECT_MATRIX) + BLI_dynstr_append(ds, "objinv"); + else if (input->builtin == GPU_VIEW_POSITION) + BLI_dynstr_append(ds, "viewposition"); + else if (input->builtin == GPU_VIEW_NORMAL) BLI_dynstr_append(ds, "facingnormal"); else BLI_dynstr_append(ds, GPU_builtin_name(input->builtin)); } + else if (input->source == GPU_SOURCE_STRUCT) { + BLI_dynstr_appendf(ds, "strct%d", input->id); + } else if (input->source == GPU_SOURCE_VEC_UNIFORM) { if (input->dynamicvec) BLI_dynstr_appendf(ds, "unf%d", input->id); @@ -655,12 +776,11 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final BLI_dynstr_append(ds, ");\n"); } - BLI_dynstr_append(ds, "\n\tgl_FragColor = "); - codegen_convert_datatype(ds, finaloutput->type, GPU_VEC4, "tmp", finaloutput->id); + BLI_dynstr_appendf(ds, "\n\treturn tmp%d", finaloutput->id); BLI_dynstr_append(ds, ";\n"); } -static char *code_generate_fragment(ListBase *nodes, GPUOutput *output) +static char *code_generate_fragment(GPUMaterial *material, ListBase *nodes, GPUOutput *output) { DynStr *ds = BLI_dynstr_new(); char *code; @@ -677,17 +797,29 @@ static char *code_generate_fragment(ListBase *nodes, GPUOutput *output) #endif codegen_set_unique_ids(nodes); - builtins = codegen_print_uniforms_functions(ds, nodes); + builtins = codegen_process_uniforms_functions(material, ds, nodes); #if 0 if (G.debug & G_DEBUG) BLI_dynstr_appendf(ds, "/* %s */\n", name); #endif - BLI_dynstr_append(ds, "void main()\n{\n"); - + BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); + + if (builtins & GPU_VIEW_MATRIX) + BLI_dynstr_append(ds, "\tmat4 viewmat = ViewMatrix;\n"); + if (builtins & GPU_CAMERA_TEXCO_FACTORS) + BLI_dynstr_append(ds, "\tvec4 camtexfac = CameraTexCoFactors;\n"); + if (builtins & GPU_OBJECT_MATRIX) + BLI_dynstr_append(ds, "\tmat4 objmat = ModelMatrix;\n"); + if (builtins & GPU_INVERSE_OBJECT_MATRIX) + BLI_dynstr_append(ds, "\tmat4 objinv = ModelMatrixInverse;\n"); + if (builtins & GPU_INVERSE_VIEW_MATRIX) + BLI_dynstr_append(ds, "\tmat4 viewinv = ViewMatrixInverse;\n"); if (builtins & GPU_VIEW_NORMAL) - BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing? varnormal: -varnormal;\n"); + BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing? viewNormal: -viewNormal;\n"); + if (builtins & GPU_VIEW_POSITION) + BLI_dynstr_append(ds, "\tvec3 viewposition = viewPosition;\n"); /* Calculate tangent space. */ #ifdef WITH_OPENSUBDIV @@ -722,6 +854,17 @@ static char *code_generate_fragment(ListBase *nodes, GPUOutput *output) BLI_dynstr_append(ds, "}\n"); + /* XXX This cannot go into gpu_shader_material.glsl because main() would be parsed and generate error */ + /* Old glsl mode compat. */ + BLI_dynstr_append(ds, "#ifndef NODETREE_EXEC\n"); + BLI_dynstr_append(ds, "out vec4 fragColor;\n"); + BLI_dynstr_append(ds, "void main()\n"); + BLI_dynstr_append(ds, "{\n"); + BLI_dynstr_append(ds, "\tClosure cl = nodetree_exec();\n"); + BLI_dynstr_append(ds, "\tfragColor = vec4(cl.radiance, cl.opacity);\n"); + BLI_dynstr_append(ds, "}\n"); + BLI_dynstr_append(ds, "#endif\n\n"); + /* create shader */ code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -733,103 +876,152 @@ static char *code_generate_fragment(ListBase *nodes, GPUOutput *output) return code; } -static char *code_generate_vertex(ListBase *nodes, const GPUMatType type) +static const char *attrib_prefix_get(CustomDataType type) +{ + switch (type) { + case CD_ORCO: return "orco"; + case CD_MTFACE: return "u"; + case CD_TANGENT: return "t"; + case CD_MCOL: return "c"; + case CD_AUTO_FROM_NAME: return "a"; + default: BLI_assert(false && "Gwn_VertAttr Prefix type not found : This should not happen!"); return ""; + } +} + +static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool use_geom) { DynStr *ds = BLI_dynstr_new(); GPUNode *node; GPUInput *input; char *code; - char *vertcode = NULL; + + /* Hairs uv and col attribs are passed by bufferTextures. */ + BLI_dynstr_append(ds, + "#ifdef HAIR_SHADER\n" + "#define DEFINE_ATTRIB(type, attr) uniform samplerBuffer attr\n" + "#else\n" + "#define DEFINE_ATTRIB(type, attr) in type attr\n" + "#endif\n" + ); for (node = nodes->first; node; node = node->next) { for (input = node->inputs.first; input; input = input->next) { if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { -#ifdef WITH_OPENSUBDIV - bool skip_opensubdiv = ELEM(input->attribtype, CD_MTFACE, CD_TANGENT); - if (skip_opensubdiv) { - BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n"); + /* XXX FIXME : see notes in mesh_render_data_create() */ + /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + if (input->attribtype == CD_ORCO) { + /* orco is computed from local positions, see bellow */ + BLI_dynstr_appendf(ds, "uniform vec3 OrcoTexCoFactors[2];\n"); } -#endif - BLI_dynstr_appendf(ds, "%s %s att%d;\n", - GLEW_VERSION_3_0 ? "in" : "attribute", - GPU_DATATYPE_STR[input->type], input->attribid); - BLI_dynstr_appendf(ds, "uniform int att%d_info;\n", input->attribid); - BLI_dynstr_appendf(ds, "%s %s var%d;\n", - GLEW_VERSION_3_0 ? "out" : "varying", - GPU_DATATYPE_STR[input->type], input->attribid); -#ifdef WITH_OPENSUBDIV - if (skip_opensubdiv) { - BLI_dynstr_appendf(ds, "#endif\n"); + else if (input->attribname[0] == '\0') { + BLI_dynstr_appendf(ds, "DEFINE_ATTRIB(%s, %s);\n", GPU_DATATYPE_STR[input->type], attrib_prefix_get(input->attribtype)); + BLI_dynstr_appendf(ds, "#define att%d %s\n", input->attribid, attrib_prefix_get(input->attribtype)); } -#endif + else { + unsigned int hash = BLI_ghashutil_strhash_p(input->attribname); + BLI_dynstr_appendf(ds, "DEFINE_ATTRIB(%s, %s%u);\n", + GPU_DATATYPE_STR[input->type], attrib_prefix_get(input->attribtype), hash); + BLI_dynstr_appendf(ds, "#define att%d %s%u\n", + input->attribid, attrib_prefix_get(input->attribtype), hash); + /* Auto attrib can be vertex color byte buffer. + * We need to know and convert them to linear space in VS. */ + if (!use_geom && input->attribtype == CD_AUTO_FROM_NAME) { + BLI_dynstr_appendf(ds, "uniform bool ba%u;\n", hash); + BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%u\n", input->attribid, hash); + } + } + BLI_dynstr_appendf(ds, "out %s var%d%s;\n", + GPU_DATATYPE_STR[input->type], input->attribid, use_geom ? "g" : ""); } } } BLI_dynstr_append(ds, "\n"); - switch (type) { - case GPU_MATERIAL_TYPE_MESH: - vertcode = datatoc_gpu_shader_vertex_glsl; - break; - case GPU_MATERIAL_TYPE_WORLD: - vertcode = datatoc_gpu_shader_vertex_world_glsl; - break; - default: - fprintf(stderr, "invalid material type, set one after GPU_material_construct_begin\n"); - break; + BLI_dynstr_append(ds, + "#define ATTRIB\n" + "uniform mat3 NormalMatrix;\n" + "uniform mat4 ModelMatrixInverse;\n" + "vec3 srgb_to_linear_attrib(vec3 c) {\n" + "\tc = max(c, vec3(0.0));\n" + "\tvec3 c1 = c * (1.0 / 12.92);\n" + "\tvec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));\n" + "\treturn mix(c1, c2, step(vec3(0.04045), c));\n" + "}\n\n" + ); + + /* Prototype because defined later. */ + BLI_dynstr_append(ds, + "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" + "vec3 hair_get_customdata_vec3(const samplerBuffer);\n" + "vec4 hair_get_customdata_vec4(const samplerBuffer);\n" + "vec3 hair_get_strand_pos(void);\n" + "\n" + ); + + BLI_dynstr_append(ds, "void pass_attrib(in vec3 position) {\n"); + + BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); + + for (node = nodes->first; node; node = node->next) { + for (input = node->inputs.first; input; input = input->next) { + if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { + if (input->attribtype == CD_TANGENT) { + /* Not supported by hairs */ + BLI_dynstr_appendf(ds, "\tvar%d%s = vec4(0.0);\n", + input->attribid, use_geom ? "g" : ""); + } + else if (input->attribtype == CD_ORCO) { + BLI_dynstr_appendf(ds, "\tvar%d%s = OrcoTexCoFactors[0] + (ModelMatrixInverse * vec4(hair_get_strand_pos(), 1.0)).xyz * OrcoTexCoFactors[1];\n", + input->attribid, use_geom ? "g" : ""); + } + else { + BLI_dynstr_appendf(ds, "\tvar%d%s = hair_get_customdata_%s(att%d);\n", + input->attribid, use_geom ? "g" : "", GPU_DATATYPE_STR[input->type], input->attribid); + } + } + } } - BLI_dynstr_append(ds, vertcode); + BLI_dynstr_append(ds, "#else /* MESH_SHADER */\n"); - for (node = nodes->first; node; node = node->next) - for (input = node->inputs.first; input; input = input->next) + for (node = nodes->first; node; node = node->next) { + for (input = node->inputs.first; input; input = input->next) { if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { if (input->attribtype == CD_TANGENT) { /* silly exception */ -#ifdef WITH_OPENSUBDIV - BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n"); -#endif BLI_dynstr_appendf( - ds, "\tvar%d.xyz = normalize(gl_NormalMatrix * att%d.xyz);\n", - input->attribid, input->attribid); + ds, "\tvar%d%s.xyz = normalize(NormalMatrix * att%d.xyz);\n", + input->attribid, use_geom ? "g" : "", input->attribid); BLI_dynstr_appendf( - ds, "\tvar%d.w = att%d.w;\n", - input->attribid, input->attribid); -#ifdef WITH_OPENSUBDIV - BLI_dynstr_appendf(ds, "#endif\n"); -#endif + ds, "\tvar%d%s.w = att%d.w;\n", + input->attribid, use_geom ? "g" : "", input->attribid); } - else { -#ifdef WITH_OPENSUBDIV - bool is_mtface = input->attribtype == CD_MTFACE; - if (is_mtface) { - BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n"); - } -#endif - BLI_dynstr_appendf(ds, "\tset_var_from_attr(att%d, att%d_info, var%d);\n", - input->attribid, input->attribid, input->attribid); -#ifdef WITH_OPENSUBDIV - if (is_mtface) { - BLI_dynstr_appendf(ds, "#endif\n"); - } -#endif + else if (input->attribtype == CD_ORCO) { + BLI_dynstr_appendf(ds, "\tvar%d%s = OrcoTexCoFactors[0] + position * OrcoTexCoFactors[1];\n", + input->attribid, use_geom ? "g" : ""); } - } - /* unfortunately special handling is needed here because we abuse gl_Color/gl_SecondaryColor flat shading */ - else if (input->source == GPU_SOURCE_OPENGL_BUILTIN) { - if (input->oglbuiltin == GPU_MATCAP_NORMAL) { - /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors - * between shader stages and we want the full range of the normal */ - BLI_dynstr_appendf(ds, "\tvec3 matcapcol = vec3(0.5) * varnormal + vec3(0.5);\n"); - BLI_dynstr_appendf(ds, "\tgl_FrontSecondaryColor = vec4(matcapcol, 1.0);\n"); + else if (input->attribtype == CD_MCOL) { + BLI_dynstr_appendf(ds, "\tvar%d%s = srgb_to_linear_attrib(att%d);\n", + input->attribid, use_geom ? "g" : "", input->attribid); } - else if (input->oglbuiltin == GPU_COLOR) { - BLI_dynstr_appendf(ds, "\tgl_FrontColor = gl_Color;\n"); + else if (input->attribtype == CD_AUTO_FROM_NAME) { + BLI_dynstr_appendf(ds, "\tvar%d%s = (att%d_is_srgb) ? srgb_to_linear_attrib(att%d) : att%d;\n", + input->attribid, use_geom ? "g" : "", + input->attribid, input->attribid, input->attribid); + } + else { + BLI_dynstr_appendf(ds, "\tvar%d%s = att%d;\n", + input->attribid, use_geom ? "g" : "", input->attribid); } } + } + } + BLI_dynstr_append(ds, "#endif /* HAIR_SHADER */\n"); BLI_dynstr_append(ds, "}\n"); + BLI_dynstr_append(ds, vert_code); + code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -841,65 +1033,49 @@ static char *code_generate_vertex(ListBase *nodes, const GPUMatType type) return code; } -static char *code_generate_geometry(ListBase *nodes, bool use_opensubdiv) +static char *code_generate_geometry(ListBase *nodes, const char *geom_code) { -#ifdef WITH_OPENSUBDIV - if (use_opensubdiv) { - DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; + DynStr *ds = BLI_dynstr_new(); + GPUNode *node; + GPUInput *input; + char *code; - /* Generate varying declarations. */ - for (node = nodes->first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { - if (input->attribtype == CD_MTFACE) { - /* NOTE: For now we are using varying on purpose, - * otherwise we are not able to write to the varying. - */ - BLI_dynstr_appendf(ds, "%s %s var%d%s;\n", - "varying", - GPU_DATATYPE_STR[input->type], - input->attribid, - ""); - BLI_dynstr_appendf(ds, "uniform int fvar%d_offset;\n", - input->attribid); - } - } - } - } + /* Create prototype because attributes cannot be declared before layout. */ + BLI_dynstr_appendf(ds, "void pass_attrib(in int vert);\n"); + BLI_dynstr_append(ds, "#define ATTRIB\n"); - BLI_dynstr_append(ds, datatoc_gpu_shader_geometry_glsl); + BLI_dynstr_append(ds, geom_code); - /* Generate varying assignments. */ - for (node = nodes->first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { - if (input->attribtype == CD_MTFACE) { - BLI_dynstr_appendf( - ds, - "\tINTERP_FACE_VARYING_ATT_2(var%d, " - "int(texelFetch(FVarDataOffsetBuffer, fvar%d_offset).r), st);\n", - input->attribid, - input->attribid); - } - } + /* Generate varying declarations. */ + for (node = nodes->first; node; node = node->next) { + for (input = node->inputs.first; input; input = input->next) { + if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { + BLI_dynstr_appendf(ds, "in %s var%dg[];\n", + GPU_DATATYPE_STR[input->type], + input->attribid); + BLI_dynstr_appendf(ds, "out %s var%d;\n", + GPU_DATATYPE_STR[input->type], + input->attribid); } } + } - BLI_dynstr_append(ds, "}\n"); - code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); + /* Generate varying assignments. */ + BLI_dynstr_appendf(ds, "void pass_attrib(in int vert) {\n"); + for (node = nodes->first; node; node = node->next) { + for (input = node->inputs.first; input; input = input->next) { + if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) { + /* TODO let shader choose what to do depending on what the attrib is. */ + BLI_dynstr_appendf(ds, "\tvar%d = var%dg[vert];\n", input->attribid, input->attribid); + } + } + } + BLI_dynstr_append(ds, "}\n"); - //if (G.debug & G_DEBUG) printf("%s\n", code); + code = BLI_dynstr_get_cstring(ds); + BLI_dynstr_free(ds); - return code; - } -#else - UNUSED_VARS(nodes, use_opensubdiv); -#endif - return NULL; + return code; } void GPU_code_generate_glsl_lib(void) @@ -928,15 +1104,13 @@ GPUShader *GPU_pass_shader(GPUPass *pass) return pass->shader; } -static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) +static void gpu_nodes_extract_dynamic_inputs(GPUShader *shader, ListBase *inputs, ListBase *nodes) { - GPUShader *shader = pass->shader; GPUNode *node; GPUInput *next, *input; - ListBase *inputs = &pass->inputs; int extract, z; - memset(inputs, 0, sizeof(*inputs)); + BLI_listbase_clear(inputs); if (!shader) return; @@ -951,26 +1125,9 @@ static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) /* attributes don't need to be bound, they already have * an id that the drawing functions will use */ if (input->source == GPU_SOURCE_ATTRIB) { -#ifdef WITH_OPENSUBDIV - /* We do need mtface attributes for later, so we can - * update face-varuing variables offset in the texture - * buffer for proper sampling from the shader. - * - * We don't do anything about attribute itself, we - * only use it to learn which uniform name is to be - * updated. - * - * TODO(sergey): We can add ad extra uniform input - * for the offset, which will be purely internal and - * which would avoid having such an exceptions. - */ - if (input->attribtype != CD_MTFACE) { - continue; - } -#else continue; -#endif } + if (input->source == GPU_SOURCE_BUILTIN || input->source == GPU_SOURCE_OPENGL_BUILTIN) { @@ -995,14 +1152,6 @@ static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) if (extract) input->shaderloc = GPU_shader_get_uniform(shader, input->shadername); -#ifdef WITH_OPENSUBDIV - if (input->source == GPU_SOURCE_ATTRIB && - input->attribtype == CD_MTFACE) - { - extract = 1; - } -#endif - /* extract nodes */ if (extract) { BLI_remlink(&node->inputs, input); @@ -1014,11 +1163,10 @@ static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) GPU_shader_unbind(); } -void GPU_pass_bind(GPUPass *pass, double time, int mipmap) +void GPU_pass_bind(GPUPass *pass, ListBase *inputs, double time, int mipmap) { GPUInput *input; GPUShader *shader = pass->shader; - ListBase *inputs = &pass->inputs; if (!shader) return; @@ -1043,11 +1191,10 @@ void GPU_pass_bind(GPUPass *pass, double time, int mipmap) } } -void GPU_pass_update_uniforms(GPUPass *pass) +void GPU_pass_update_uniforms(GPUPass *pass, ListBase *inputs) { GPUInput *input; GPUShader *shader = pass->shader; - ListBase *inputs = &pass->inputs; if (!shader) return; @@ -1068,11 +1215,10 @@ void GPU_pass_update_uniforms(GPUPass *pass) } } -void GPU_pass_unbind(GPUPass *pass) +void GPU_pass_unbind(GPUPass *pass, ListBase *inputs) { GPUInput *input; GPUShader *shader = pass->shader; - ListBase *inputs = &pass->inputs; if (!shader) return; @@ -1135,7 +1281,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType name = outnode->name; input = outnode->inputs.first; - if ((STREQ(name, "set_value") || STREQ(name, "set_rgb")) && + if ((STREQ(name, "set_value") || STREQ(name, "set_rgb") || STREQ(name, "set_rgba")) && (input->type == type)) { input = MEM_dupallocN(outnode->inputs.first); @@ -1194,7 +1340,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType #if 0 input->tex = GPU_texture_create_2D(link->texturesize, link->texturesize, link->ptr2, NULL); #endif - input->tex = GPU_texture_create_2D(link->texturesize, 1, link->ptr1, GPU_HDR_NONE, NULL); + input->tex = GPU_texture_create_2D(link->texturesize, 1, GPU_RGBA8, link->ptr1, NULL); input->textarget = GL_TEXTURE_2D; MEM_freeN(link->ptr1); @@ -1235,6 +1381,12 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType BLI_strncpy(input->attribname, link->attribname, sizeof(input->attribname)); MEM_freeN(link); } + else if (type == GPU_CLOSURE) { + input->type = type; + input->source = GPU_SOURCE_STRUCT; + + MEM_freeN(link); + } else { /* uniform vector */ input->type = type; @@ -1246,21 +1398,100 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType input->dynamictype = link->dynamictype; input->dynamicdata = link->ptr2; } + MEM_freeN(link); } BLI_addtail(&node->inputs, input); } -static void gpu_node_input_socket(GPUNode *node, GPUNodeStack *sock) + +static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: + return "set_value"; + case SOCK_VECTOR: + return "set_rgb"; + case SOCK_RGBA: + return "set_rgba"; + default: + BLI_assert(!"No gpu function for non-supported eNodeSocketDatatype"); + return NULL; + } +} + +/** + * Link stack uniform buffer. + * This is called for the input/output sockets that are note connected. + */ +static GPUNodeLink *gpu_uniformbuffer_link( + GPUMaterial *mat, bNode *node, GPUNodeStack *stack, const int index, const eNodeSocketInOut in_out) { - GPUNodeLink *link; + bNodeSocket *socket; + + /* Some nodes can have been create on the fly and does + * not have an original to point to. (i.e. the bump from + * ntree_shader_relink_displacement). In this case just + * revert to static constant folding. */ + if (node->original == NULL) { + return NULL; + } + + if (in_out == SOCK_IN) { + socket = BLI_findlink(&node->original->inputs, index); + } + else { + socket = BLI_findlink(&node->original->outputs, index); + } + BLI_assert(socket != NULL); + BLI_assert(socket->in_out == in_out); + + if ((socket->flag & SOCK_HIDE_VALUE) == 0) { + GPUNodeLink *link; + switch (socket->type) { + case SOCK_FLOAT: + { + bNodeSocketValueFloat *socket_data = socket->default_value; + link = GPU_uniform_buffer(&socket_data->value, GPU_FLOAT); + break; + } + case SOCK_VECTOR: + { + bNodeSocketValueRGBA *socket_data = socket->default_value; + link = GPU_uniform_buffer(socket_data->value, GPU_VEC3); + break; + } + case SOCK_RGBA: + { + bNodeSocketValueRGBA *socket_data = socket->default_value; + link = GPU_uniform_buffer(socket_data->value, GPU_VEC4); + break; + } + default: + return NULL; + break; + } + + if (in_out == SOCK_IN) { + GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link); + } + return link; + } + return NULL; +} + +static void gpu_node_input_socket(GPUMaterial *material, bNode *bnode, GPUNode *node, GPUNodeStack *sock, const int index) +{ if (sock->link) { gpu_node_input_link(node, sock->link, sock->type); } + else if ((material != NULL) && (gpu_uniformbuffer_link(material, bnode, sock, index, SOCK_IN) != NULL)) { + gpu_node_input_link(node, sock->link, sock->type); + } else { - link = GPU_node_link_create(); + GPUNodeLink *link = GPU_node_link_create(); link->ptr1 = sock->vec; gpu_node_input_link(node, link, sock->type); } @@ -1286,7 +1517,7 @@ static void gpu_node_output(GPUNode *node, const GPUType type, GPUNodeLink **lin BLI_addtail(&node->outputs, output); } -static void gpu_inputs_free(ListBase *inputs) +void GPU_inputs_free(ListBase *inputs) { GPUInput *input; @@ -1304,7 +1535,7 @@ static void gpu_node_free(GPUNode *node) { GPUOutput *output; - gpu_inputs_free(&node->inputs); + GPU_inputs_free(&node->inputs); for (output = node->outputs.first; output; output = output->next) if (output->link) { @@ -1327,7 +1558,7 @@ static void gpu_nodes_free(ListBase *nodes) /* vertex attributes */ -static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *attribs) +void GPU_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *attribs) { GPUNode *node; GPUInput *input; @@ -1368,26 +1599,20 @@ static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *a } } -static void gpu_nodes_get_builtin_flag(ListBase *nodes, int *builtin) -{ - GPUNode *node; - GPUInput *input; - - *builtin = 0; - - for (node = nodes->first; node; node = node->next) - for (input = node->inputs.first; input; input = input->next) - if (input->source == GPU_SOURCE_BUILTIN) - *builtin |= input->builtin; -} - /* varargs linking */ GPUNodeLink *GPU_attribute(const CustomDataType type, const char *name) { GPUNodeLink *link = GPU_node_link_create(); - link->attribtype = type; + /* Fall back to the UV layer, which matches old behavior. */ + if (type == CD_AUTO_FROM_NAME && name[0] == '\0') { + link->attribtype = CD_MTFACE; + } + else { + link->attribtype = type; + } + link->attribname = name; return link; @@ -1416,6 +1641,21 @@ GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *d return link; } +/** + * Add uniform to UBO struct of GPUMaterial. + */ +GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype) +{ + GPUNodeLink *link = GPU_node_link_create(); + link->ptr1 = num; + link->ptr2 = NULL; + link->dynamic = true; + link->dynamictype = GPU_DYNAMIC_UBO; + link->type = gputype; + + return link; +} + GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data) { GPUNodeLink *link = GPU_node_link_create(); @@ -1526,7 +1766,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) return true; } -bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...) +bool GPU_stack_link(GPUMaterial *material, bNode *bnode, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...) { GPUNode *node; GPUFunction *function; @@ -1545,16 +1785,20 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod totout = 0; if (in) { - for (i = 0; in[i].type != GPU_NONE; i++) { - gpu_node_input_socket(node, &in[i]); - totin++; + for (i = 0; !in[i].end; i++) { + if (in[i].type != GPU_NONE) { + gpu_node_input_socket(material, bnode, node, &in[i], i); + totin++; + } } } if (out) { - for (i = 0; out[i].type != GPU_NONE; i++) { - gpu_node_output(node, out[i].type, &out[i].link); - totout++; + for (i = 0; !out[i].end; i++) { + if (out[i].type != GPU_NONE) { + gpu_node_output(node, out[i].type, &out[i].link); + totout++; + } } } @@ -1572,7 +1816,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod if (totin == 0) { link = va_arg(params, GPUNodeLink *); if (link->socket) - gpu_node_input_socket(node, link->socket); + gpu_node_input_socket(NULL, NULL, node, link->socket, -1); else gpu_node_input_link(node, link, function->paramtype[i]); } @@ -1582,7 +1826,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod } va_end(params); - gpu_material_add_node(mat, node); + gpu_material_add_node(material, node); return true; } @@ -1608,6 +1852,11 @@ int GPU_link_changed(GPUNodeLink *link) return 0; } +GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat, bNode *node, GPUNodeStack *stack, const int index) +{ + return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT); +} + /* Pass create/free */ static void gpu_nodes_tag(GPUNodeLink *link) @@ -1628,7 +1877,7 @@ static void gpu_nodes_tag(GPUNodeLink *link) gpu_nodes_tag(input->link); } -static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink) +void GPU_nodes_prune(ListBase *nodes, GPUNodeLink *outlink) { GPUNode *node, *next; @@ -1647,91 +1896,97 @@ static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink) } } -GPUPass *GPU_generate_pass( - ListBase *nodes, GPUNodeLink *outlink, - GPUVertexAttribs *attribs, int *builtins, - const GPUMatType type, const char *UNUSED(name), - const bool use_opensubdiv, - const bool use_new_shading) +GPUPass *GPU_generate_pass_new( + GPUMaterial *material, + GPUNodeLink *frag_outlink, struct GPUVertexAttribs *attribs, + ListBase *nodes, ListBase *inputs, + const char *vert_code, const char *geom_code, + const char *frag_lib, const char *defines) { + char *vertexcode, *geometrycode, *fragmentcode; GPUShader *shader; GPUPass *pass; - char *vertexcode, *geometrycode, *fragmentcode; -#if 0 - if (!FUNCTION_LIB) { - GPU_nodes_free(nodes); - return NULL; + /* prune unused nodes */ + GPU_nodes_prune(nodes, frag_outlink); + + GPU_nodes_get_vertex_attributes(nodes, attribs); + + /* generate code */ + char *fragmentgen = code_generate_fragment(material, nodes, frag_outlink->output); + char *tmp = BLI_strdupcat(frag_lib, glsl_material_library); + + vertexcode = code_generate_vertex(nodes, vert_code, (geom_code != NULL)); + geometrycode = (geom_code) ? code_generate_geometry(nodes, geom_code) : NULL; + fragmentcode = BLI_strdupcat(tmp, fragmentgen); + + MEM_freeN(fragmentgen); + MEM_freeN(tmp); + + /* Cache lookup: Reuse shaders already compiled */ + uint32_t hash = gpu_pass_hash(vertexcode, geometrycode, fragmentcode, defines); + pass = gpu_pass_cache_lookup(vertexcode, geometrycode, fragmentcode, defines, hash); + if (pass) { + /* Cache hit. Reuse the same GPUPass and GPUShader. */ + shader = pass->shader; + pass->refcount += 1; + + MEM_SAFE_FREE(vertexcode); + MEM_SAFE_FREE(fragmentcode); + MEM_SAFE_FREE(geometrycode); + } + else { + /* Cache miss. (Re)compile the shader. */ + shader = GPU_shader_create(vertexcode, + fragmentcode, + geometrycode, + NULL, + defines); + + /* We still create a pass even if shader compilation + * fails to avoid trying to compile again and again. */ + pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); + pass->shader = shader; + pass->refcount = 1; + pass->hash = hash; + pass->vertexcode = vertexcode; + pass->fragmentcode = fragmentcode; + pass->geometrycode = geometrycode; + pass->libcode = glsl_material_library; + pass->defines = (defines) ? BLI_strdup(defines) : NULL; + + BLI_linklist_prepend(&pass_cache, pass); } -#endif - /* prune unused nodes */ - gpu_nodes_prune(nodes, outlink); - - gpu_nodes_get_vertex_attributes(nodes, attribs); - gpu_nodes_get_builtin_flag(nodes, builtins); - - /* generate code and compile with opengl */ - fragmentcode = code_generate_fragment(nodes, outlink->output); - vertexcode = code_generate_vertex(nodes, type); - geometrycode = code_generate_geometry(nodes, use_opensubdiv); - - int flags = GPU_SHADER_FLAGS_NONE; - if (use_opensubdiv) { - flags |= GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV; - } - if (use_new_shading) { - flags |= GPU_SHADER_FLAGS_NEW_SHADING; - } - shader = GPU_shader_create_ex(vertexcode, - fragmentcode, - geometrycode, - glsl_material_library, - NULL, - 0, - 0, - 0, - flags); - - /* failed? */ + /* did compilation failed ? */ if (!shader) { - if (fragmentcode) - MEM_freeN(fragmentcode); - if (vertexcode) - MEM_freeN(vertexcode); - memset(attribs, 0, sizeof(*attribs)); - memset(builtins, 0, sizeof(*builtins)); gpu_nodes_free(nodes); + /* Pass will not be used. Don't increment refcount. */ + pass->refcount--; return NULL; } + else { + gpu_nodes_extract_dynamic_inputs(shader, inputs, nodes); + return pass; + } +} - /* create pass */ - pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); - - pass->output = outlink->output; - pass->shader = shader; - pass->fragmentcode = fragmentcode; - pass->geometrycode = geometrycode; - pass->vertexcode = vertexcode; - pass->libcode = glsl_material_library; - - /* extract dynamic inputs and throw away nodes */ - gpu_nodes_extract_dynamic_inputs(pass, nodes); - gpu_nodes_free(nodes); - - return pass; +void GPU_pass_release(GPUPass *pass) +{ + BLI_assert(pass->refcount > 0); + pass->refcount--; } -void GPU_pass_free(GPUPass *pass) +static void gpu_pass_free(GPUPass *pass) { - GPU_shader_free(pass->shader); - gpu_inputs_free(&pass->inputs); - if (pass->fragmentcode) - MEM_freeN(pass->fragmentcode); - if (pass->geometrycode) - MEM_freeN(pass->geometrycode); - if (pass->vertexcode) - MEM_freeN(pass->vertexcode); + BLI_assert(pass->refcount == 0); + if (pass->shader) { + GPU_shader_free(pass->shader); + } + MEM_SAFE_FREE(pass->fragmentcode); + MEM_SAFE_FREE(pass->geometrycode); + MEM_SAFE_FREE(pass->vertexcode); + MEM_SAFE_FREE(pass->defines); MEM_freeN(pass); } @@ -1740,3 +1995,34 @@ void GPU_pass_free_nodes(ListBase *nodes) gpu_nodes_free(nodes); } +void GPU_pass_cache_garbage_collect(void) +{ + static int lasttime = 0; + const int shadercollectrate = 60; /* hardcoded for now. */ + int ctime = (int)PIL_check_seconds_timer(); + + if (ctime < shadercollectrate + lasttime) + return; + + lasttime = ctime; + + LinkNode *next, **prev_ln = &pass_cache; + for (LinkNode *ln = pass_cache; ln; ln = next) { + GPUPass *pass = (GPUPass *)ln->link; + next = ln->next; + if (pass->refcount == 0) { + gpu_pass_free(pass); + /* Remove from list */ + MEM_freeN(ln); + *prev_ln = next; + } + else { + prev_ln = &ln->next; + } + } +} + +void GPU_pass_cache_free(void) +{ + BLI_linklist_free(pass_cache, (LinkNodeFreeFP)gpu_pass_free); +} diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 7af17f9122d..328da36c3de 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -57,7 +57,8 @@ typedef enum GPUDataSource { GPU_SOURCE_OPENGL_BUILTIN, GPU_SOURCE_TEX_PIXEL, GPU_SOURCE_TEX, - GPU_SOURCE_ATTRIB + GPU_SOURCE_ATTRIB, + GPU_SOURCE_STRUCT } GPUDataSource; typedef enum { @@ -156,35 +157,45 @@ typedef struct GPUInput { } GPUInput; struct GPUPass { - struct GPUPass *next, *prev; - - ListBase inputs; - struct GPUOutput *output; struct GPUShader *shader; char *fragmentcode; char *geometrycode; char *vertexcode; + char *defines; const char *libcode; + unsigned int refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */ + uint32_t hash; /* Identity hash generated from all GLSL code. */ }; typedef struct GPUPass GPUPass; -GPUPass *GPU_generate_pass(ListBase *nodes, struct GPUNodeLink *outlink, - struct GPUVertexAttribs *attribs, int *builtin, - const GPUMatType type, const char *name, - const bool use_opensubdiv, - const bool use_new_shading); +GPUPass *GPU_generate_pass_new( + GPUMaterial *material, + GPUNodeLink *frag_outlink, struct GPUVertexAttribs *attribs, + ListBase *nodes, ListBase *inputs, + const char *vert_code, const char *geom_code, + const char *frag_lib, const char *defines); +GPUPass *GPU_generate_pass( + ListBase *nodes, ListBase *inputs, struct GPUNodeLink *outlink, + struct GPUVertexAttribs *attribs, int *builtin, + const GPUMatType type, const char *name, + const bool use_opensubdiv); struct GPUShader *GPU_pass_shader(GPUPass *pass); -void GPU_pass_bind(GPUPass *pass, double time, int mipmap); -void GPU_pass_update_uniforms(GPUPass *pass); -void GPU_pass_unbind(GPUPass *pass); +void GPU_nodes_get_vertex_attributes(ListBase *nodes, struct GPUVertexAttribs *attribs); +void GPU_nodes_prune(ListBase *nodes, struct GPUNodeLink *outlink); -void GPU_pass_free(GPUPass *pass); +void GPU_pass_bind(GPUPass *pass, ListBase *inputs, double time, int mipmap); +void GPU_pass_update_uniforms(GPUPass *pass, ListBase *inputs); +void GPU_pass_unbind(GPUPass *pass, ListBase *inputs); + +void GPU_pass_release(GPUPass *pass); void GPU_pass_free_nodes(ListBase *nodes); +void GPU_inputs_free(ListBase *inputs); + void gpu_codegen_init(void); void gpu_codegen_exit(void); diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c deleted file mode 100644 index 2f2a16f9e1d..00000000000 --- a/source/blender/gpu/intern/gpu_compositing.c +++ /dev/null @@ -1,1461 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): Antony Riakiotakis. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/gpu/intern/gpu_compositing.c - * \ingroup gpu - * - * System that manages framebuffer compositing. - */ - -#include "BLI_sys_types.h" -#include "BLI_rect.h" -#include "BLI_math.h" - -#include "BLI_rand.h" - -#include "DNA_vec_types.h" -#include "DNA_scene_types.h" -#include "DNA_gpu_types.h" - -#include "GPU_compositing.h" -#include "GPU_extensions.h" -#include "GPU_framebuffer.h" -#include "GPU_glew.h" -#include "GPU_shader.h" -#include "GPU_texture.h" - -#include "MEM_guardedalloc.h" - -static const float fullscreencos[4][2] = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, 1.0f}}; -static const float fullscreenuvs[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}}; - - -/* shader interfaces (legacy GL 2 style, without uniform buffer objects) */ - -typedef struct { - int ssao_uniform; - int ssao_color_uniform; - int color_uniform; - int depth_uniform; - int viewvecs_uniform; - int ssao_sample_params_uniform; - int ssao_concentric_tex; - int ssao_jitter_uniform; -} GPUSSAOShaderInterface; - -typedef struct { - int invrendertargetdim_uniform; - int color_uniform; - int dof_uniform; - int depth_uniform; - int viewvecs_uniform; -} GPUDOFHQPassOneInterface; - -typedef struct { - int rendertargetdim_uniform; - int color_uniform; - int coc_uniform; - int select_uniform; - int dof_uniform; -} GPUDOFHQPassTwoInterface; - -typedef struct { - int dof_uniform; - int invrendertargetdim_uniform; - int color_uniform; - int far_uniform; - int near_uniform; - int viewvecs_uniform; - int depth_uniform; -} GPUDOFHQPassThreeInterface; - -typedef struct { - int dof_uniform; - int invrendertargetdim_uniform; - int color_uniform; - int depth_uniform; - int viewvecs_uniform; -} GPUDOFPassOneInterface; - -typedef struct { - int dof_uniform; - int invrendertargetdim_uniform; - int color_uniform; - int depth_uniform; - int viewvecs_uniform; -} GPUDOFPassTwoInterface; - -typedef struct { - int near_coc_downsampled; - int near_coc_blurred; -} GPUDOFPassThreeInterface; - -typedef struct { - int near_coc_downsampled; - int invrendertargetdim_uniform; -} GPUDOFPassFourInterface; - -typedef struct { - int medium_blurred_uniform; - int high_blurred_uniform; - int dof_uniform; - int invrendertargetdim_uniform; - int original_uniform; - int depth_uniform; - int viewvecs_uniform; -} GPUDOFPassFiveInterface; - -typedef struct { - int depth_uniform; -} GPUDepthResolveInterface; - - -struct GPUFX { - /* we borrow the term gbuffer from deferred rendering however this is just a regular - * depth/color framebuffer. Could be extended later though */ - GPUFrameBuffer *gbuffer; - - /* dimensions of the gbuffer */ - int gbuffer_dim[2]; - - /* texture bound to the first color attachment of the gbuffer */ - GPUTexture *color_buffer; - - /* second texture used for ping-pong compositing */ - GPUTexture *color_buffer_sec; - /* texture bound to the depth attachment of the gbuffer */ - GPUTexture *depth_buffer; - GPUTexture *depth_buffer_xray; - - /* texture used for jittering for various effects */ - GPUTexture *jitter_buffer; - - /* all those buffers below have to coexist. - * Fortunately they are all quarter sized (1/16th of memory) of original framebuffer */ - int dof_downsampled_w; - int dof_downsampled_h; - - /* texture used for near coc and color blurring calculation */ - GPUTexture *dof_near_coc_buffer; - /* blurred near coc buffer. */ - GPUTexture *dof_near_coc_blurred_buffer; - /* final near coc buffer. */ - GPUTexture *dof_near_coc_final_buffer; - - /* half size blur buffer */ - GPUTexture *dof_half_downsampled_near; - GPUTexture *dof_half_downsampled_far; - /* high quality dof texture downsamplers. 6 levels means 64 pixels wide - should be enough */ - GPUTexture *dof_nearfar_coc; - GPUTexture *dof_near_blur; - GPUTexture *dof_far_blur; - - /* for high quality we use again a spiral texture with radius adapted */ - bool dof_high_quality; - - /* texture used for ssao */ - int ssao_sample_count_cache; - GPUTexture *ssao_spiral_samples_tex; - - - GPUFXSettings settings; - - /* or-ed flags of enabled effects */ - int effects; - - /* number of passes, needed to detect if ping pong buffer allocation is needed */ - int num_passes; - - /* we have a stencil, restore the previous state */ - bool restore_stencil; - - unsigned int vbuffer; -}; - -#if 0 -/* concentric mapping, see "A Low Distortion Map Between Disk and Square" and - * http://psgraphics.blogspot.nl/2011/01/improved-code-for-concentric-map.html */ -static GPUTexture * create_concentric_sample_texture(int side) -{ - GPUTexture *tex; - float midpoint = 0.5f * (side - 1); - float *texels = (float *)MEM_mallocN(sizeof(float) * 2 * side * side, "concentric_tex"); - int i, j; - - for (i = 0; i < side; i++) { - for (j = 0; j < side; j++) { - int index = (i * side + j) * 2; - float a = 1.0f - i / midpoint; - float b = 1.0f - j / midpoint; - float phi, r; - if (a * a > b * b) { - r = a; - phi = (M_PI_4) * (b / a); - } - else { - r = b; - phi = M_PI_2 - (M_PI_4) * (a / b); - } - texels[index] = r * cos(phi); - texels[index + 1] = r * sin(phi); - } - } - - tex = GPU_texture_create_1D_procedural(side * side, texels, NULL); - MEM_freeN(texels); - return tex; -} -#endif - -static GPUTexture *create_spiral_sample_texture(int numsaples) -{ - GPUTexture *tex; - float (*texels)[2] = MEM_mallocN(sizeof(float[2]) * numsaples, "concentric_tex"); - const float numsaples_inv = 1.0f / numsaples; - int i; - /* arbitrary number to ensure we don't get conciding samples every circle */ - const float spirals = 7.357; - - for (i = 0; i < numsaples; i++) { - float r = (i + 0.5f) * numsaples_inv; - float phi = r * spirals * (float)(2.0 * M_PI); - texels[i][0] = r * cosf(phi); - texels[i][1] = r * sinf(phi); - } - - tex = GPU_texture_create_1D_procedural(numsaples, (float *)texels, NULL); - MEM_freeN(texels); - return tex; -} - -/* generate a new FX compositor */ -GPUFX *GPU_fx_compositor_create(void) -{ - GPUFX *fx = MEM_callocN(sizeof(GPUFX), "GPUFX compositor"); - - glGenBuffers(1, &fx->vbuffer); - glBindBuffer(GL_ARRAY_BUFFER, fx->vbuffer); - glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * sizeof(float), fullscreencos); - glBufferSubData(GL_ARRAY_BUFFER, 8 * sizeof(float), 8 * sizeof(float), fullscreenuvs); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - return fx; -} - -static void cleanup_fx_dof_buffers(GPUFX *fx) -{ - if (fx->dof_near_coc_blurred_buffer) { - GPU_texture_free(fx->dof_near_coc_blurred_buffer); - fx->dof_near_coc_blurred_buffer = NULL; - } - if (fx->dof_near_coc_buffer) { - GPU_texture_free(fx->dof_near_coc_buffer); - fx->dof_near_coc_buffer = NULL; - } - if (fx->dof_near_coc_final_buffer) { - GPU_texture_free(fx->dof_near_coc_final_buffer); - fx->dof_near_coc_final_buffer = NULL; - } - - if (fx->dof_half_downsampled_near) { - GPU_texture_free(fx->dof_half_downsampled_near); - fx->dof_half_downsampled_near = NULL; - } - if (fx->dof_half_downsampled_far) { - GPU_texture_free(fx->dof_half_downsampled_far); - fx->dof_half_downsampled_far = NULL; - } - if (fx->dof_nearfar_coc) { - GPU_texture_free(fx->dof_nearfar_coc); - fx->dof_nearfar_coc = NULL; - } - if (fx->dof_near_blur) { - GPU_texture_free(fx->dof_near_blur); - fx->dof_near_blur = NULL; - } - if (fx->dof_far_blur) { - GPU_texture_free(fx->dof_far_blur); - fx->dof_far_blur = NULL; - } -} - -static void cleanup_fx_gl_data(GPUFX *fx, bool do_fbo) -{ - if (fx->color_buffer) { - GPU_framebuffer_texture_detach(fx->color_buffer); - GPU_texture_free(fx->color_buffer); - fx->color_buffer = NULL; - } - - if (fx->color_buffer_sec) { - GPU_framebuffer_texture_detach(fx->color_buffer_sec); - GPU_texture_free(fx->color_buffer_sec); - fx->color_buffer_sec = NULL; - } - - if (fx->depth_buffer) { - GPU_framebuffer_texture_detach(fx->depth_buffer); - GPU_texture_free(fx->depth_buffer); - fx->depth_buffer = NULL; - } - - if (fx->depth_buffer_xray) { - GPU_framebuffer_texture_detach(fx->depth_buffer_xray); - GPU_texture_free(fx->depth_buffer_xray); - fx->depth_buffer_xray = NULL; - } - - cleanup_fx_dof_buffers(fx); - - if (fx->ssao_spiral_samples_tex) { - GPU_texture_free(fx->ssao_spiral_samples_tex); - fx->ssao_spiral_samples_tex = NULL; - } - - if (fx->jitter_buffer && do_fbo) { - GPU_texture_free(fx->jitter_buffer); - fx->jitter_buffer = NULL; - } - - if (fx->gbuffer && do_fbo) { - GPU_framebuffer_free(fx->gbuffer); - fx->gbuffer = NULL; - } -} - -/* destroy a text compositor */ -void GPU_fx_compositor_destroy(GPUFX *fx) -{ - cleanup_fx_gl_data(fx, true); - glDeleteBuffers(1, &fx->vbuffer); - MEM_freeN(fx); -} - -static GPUTexture * create_jitter_texture(void) -{ - float jitter[64 * 64][2]; - int i; - - for (i = 0; i < 64 * 64; i++) { - jitter[i][0] = 2.0f * BLI_frand() - 1.0f; - jitter[i][1] = 2.0f * BLI_frand() - 1.0f; - normalize_v2(jitter[i]); - } - - return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], true, NULL); -} - - -bool GPU_fx_compositor_initialize_passes( - GPUFX *fx, const rcti *rect, const rcti *scissor_rect, - const GPUFXSettings *fx_settings) -{ - int w = BLI_rcti_size_x(rect), h = BLI_rcti_size_y(rect); - char err_out[256]; - int num_passes = 0; - char fx_flag; - - fx->effects = 0; - - if (!GLEW_EXT_framebuffer_object) - return false; - - if (!fx_settings) { - cleanup_fx_gl_data(fx, true); - return false; - } - - fx_flag = fx_settings->fx_flag; - - /* disable effects if no options passed for them */ - if (!fx_settings->dof) { - fx_flag &= ~GPU_FX_FLAG_DOF; - } - if (!fx_settings->ssao || fx_settings->ssao->samples < 1) { - fx_flag &= ~GPU_FX_FLAG_SSAO; - } - - if (!fx_flag) { - cleanup_fx_gl_data(fx, true); - return false; - } - - /* scissor is missing when drawing offscreen, in that case, dimensions match exactly. In opposite case - * add one to match viewport dimensions */ - if (scissor_rect) { - w++; - h++; - } - - fx->num_passes = 0; - /* dof really needs a ping-pong buffer to work */ - if (fx_flag & GPU_FX_FLAG_DOF) - num_passes++; - - if (fx_flag & GPU_FX_FLAG_SSAO) - num_passes++; - - if (!fx->gbuffer) { - fx->gbuffer = GPU_framebuffer_create(); - - if (!fx->gbuffer) { - return false; - } - } - - /* try creating the jitter texture */ - if (!fx->jitter_buffer) - fx->jitter_buffer = create_jitter_texture(); - - /* check if color buffers need recreation */ - if (!fx->color_buffer || !fx->depth_buffer || w != fx->gbuffer_dim[0] || h != fx->gbuffer_dim[1]) { - cleanup_fx_gl_data(fx, false); - - if (!(fx->color_buffer = GPU_texture_create_2D(w, h, NULL, GPU_HDR_NONE, err_out))) { - printf(".256%s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - - if (!(fx->depth_buffer = GPU_texture_create_depth(w, h, err_out))) { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - } - - if (fx_flag & GPU_FX_FLAG_SSAO) { - if (fx_settings->ssao->samples != fx->ssao_sample_count_cache || !fx->ssao_spiral_samples_tex) { - if (fx_settings->ssao->samples < 1) - fx_settings->ssao->samples = 1; - - fx->ssao_sample_count_cache = fx_settings->ssao->samples; - - if (fx->ssao_spiral_samples_tex) { - GPU_texture_free(fx->ssao_spiral_samples_tex); - } - - fx->ssao_spiral_samples_tex = create_spiral_sample_texture(fx_settings->ssao->samples); - } - } - else { - if (fx->ssao_spiral_samples_tex) { - GPU_texture_free(fx->ssao_spiral_samples_tex); - fx->ssao_spiral_samples_tex = NULL; - } - } - - /* create textures for dof effect */ - if (fx_flag & GPU_FX_FLAG_DOF) { - bool dof_high_quality = (fx_settings->dof->high_quality != 0) && - GPU_geometry_shader_support() && GPU_instanced_drawing_support(); - - /* cleanup buffers if quality setting has changed (no need to keep more buffers around than necessary ) */ - if (dof_high_quality != fx->dof_high_quality) - cleanup_fx_dof_buffers(fx); - - if (dof_high_quality) { - fx->dof_downsampled_w = w / 2; - fx->dof_downsampled_h = h / 2; - - if (!fx->dof_half_downsampled_near || !fx->dof_nearfar_coc || !fx->dof_near_blur || - !fx->dof_far_blur || !fx->dof_half_downsampled_far) - { - - if (!(fx->dof_half_downsampled_near = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - if (!(fx->dof_half_downsampled_far = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - if (!(fx->dof_nearfar_coc = GPU_texture_create_2D_procedural( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, false, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - - - if (!(fx->dof_near_blur = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_HALF_FLOAT, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - - if (!(fx->dof_far_blur = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_HALF_FLOAT, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - } - } - else { - fx->dof_downsampled_w = w / 4; - fx->dof_downsampled_h = h / 4; - - if (!fx->dof_near_coc_buffer || !fx->dof_near_coc_blurred_buffer || !fx->dof_near_coc_final_buffer) { - - if (!(fx->dof_near_coc_buffer = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - if (!(fx->dof_near_coc_blurred_buffer = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - if (!(fx->dof_near_coc_final_buffer = GPU_texture_create_2D( - fx->dof_downsampled_w, fx->dof_downsampled_h, NULL, GPU_HDR_NONE, err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - } - } - - fx->dof_high_quality = dof_high_quality; - } - else { - /* cleanup unnecessary buffers */ - cleanup_fx_dof_buffers(fx); - } - - /* we need to pass data between shader stages, allocate an extra color buffer */ - if (num_passes > 1) { - if (!fx->color_buffer_sec) { - if (!(fx->color_buffer_sec = GPU_texture_create_2D(w, h, NULL, GPU_HDR_NONE, err_out))) { - printf(".256%s\n", err_out); - cleanup_fx_gl_data(fx, true); - return false; - } - } - } - else { - if (fx->color_buffer_sec) { - GPU_framebuffer_texture_detach(fx->color_buffer_sec); - GPU_texture_free(fx->color_buffer_sec); - fx->color_buffer_sec = NULL; - } - } - - /* bind the buffers */ - - /* first depth buffer, because system assumes read/write buffers */ - if (!GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer, 0, err_out)) - printf("%.256s\n", err_out); - - if (!GPU_framebuffer_texture_attach(fx->gbuffer, fx->color_buffer, 0, err_out)) - printf("%.256s\n", err_out); - - if (!GPU_framebuffer_check_valid(fx->gbuffer, err_out)) - printf("%.256s\n", err_out); - - GPU_texture_bind_as_framebuffer(fx->color_buffer); - - /* enable scissor test. It's needed to ensure sculpting works correctly */ - if (scissor_rect) { - int w_sc = BLI_rcti_size_x(scissor_rect) + 1; - int h_sc = BLI_rcti_size_y(scissor_rect) + 1; - glPushAttrib(GL_SCISSOR_BIT); - glEnable(GL_SCISSOR_TEST); - glScissor(scissor_rect->xmin - rect->xmin, scissor_rect->ymin - rect->ymin, - w_sc, h_sc); - fx->restore_stencil = true; - } - else { - fx->restore_stencil = false; - } - - fx->effects = fx_flag; - - if (fx_settings) - fx->settings = *fx_settings; - fx->gbuffer_dim[0] = w; - fx->gbuffer_dim[1] = h; - - fx->num_passes = num_passes; - - return true; -} - -static void gpu_fx_bind_render_target(int *passes_left, GPUFX *fx, struct GPUOffScreen *ofs, GPUTexture *target) -{ - if ((*passes_left)-- == 1) { - GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); - if (ofs) { - GPU_offscreen_bind(ofs, false); - } - else - GPU_framebuffer_restore(); - } - else { - /* bind the ping buffer to the color buffer */ - GPU_framebuffer_texture_attach(fx->gbuffer, target, 0, NULL); - } -} - -void GPU_fx_compositor_setup_XRay_pass(GPUFX *fx, bool do_xray) -{ - char err_out[256]; - - if (do_xray) { - if (!fx->depth_buffer_xray && - !(fx->depth_buffer_xray = GPU_texture_create_depth(fx->gbuffer_dim[0], fx->gbuffer_dim[1], err_out))) - { - printf("%.256s\n", err_out); - cleanup_fx_gl_data(fx, true); - return; - } - } - else { - if (fx->depth_buffer_xray) { - GPU_framebuffer_texture_detach(fx->depth_buffer_xray); - GPU_texture_free(fx->depth_buffer_xray); - fx->depth_buffer_xray = NULL; - } - return; - } - - GPU_framebuffer_texture_detach(fx->depth_buffer); - - /* first depth buffer, because system assumes read/write buffers */ - if (!GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer_xray, 0, err_out)) - printf("%.256s\n", err_out); -} - - -void GPU_fx_compositor_XRay_resolve(GPUFX *fx) -{ - GPUShader *depth_resolve_shader; - GPU_framebuffer_texture_detach(fx->depth_buffer_xray); - - /* attach regular framebuffer */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->depth_buffer, 0, NULL); - - /* full screen quad where we will always write to depth buffer */ - glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT); - glDepthFunc(GL_ALWAYS); - /* disable scissor from sculpt if any */ - glDisable(GL_SCISSOR_TEST); - /* disable writing to color buffer, it's depth only pass */ - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - - /* set up quad buffer */ - glBindBuffer(GL_ARRAY_BUFFER, fx->vbuffer); - glVertexPointer(2, GL_FLOAT, 0, NULL); - glTexCoordPointer(2, GL_FLOAT, 0, ((GLubyte *)NULL + 8 * sizeof(float))); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - depth_resolve_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_RESOLVE, false); - - if (depth_resolve_shader) { - GPUDepthResolveInterface *interface = GPU_shader_get_interface(depth_resolve_shader); - - GPU_shader_bind(depth_resolve_shader); - - GPU_texture_bind(fx->depth_buffer_xray, 0); - GPU_texture_filter_mode(fx->depth_buffer_xray, false, true); - GPU_shader_uniform_texture(depth_resolve_shader, interface->depth_uniform, fx->depth_buffer_xray); - - /* draw */ - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - /* disable bindings */ - GPU_texture_filter_mode(fx->depth_buffer_xray, true, false); - GPU_texture_unbind(fx->depth_buffer_xray); - - GPU_shader_unbind(); - } - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glPopAttrib(); -} - - -bool GPU_fx_do_composite_pass( - GPUFX *fx, float projmat[4][4], bool is_persp, - struct Scene *scene, struct GPUOffScreen *ofs) -{ - GPUTexture *src, *target; - int numslots = 0; - float invproj[4][4]; - int i; - float dfdyfac[2]; - /* number of passes left. when there are no more passes, the result is passed to the frambuffer */ - int passes_left = fx->num_passes; - /* view vectors for the corners of the view frustum. Can be used to recreate the world space position easily */ - float viewvecs[3][4] = { - {-1.0f, -1.0f, -1.0f, 1.0f}, - {1.0f, -1.0f, -1.0f, 1.0f}, - {-1.0f, 1.0f, -1.0f, 1.0f} - }; - - if (fx->effects == 0) - return false; - - GPU_get_dfdy_factors(dfdyfac); - /* first, unbind the render-to-texture framebuffer */ - GPU_framebuffer_texture_detach(fx->color_buffer); - GPU_framebuffer_texture_detach(fx->depth_buffer); - - if (fx->restore_stencil) - glPopAttrib(); - - src = fx->color_buffer; - target = fx->color_buffer_sec; - - /* set up quad buffer */ - glBindBuffer(GL_ARRAY_BUFFER, fx->vbuffer); - glVertexPointer(2, GL_FLOAT, 0, NULL); - glTexCoordPointer(2, GL_FLOAT, 0, ((GLubyte *)NULL + 8 * sizeof(float))); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - /* full screen FX pass */ - - /* invert the view matrix */ - invert_m4_m4(invproj, projmat); - - /* convert the view vectors to view space */ - for (i = 0; i < 3; i++) { - mul_m4_v4(invproj, viewvecs[i]); - /* normalized trick see http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ - mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][3]); - if (is_persp) - mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]); - viewvecs[i][3] = 1.0; - } - - /* we need to store the differences */ - viewvecs[1][0] -= viewvecs[0][0]; - viewvecs[1][1] = viewvecs[2][1] - viewvecs[0][1]; - - /* calculate a depth offset as well */ - if (!is_persp) { - float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f}; - mul_m4_v4(invproj, vec_far); - mul_v3_fl(vec_far, 1.0f / vec_far[3]); - viewvecs[1][2] = vec_far[2] - viewvecs[0][2]; - } - - /* set invalid color in case shader fails */ - glColor3f(1.0, 0.0, 1.0); - glDisable(GL_DEPTH_TEST); - - /* ssao pass */ - if (fx->effects & GPU_FX_FLAG_SSAO) { - GPUShader *ssao_shader; - ssao_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_SSAO, is_persp); - if (ssao_shader) { - const GPUSSAOSettings *fx_ssao = fx->settings.ssao; - /* adjust attenuation to be scale invariant */ - float attenuation = fx_ssao->attenuation / (fx_ssao->distance_max * fx_ssao->distance_max); - float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, attenuation, 0.0f}; - float sample_params[3]; - - sample_params[0] = fx->ssao_sample_count_cache; - /* multiplier so we tile the random texture on screen */ - sample_params[1] = fx->gbuffer_dim[0] / 64.0; - sample_params[2] = fx->gbuffer_dim[1] / 64.0; - - ssao_params[3] = (passes_left == 1 && !ofs) ? dfdyfac[0] : dfdyfac[1]; - - GPUSSAOShaderInterface *interface = GPU_shader_get_interface(ssao_shader); - - GPU_shader_bind(ssao_shader); - - GPU_shader_uniform_vector(ssao_shader, interface->ssao_uniform, 4, 1, ssao_params); - GPU_shader_uniform_vector(ssao_shader, interface->ssao_color_uniform, 4, 1, fx_ssao->color); - GPU_shader_uniform_vector(ssao_shader, interface->viewvecs_uniform, 4, 3, viewvecs[0]); - GPU_shader_uniform_vector(ssao_shader, interface->ssao_sample_params_uniform, 3, 1, sample_params); - - GPU_texture_bind(src, numslots++); - GPU_shader_uniform_texture(ssao_shader, interface->color_uniform, src); - - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_texture_filter_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(ssao_shader, interface->depth_uniform, fx->depth_buffer); - - GPU_texture_bind(fx->jitter_buffer, numslots++); - GPU_shader_uniform_texture(ssao_shader, interface->ssao_jitter_uniform, fx->jitter_buffer); - - GPU_texture_bind(fx->ssao_spiral_samples_tex, numslots++); - GPU_shader_uniform_texture(ssao_shader, interface->ssao_concentric_tex, fx->ssao_spiral_samples_tex); - - /* draw */ - gpu_fx_bind_render_target(&passes_left, fx, ofs, target); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - /* disable bindings */ - GPU_texture_unbind(src); - GPU_texture_filter_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); - GPU_texture_unbind(fx->jitter_buffer); - GPU_texture_unbind(fx->ssao_spiral_samples_tex); - - /* may not be attached, in that case this just returns */ - if (target) { - GPU_framebuffer_texture_detach(target); - if (ofs) { - GPU_offscreen_bind(ofs, false); - } - else { - GPU_framebuffer_restore(); - } - } - - /* swap here, after src/target have been unbound */ - SWAP(GPUTexture *, target, src); - numslots = 0; - } - } - - /* second pass, dof */ - if (fx->effects & GPU_FX_FLAG_DOF) { - const GPUDOFSettings *fx_dof = fx->settings.dof; - float dof_params[4]; - float scale = scene->unit.system ? scene->unit.scale_length : 1.0f; - /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm - * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though - * because the shader reads coordinates in world space, which is in blender units. - * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */ - float scale_camera = 0.001f / scale; - /* we want radius here for the aperture number */ - float aperture = 0.5f * scale_camera * fx_dof->focal_length / fx_dof->fstop; - - dof_params[0] = aperture * fabsf(scale_camera * fx_dof->focal_length / - (fx_dof->focus_distance - scale_camera * fx_dof->focal_length)); - dof_params[1] = fx_dof->focus_distance; - dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); - dof_params[3] = fx_dof->num_blades; - - if (fx->dof_high_quality) { - GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3; - - /* custom shaders close to the effect described in CryEngine 3 Graphics Gems */ - dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE, is_persp); - dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO, is_persp); - dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE, is_persp); - - /* error occured, restore framebuffers and return */ - if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3)) { - GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); - GPU_framebuffer_restore(); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - GPU_shader_unbind(); - glBindBuffer(GL_ARRAY_BUFFER, 0); - return false; - } - - /* pass first, downsample the color buffer to near/far targets and calculate coc texture */ - { - float invrendertargetdim[2] = {1.0f / fx->dof_downsampled_w, 1.0f / fx->dof_downsampled_h}; - - GPUDOFHQPassOneInterface *interface = GPU_shader_get_interface(dof_shader_pass1); - - GPU_shader_bind(dof_shader_pass1); - - GPU_shader_uniform_vector(dof_shader_pass1, interface->dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass1, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass1, interface->viewvecs_uniform, 4, 3, viewvecs[0]); - - GPU_shader_uniform_vector(dof_shader_pass1, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_texture_filter_mode(fx->depth_buffer, false, false); - GPU_shader_uniform_texture(dof_shader_pass1, interface->depth_uniform, fx->depth_buffer); - - GPU_texture_bind(src, numslots++); - /* disable filtering for the texture so custom downsample can do the right thing */ - GPU_texture_filter_mode(src, false, false); - GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, src); - - /* target is the downsampled coc buffer */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_half_downsampled_near, 0, NULL); - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_half_downsampled_far, 1, NULL); - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_nearfar_coc, 2, NULL); - /* binding takes care of setting the viewport to the downsampled size */ - GPU_framebuffer_slots_bind(fx->gbuffer, 0); - - GPU_framebuffer_check_valid(fx->gbuffer, NULL); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_filter_mode(src, false, true); - GPU_texture_unbind(src); - GPU_texture_filter_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); - - GPU_framebuffer_texture_detach(fx->dof_half_downsampled_near); - GPU_framebuffer_texture_detach(fx->dof_half_downsampled_far); - GPU_framebuffer_texture_detach(fx->dof_nearfar_coc); - GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_half_downsampled_near); - - numslots = 0; - } - - /* second pass, shoot quads for every pixel in the downsampled buffers, scaling according - * to circle of confusion */ - { - int rendertargetdim[2] = {fx->dof_downsampled_w, fx->dof_downsampled_h}; - float selection[2] = {0.0f, 1.0f}; - - GPUDOFHQPassTwoInterface *interface = GPU_shader_get_interface(dof_shader_pass2); - - GPU_shader_bind(dof_shader_pass2); - - GPU_shader_uniform_vector(dof_shader_pass2, interface->dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector_int(dof_shader_pass2, interface->rendertargetdim_uniform, 2, 1, rendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass2, interface->select_uniform, 2, 1, selection); - - GPU_texture_bind(fx->dof_nearfar_coc, numslots++); - GPU_shader_uniform_texture(dof_shader_pass2, interface->coc_uniform, fx->dof_nearfar_coc); - - GPU_texture_bind(fx->dof_half_downsampled_far, numslots++); - GPU_texture_bind(fx->dof_half_downsampled_near, numslots++); - GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_half_downsampled_far); - GPU_texture_filter_mode(fx->dof_half_downsampled_far, false, false); - - /* target is the downsampled coc buffer */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_far_blur, 0, NULL); - GPU_texture_bind_as_framebuffer(fx->dof_far_blur); - - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); - glPointSize(1.0f); - /* have to clear the buffer unfortunately */ - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - /* the draw call we all waited for, draw a point per pixel, scaled per circle of confusion */ - glDrawArraysInstancedARB(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); - - GPU_texture_unbind(fx->dof_half_downsampled_far); - GPU_framebuffer_texture_detach(fx->dof_far_blur); - - GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_half_downsampled_near); - GPU_texture_filter_mode(fx->dof_half_downsampled_near, false, false); - - selection[0] = 1.0f; - selection[1] = 0.0f; - - GPU_shader_uniform_vector(dof_shader_pass2, interface->select_uniform, 2, 1, selection); - - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_blur, 0, NULL); - /* have to clear the buffer unfortunately */ - glClear(GL_COLOR_BUFFER_BIT); - /* the draw call we all waited for, draw a point per pixel, scaled per circle of confusion */ - glDrawArraysInstancedARB(GL_POINTS, 0, 1, fx->dof_downsampled_w * fx->dof_downsampled_h); - - /* disable bindings */ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_BLEND); - - GPU_framebuffer_texture_detach(fx->dof_near_blur); - - GPU_texture_unbind(fx->dof_half_downsampled_near); - GPU_texture_unbind(fx->dof_nearfar_coc); - - GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_far_blur); - } - - /* third pass, accumulate the near/far blur fields */ - { - float invrendertargetdim[2] = {1.0f / fx->dof_downsampled_w, 1.0f / fx->dof_downsampled_h}; - - GPUDOFHQPassThreeInterface *interface = GPU_shader_get_interface(dof_shader_pass3); - - GPU_shader_bind(dof_shader_pass3); - - GPU_shader_uniform_vector(dof_shader_pass3, interface->dof_uniform, 4, 1, dof_params); - - GPU_shader_uniform_vector(dof_shader_pass3, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass3, interface->viewvecs_uniform, 4, 3, viewvecs[0]); - - GPU_texture_bind(fx->dof_near_blur, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, interface->near_uniform, fx->dof_near_blur); - GPU_texture_filter_mode(fx->dof_near_blur, false, true); - - GPU_texture_bind(fx->dof_far_blur, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, interface->far_uniform, fx->dof_far_blur); - GPU_texture_filter_mode(fx->dof_far_blur, false, true); - - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_texture_filter_mode(fx->depth_buffer, false, false); - GPU_shader_uniform_texture(dof_shader_pass3, interface->depth_uniform, fx->depth_buffer); - - GPU_texture_bind(src, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, interface->color_uniform, src); - - /* if this is the last pass, prepare for rendering on the frambuffer */ - gpu_fx_bind_render_target(&passes_left, fx, ofs, target); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_blur); - GPU_texture_unbind(fx->dof_far_blur); - GPU_texture_unbind(src); - GPU_texture_filter_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); - - /* may not be attached, in that case this just returns */ - if (target) { - GPU_framebuffer_texture_detach(target); - if (ofs) { - GPU_offscreen_bind(ofs, false); - } - else { - GPU_framebuffer_restore(); - } - } - - numslots = 0; - } - } - else { - GPUShader *dof_shader_pass1, *dof_shader_pass2, *dof_shader_pass3, *dof_shader_pass4, *dof_shader_pass5; - - /* DOF effect has many passes but most of them are performed - * on a texture whose dimensions are 4 times less than the original - * (16 times lower than original screen resolution). - * Technique used is not very exact but should be fast enough and is based - * on "Practical Post-Process Depth of Field" - * see http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html */ - dof_shader_pass1 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE, is_persp); - dof_shader_pass2 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO, is_persp); - dof_shader_pass3 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE, is_persp); - dof_shader_pass4 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR, is_persp); - dof_shader_pass5 = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE, is_persp); - - /* error occured, restore framebuffers and return */ - if (!(dof_shader_pass1 && dof_shader_pass2 && dof_shader_pass3 && dof_shader_pass4 && dof_shader_pass5)) { - GPU_framebuffer_texture_unbind(fx->gbuffer, NULL); - GPU_framebuffer_restore(); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - GPU_shader_unbind(); - glBindBuffer(GL_ARRAY_BUFFER, 0); - return false; - } - - /* pass first, first level of blur in low res buffer */ - { - float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; - - GPUDOFPassOneInterface *interface = GPU_shader_get_interface(dof_shader_pass1); - - GPU_shader_bind(dof_shader_pass1); - - GPU_shader_uniform_vector(dof_shader_pass1, interface->dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass1, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass1, interface->viewvecs_uniform, 4, 3, viewvecs[0]); - - GPU_texture_bind(src, numslots++); - GPU_shader_uniform_texture(dof_shader_pass1, interface->color_uniform, src); - - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_texture_filter_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(dof_shader_pass1, interface->depth_uniform, fx->depth_buffer); - - /* target is the downsampled coc buffer */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); - /* binding takes care of setting the viewport to the downsampled size */ - GPU_texture_bind_as_framebuffer(fx->dof_near_coc_buffer); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(src); - GPU_texture_filter_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); - - GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); - numslots = 0; - } - - /* second pass, gaussian blur the downsampled image */ - { - float invrendertargetdim[2] = {1.0f / GPU_texture_width(fx->dof_near_coc_blurred_buffer), - 1.0f / GPU_texture_height(fx->dof_near_coc_blurred_buffer)}; - float tmp = invrendertargetdim[0]; - invrendertargetdim[0] = 0.0f; - - GPUDOFPassTwoInterface *interface = GPU_shader_get_interface(dof_shader_pass2); - - dof_params[2] = GPU_texture_width(fx->dof_near_coc_blurred_buffer) / (scale_camera * fx_dof->sensor); - - /* Blurring vertically */ - GPU_shader_bind(dof_shader_pass2); - - GPU_shader_uniform_vector(dof_shader_pass2, interface->dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass2, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass2, interface->viewvecs_uniform, 4, 3, viewvecs[0]); - - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_texture_filter_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(dof_shader_pass2, interface->depth_uniform, fx->depth_buffer); - - GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_near_coc_buffer); - - /* use final buffer as a temp here */ - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); - - /* Drawing quad */ - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - /* *unbind/detach */ - GPU_texture_unbind(fx->dof_near_coc_buffer); - GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); - - /* Blurring horizontally */ - invrendertargetdim[0] = tmp; - invrendertargetdim[1] = 0.0f; - GPU_shader_uniform_vector(dof_shader_pass2, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - - GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass2, interface->color_uniform, fx->dof_near_coc_final_buffer); - - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_blurred_buffer, 0, NULL); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - /* *unbind/detach */ - GPU_texture_filter_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); - - GPU_texture_unbind(fx->dof_near_coc_final_buffer); - GPU_framebuffer_texture_detach(fx->dof_near_coc_blurred_buffer); - - dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); - - numslots = 0; - } - - /* third pass, calculate near coc */ - { - GPUDOFPassThreeInterface *interface = GPU_shader_get_interface(dof_shader_pass3); - - GPU_shader_bind(dof_shader_pass3); - - GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, interface->near_coc_downsampled, fx->dof_near_coc_buffer); - - GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass3, interface->near_coc_blurred, fx->dof_near_coc_blurred_buffer); - - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_final_buffer, 0, NULL); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_coc_buffer); - GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); - - /* unbinding here restores the size to the original */ - GPU_framebuffer_texture_detach(fx->dof_near_coc_final_buffer); - - numslots = 0; - } - - /* fourth pass blur final coc once to eliminate discontinuities */ - { - float invrendertargetdim[2] = {1.0f / GPU_texture_width(fx->dof_near_coc_blurred_buffer), - 1.0f / GPU_texture_height(fx->dof_near_coc_blurred_buffer)}; - - GPUDOFPassFourInterface *interface = GPU_shader_get_interface(dof_shader_pass4); - - GPU_shader_bind(dof_shader_pass4); - - GPU_texture_bind(fx->dof_near_coc_final_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass4, interface->near_coc_downsampled, fx->dof_near_coc_final_buffer); - GPU_shader_uniform_vector(dof_shader_pass4, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - - GPU_framebuffer_texture_attach(fx->gbuffer, fx->dof_near_coc_buffer, 0, NULL); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_coc_final_buffer); - - /* unbinding here restores the size to the original */ - GPU_framebuffer_texture_unbind(fx->gbuffer, fx->dof_near_coc_buffer); - GPU_framebuffer_texture_detach(fx->dof_near_coc_buffer); - - numslots = 0; - } - - /* final pass, merge blurred layers according to final calculated coc */ - { - float invrendertargetdim[2] = {1.0f / fx->gbuffer_dim[0], 1.0f / fx->gbuffer_dim[1]}; - - GPUDOFPassFiveInterface *interface = GPU_shader_get_interface(dof_shader_pass5); - - GPU_shader_bind(dof_shader_pass5); - - GPU_shader_uniform_vector(dof_shader_pass5, interface->dof_uniform, 4, 1, dof_params); - GPU_shader_uniform_vector(dof_shader_pass5, interface->invrendertargetdim_uniform, 2, 1, invrendertargetdim); - GPU_shader_uniform_vector(dof_shader_pass5, interface->viewvecs_uniform, 4, 3, viewvecs[0]); - - GPU_texture_bind(src, numslots++); - GPU_shader_uniform_texture(dof_shader_pass5, interface->original_uniform, src); - - GPU_texture_bind(fx->dof_near_coc_blurred_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass5, interface->high_blurred_uniform, fx->dof_near_coc_blurred_buffer); - - GPU_texture_bind(fx->dof_near_coc_buffer, numslots++); - GPU_shader_uniform_texture(dof_shader_pass5, interface->medium_blurred_uniform, fx->dof_near_coc_buffer); - - GPU_texture_bind(fx->depth_buffer, numslots++); - GPU_texture_filter_mode(fx->depth_buffer, false, true); - GPU_shader_uniform_texture(dof_shader_pass5, interface->depth_uniform, fx->depth_buffer); - - /* if this is the last pass, prepare for rendering on the frambuffer */ - gpu_fx_bind_render_target(&passes_left, fx, ofs, target); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - /* disable bindings */ - GPU_texture_unbind(fx->dof_near_coc_buffer); - GPU_texture_unbind(fx->dof_near_coc_blurred_buffer); - GPU_texture_unbind(src); - GPU_texture_filter_mode(fx->depth_buffer, true, false); - GPU_texture_unbind(fx->depth_buffer); - - /* may not be attached, in that case this just returns */ - if (target) { - GPU_framebuffer_texture_detach(target); - if (ofs) { - GPU_offscreen_bind(ofs, false); - } - else { - GPU_framebuffer_restore(); - } - } - - SWAP(GPUTexture *, target, src); - numslots = 0; - } - } - } - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - GPU_shader_unbind(); - - return true; -} - -void GPU_fx_compositor_init_dof_settings(GPUDOFSettings *fx_dof) -{ - fx_dof->fstop = 128.0f; - fx_dof->focal_length = 1.0f; - fx_dof->focus_distance = 1.0f; - fx_dof->sensor = 1.0f; - fx_dof->num_blades = 6; -} - -void GPU_fx_compositor_init_ssao_settings(GPUSSAOSettings *fx_ssao) -{ - fx_ssao->factor = 1.0f; - fx_ssao->distance_max = 0.2f; - fx_ssao->attenuation = 1.0f; - fx_ssao->samples = 20; -} - -void GPU_fx_shader_init_interface(struct GPUShader *shader, GPUFXShaderEffect effect) -{ - if (!shader) - return; - - switch (effect) { - case GPU_SHADER_FX_SSAO: - { - GPUSSAOShaderInterface *interface = MEM_mallocN(sizeof(GPUSSAOShaderInterface), "GPUSSAOShaderInterface"); - - interface->ssao_uniform = GPU_shader_get_uniform(shader, "ssao_params"); - interface->ssao_color_uniform = GPU_shader_get_uniform(shader, "ssao_color"); - interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs"); - interface->ssao_sample_params_uniform = GPU_shader_get_uniform(shader, "ssao_sample_params"); - interface->ssao_concentric_tex = GPU_shader_get_uniform(shader, "ssao_concentric_tex"); - interface->ssao_jitter_uniform = GPU_shader_get_uniform(shader, "jitter_tex"); - - GPU_shader_set_interface(shader, interface); - break; - } - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE: - { - GPUDOFHQPassOneInterface *interface = MEM_mallocN(sizeof(GPUDOFHQPassOneInterface), "GPUDOFHQPassOneInterface"); - - interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim"); - interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params"); - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs"); - - GPU_shader_set_interface(shader, interface); - break; - } - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO: - { - GPUDOFHQPassTwoInterface *interface = MEM_mallocN(sizeof(GPUDOFHQPassTwoInterface), "GPUDOFHQPassTwoInterface"); - - interface->rendertargetdim_uniform = GPU_shader_get_uniform(shader, "rendertargetdim"); - interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->coc_uniform = GPU_shader_get_uniform(shader, "cocbuffer"); - interface->select_uniform = GPU_shader_get_uniform(shader, "layerselection"); - interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params"); - - GPU_shader_set_interface(shader, interface); - break; - } - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE: - { - GPUDOFHQPassThreeInterface *interface = MEM_mallocN(sizeof(GPUDOFHQPassThreeInterface), "GPUDOFHQPassThreeInterface"); - - interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params"); - interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim"); - interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->far_uniform = GPU_shader_get_uniform(shader, "farbuffer"); - interface->near_uniform = GPU_shader_get_uniform(shader, "nearbuffer"); - interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs"); - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - - GPU_shader_set_interface(shader, interface); - break; - } - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE: - { - GPUDOFPassOneInterface *interface = MEM_mallocN(sizeof(GPUDOFPassOneInterface), "GPUDOFPassOneInterface"); - - interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params"); - interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim"); - interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs"); - - GPU_shader_set_interface(shader, interface); - break; - } - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO: - { - GPUDOFPassTwoInterface *interface = MEM_mallocN(sizeof(GPUDOFPassTwoInterface), "GPUDOFPassTwoInterface"); - - interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params"); - interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim"); - interface->color_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs"); - - GPU_shader_set_interface(shader, interface); - break; - } - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE: - { - GPUDOFPassThreeInterface *interface = MEM_mallocN(sizeof(GPUDOFPassThreeInterface), "GPUDOFPassThreeInterface"); - - interface->near_coc_downsampled = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->near_coc_blurred = GPU_shader_get_uniform(shader, "blurredcolorbuffer"); - - GPU_shader_set_interface(shader, interface); - break; - } - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR: - { - GPUDOFPassFourInterface *interface = MEM_mallocN(sizeof(GPUDOFPassFourInterface), "GPUDOFPassFourInterface"); - - interface->near_coc_downsampled = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim"); - - GPU_shader_set_interface(shader, interface); - break; - } - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE: - { - GPUDOFPassFiveInterface *interface = MEM_mallocN(sizeof(GPUDOFPassFiveInterface), "GPUDOFPassFiveInterface"); - - interface->medium_blurred_uniform = GPU_shader_get_uniform(shader, "mblurredcolorbuffer"); - interface->high_blurred_uniform = GPU_shader_get_uniform(shader, "blurredcolorbuffer"); - interface->dof_uniform = GPU_shader_get_uniform(shader, "dof_params"); - interface->invrendertargetdim_uniform = GPU_shader_get_uniform(shader, "invrendertargetdim"); - interface->original_uniform = GPU_shader_get_uniform(shader, "colorbuffer"); - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - interface->viewvecs_uniform = GPU_shader_get_uniform(shader, "viewvecs"); - - GPU_shader_set_interface(shader, interface); - break; - } - - case GPU_SHADER_FX_DEPTH_RESOLVE: - { - GPUDepthResolveInterface *interface = MEM_mallocN(sizeof(GPUDepthResolveInterface), "GPUDepthResolveInterface"); - - interface->depth_uniform = GPU_shader_get_uniform(shader, "depthbuffer"); - - GPU_shader_set_interface(shader, interface); - break; - } - - default: - break; - } -} - diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.c index 859aab9565f..8aea87ef659 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.c @@ -20,7 +20,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): Brecht Van Lommel, Jason Wilkins. + * Contributor(s): Brecht Van Lommel, Jason Wilkins, Mike Erwin. * * ***** END GPL LICENSE BLOCK ***** */ @@ -44,122 +44,18 @@ #include <stdlib.h> #include <string.h> -#define CASE_CODE_RETURN_STR(code) case code: return #code; +#ifndef __APPLE__ /* only non-Apple systems implement OpenGL debug callbacks */ -static const char *gpu_gl_error_symbol(GLenum err) -{ - switch (err) { - CASE_CODE_RETURN_STR(GL_NO_ERROR) - CASE_CODE_RETURN_STR(GL_INVALID_ENUM) - CASE_CODE_RETURN_STR(GL_INVALID_VALUE) - CASE_CODE_RETURN_STR(GL_INVALID_OPERATION) - CASE_CODE_RETURN_STR(GL_STACK_OVERFLOW) - CASE_CODE_RETURN_STR(GL_STACK_UNDERFLOW) - CASE_CODE_RETURN_STR(GL_OUT_OF_MEMORY) - -#if GL_ARB_imaging - CASE_CODE_RETURN_STR(GL_TABLE_TOO_LARGE) -#endif - -#if defined(WITH_GLU) - CASE_CODE_RETURN_STR(GLU_INVALID_ENUM) - CASE_CODE_RETURN_STR(GLU_INVALID_VALUE) - CASE_CODE_RETURN_STR(GLU_OUT_OF_MEMORY) -#endif +/* control whether we use older AMD_debug_output extension + * some supported GPU + OS combos do not have the newer extensions */ +# define LEGACY_DEBUG 1 - default: - return "<unknown error>"; - } -} - -#undef CASE_CODE_RETURN_STR - - -static bool gpu_report_gl_errors(const char *file, int line, const char *str) -{ - GLenum gl_error = glGetError(); - - if (gl_error == GL_NO_ERROR) { - return true; - } - else { - /* glGetError should have cleared the error flag, so if we get the - * same flag twice that means glGetError itself probably triggered - * the error. This happens on Windows if the GL context is invalid. - */ - { - GLenum new_error = glGetError(); - if (gl_error == new_error) { - fprintf(stderr, "GL: Possible context invalidation issue\n"); - return false; - } - } - - fprintf(stderr, - "%s:%d: ``%s'' -> GL Error (0x%04X - %s): %s\n", - file, line, str, gl_error, - gpu_gl_error_symbol(gl_error), - gpuErrorString(gl_error)); - - return false; - } -} - - -const char *gpuErrorString(GLenum err) -{ - switch (err) { - case GL_NO_ERROR: - return "No Error"; - - case GL_INVALID_ENUM: - return "Invalid Enumeration"; - - case GL_INVALID_VALUE: - return "Invalid Value"; - - case GL_INVALID_OPERATION: - return "Invalid Operation"; - - case GL_STACK_OVERFLOW: - return "Stack Overflow"; - - case GL_STACK_UNDERFLOW: - return "Stack Underflow"; - - case GL_OUT_OF_MEMORY: - return "Out of Memory"; - -#if GL_ARB_imaging - case GL_TABLE_TOO_LARGE: - return "Table Too Large"; -#endif - -#if defined(WITH_GLU) - case GLU_INVALID_ENUM: - return "Invalid Enum (GLU)"; - - case GLU_INVALID_VALUE: - return "Invalid Value (GLU)"; - - case GLU_OUT_OF_MEMORY: - return "Out of Memory (GLU)"; -#endif - - default: - return "<unknown error>"; - } -} - - -/* Debug callbacks need the same calling convention as OpenGL functions. - */ -#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) - /* Win32 but not WinCE */ -# define APIENTRY __stdcall -#else -# define APIENTRY -#endif +/* Debug callbacks need the same calling convention as OpenGL functions. */ +# if defined(_WIN32) +# define APIENTRY __stdcall +# else +# define APIENTRY +# endif static const char *source_name(GLenum source) @@ -189,32 +85,11 @@ static const char *message_type_name(GLenum message) } } -static const char *category_name_amd(GLenum category) -{ - switch (category) { - case GL_DEBUG_CATEGORY_API_ERROR_AMD: return "API error"; - case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: return "window system"; - case GL_DEBUG_CATEGORY_DEPRECATION_AMD: return "deprecated behavior"; - case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: return "undefined behavior"; - case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: return "performance"; - case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: return "shader compiler"; - case GL_DEBUG_CATEGORY_APPLICATION_AMD: return "application"; - case GL_DEBUG_CATEGORY_OTHER_AMD: return "other"; - default: return "???"; - } -} - - static void APIENTRY gpu_debug_proc( GLenum source, GLenum type, GLuint UNUSED(id), GLenum severity, GLsizei UNUSED(length), const GLchar *message, const GLvoid *UNUSED(userParm)) { - if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) { - /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */ - return; - } - bool backtrace = false; switch (severity) { @@ -233,18 +108,28 @@ static void APIENTRY gpu_debug_proc( } } +# if LEGACY_DEBUG + +static const char *category_name_amd(GLenum category) +{ + switch (category) { + case GL_DEBUG_CATEGORY_API_ERROR_AMD: return "API error"; + case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: return "window system"; + case GL_DEBUG_CATEGORY_DEPRECATION_AMD: return "deprecated behavior"; + case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: return "undefined behavior"; + case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: return "performance"; + case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: return "shader compiler"; + case GL_DEBUG_CATEGORY_APPLICATION_AMD: return "application"; + case GL_DEBUG_CATEGORY_OTHER_AMD: return "other"; + default: return "???"; + } +} -#ifndef GLEW_ES_ONLY static void APIENTRY gpu_debug_proc_amd( GLuint UNUSED(id), GLenum category, GLenum severity, GLsizei UNUSED(length), const GLchar *message, GLvoid *UNUSED(userParm)) { - if (category == GL_DEBUG_CATEGORY_DEPRECATION_AMD) { - /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */ - return; - } - bool backtrace = false; switch (severity) { @@ -261,148 +146,88 @@ static void APIENTRY gpu_debug_proc_amd( fflush(stderr); } } -#endif +# endif /* LEGACY_DEBUG */ - -#undef APIENTRY +# undef APIENTRY +#endif /* not Apple */ void gpu_debug_init(void) { +#ifdef __APPLE__ + fprintf(stderr, "OpenGL debug callback is not available on Apple.\n"); +#else /* not Apple */ const char success[] = "Successfully hooked OpenGL debug callback."; -#if !defined(WITH_GLEW_ES) && !defined(GLEW_ES_ONLY) - if (GLEW_VERSION_4_3) { - fprintf(stderr, "Using OpenGL 4.3 debug facilities\n"); - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext()); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - return; - } -#endif - - if (GLEW_KHR_debug) { -#ifndef GLEW_ES_ONLY - fprintf(stderr, "Using KHR_debug extension\n"); + if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { + fprintf(stderr, "Using %s\n", GLEW_VERSION_4_3 ? "OpenGL 4.3 debug facilities" : "KHR_debug extension"); glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext()); + glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, NULL); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); GPU_string_marker(success); -#endif - return; } - -#ifndef GLEW_ES_ONLY - if (GLEW_ARB_debug_output) { + else if (GLEW_ARB_debug_output) { fprintf(stderr, "Using ARB_debug_output extension\n"); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, mxGetCurrentContext()); + glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, NULL); glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); GPU_string_marker(success); - - return; } - - if (GLEW_AMD_debug_output) { +# if LEGACY_DEBUG + else if (GLEW_AMD_debug_output) { fprintf(stderr, "Using AMD_debug_output extension\n"); - glDebugMessageCallbackAMD(gpu_debug_proc_amd, mxGetCurrentContext()); + glDebugMessageCallbackAMD(gpu_debug_proc_amd, NULL); glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); GPU_string_marker(success); - - return; } -#endif - - fprintf(stderr, "Failed to hook OpenGL debug callback.\n"); - - return; +# endif + else { + fprintf(stderr, "Failed to hook OpenGL debug callback.\n"); + } +#endif /* not Apple */ } void gpu_debug_exit(void) { -#ifndef WITH_GLEW_ES -#ifndef GLEW_ES_ONLY - if (GLEW_VERSION_4_3) { - glDebugMessageCallback(NULL, NULL); - - return; - } -#endif -#endif - - if (GLEW_KHR_debug) { -#ifndef GLEW_ES_ONLY +#ifndef __APPLE__ + if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { glDebugMessageCallback(NULL, NULL); -#endif - return; } - -#ifndef GLEW_ES_ONLY - if (GLEW_ARB_debug_output) { + else if (GLEW_ARB_debug_output) { glDebugMessageCallbackARB(NULL, NULL); - - return; } - - if (GLEW_AMD_debug_output) { +# if LEGACY_DEBUG + else if (GLEW_AMD_debug_output) { glDebugMessageCallbackAMD(NULL, NULL); - - return; } +# endif #endif - - return; } void GPU_string_marker(const char *buf) { -#ifndef WITH_GLEW_ES -#ifndef GLEW_ES_ONLY - if (GLEW_VERSION_4_3) { - glDebugMessageInsert( - GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, - GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf); - - return; - } -#endif -#endif - - if (GLEW_KHR_debug) { -#ifndef GLEW_ES_ONLY +#ifdef __APPLE__ + UNUSED_VARS(buf); +#else /* not Apple */ + if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf); -#endif - return; } - -#ifndef GLEW_ES_ONLY - if (GLEW_ARB_debug_output) { + else if (GLEW_ARB_debug_output) { glDebugMessageInsertARB( GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_OTHER_ARB, 0, GL_DEBUG_SEVERITY_LOW_ARB, -1, buf); - - return; } - - if (GLEW_AMD_debug_output) { +# if LEGACY_DEBUG + else if (GLEW_AMD_debug_output) { glDebugMessageInsertAMD( GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0, 0, buf); - - return; } - - if (GLEW_GREMEDY_string_marker) { - glStringMarkerGREMEDY(0, buf); - - return; - } -#endif +# endif +#endif /* not Apple */ } void GPU_print_error_debug(const char *str) @@ -410,406 +235,3 @@ void GPU_print_error_debug(const char *str) if (G.debug & G_DEBUG) fprintf(stderr, "GPU: %s\n", str); } - - -void GPU_assert_no_gl_errors(const char *file, int line, const char *str) -{ - if (G.debug) { - GLboolean gl_ok = gpu_report_gl_errors(file, line, str); - - BLI_assert(gl_ok); - (void) gl_ok; - } -} - - -static void gpu_state_print_fl_ex(const char *name, GLenum type) -{ -#define MAX_ARRAY_SIZE 64 - - const unsigned char err_mark[4] = {0xff, 0xff, 0xff, 0xff}; - - float value[MAX_ARRAY_SIZE]; - int a; - - memset(value, 0xff, sizeof(value)); - glGetFloatv(type, value); - - if (glGetError() == GL_NO_ERROR) { - printf("%s: ", name); - for (a = 0; a < MAX_ARRAY_SIZE; a++) { - if (memcmp(&value[a], err_mark, sizeof(value[a])) == 0) { - break; - } - printf("%.2f ", value[a]); - } - printf("\n"); - } - -#undef MAX_ARRAY_SIZE -} - -#define gpu_state_print_fl(val) gpu_state_print_fl_ex(#val, val) - -void GPU_state_print(void) -{ - GPU_ASSERT_NO_GL_ERRORS("GPU_state_print"); /* clear any errors */ - - gpu_state_print_fl(GL_ACCUM_ALPHA_BITS); - gpu_state_print_fl(GL_ACCUM_BLUE_BITS); - gpu_state_print_fl(GL_ACCUM_CLEAR_VALUE); - gpu_state_print_fl(GL_ACCUM_GREEN_BITS); - gpu_state_print_fl(GL_ACCUM_RED_BITS); - gpu_state_print_fl(GL_ACTIVE_TEXTURE); - gpu_state_print_fl(GL_ALIASED_LINE_WIDTH_RANGE); - gpu_state_print_fl(GL_ALIASED_POINT_SIZE_RANGE); - gpu_state_print_fl(GL_ALPHA_BIAS); - gpu_state_print_fl(GL_ALPHA_BITS); - gpu_state_print_fl(GL_ALPHA_SCALE); - gpu_state_print_fl(GL_ALPHA_TEST); - gpu_state_print_fl(GL_ALPHA_TEST_FUNC); - gpu_state_print_fl(GL_ALPHA_TEST_REF); - gpu_state_print_fl(GL_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_ATTRIB_STACK_DEPTH); - gpu_state_print_fl(GL_AUTO_NORMAL); - gpu_state_print_fl(GL_AUX_BUFFERS); - gpu_state_print_fl(GL_BLEND); - gpu_state_print_fl(GL_BLEND_COLOR); - gpu_state_print_fl(GL_BLEND_DST_ALPHA); - gpu_state_print_fl(GL_BLEND_DST_RGB); - gpu_state_print_fl(GL_BLEND_EQUATION_ALPHA); - gpu_state_print_fl(GL_BLEND_EQUATION_RGB); - gpu_state_print_fl(GL_BLEND_SRC_ALPHA); - gpu_state_print_fl(GL_BLEND_SRC_RGB); - gpu_state_print_fl(GL_BLUE_BIAS); - gpu_state_print_fl(GL_BLUE_BITS); - gpu_state_print_fl(GL_BLUE_SCALE); - gpu_state_print_fl(GL_CLIENT_ACTIVE_TEXTURE); - gpu_state_print_fl(GL_CLIENT_ATTRIB_STACK_DEPTH); - gpu_state_print_fl(GL_CLIP_PLANE0); - gpu_state_print_fl(GL_COLOR_ARRAY); - gpu_state_print_fl(GL_COLOR_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_COLOR_ARRAY_SIZE); - gpu_state_print_fl(GL_COLOR_ARRAY_STRIDE); - gpu_state_print_fl(GL_COLOR_ARRAY_TYPE); - gpu_state_print_fl(GL_COLOR_CLEAR_VALUE); - gpu_state_print_fl(GL_COLOR_LOGIC_OP); - gpu_state_print_fl(GL_COLOR_MATERIAL); - gpu_state_print_fl(GL_COLOR_MATERIAL_FACE); - gpu_state_print_fl(GL_COLOR_MATERIAL_PARAMETER); - gpu_state_print_fl(GL_COLOR_MATRIX); - gpu_state_print_fl(GL_COLOR_MATRIX_STACK_DEPTH); - gpu_state_print_fl(GL_COLOR_SUM); - gpu_state_print_fl(GL_COLOR_TABLE); - gpu_state_print_fl(GL_COLOR_WRITEMASK); - gpu_state_print_fl(GL_NUM_COMPRESSED_TEXTURE_FORMATS); - gpu_state_print_fl(GL_COMPRESSED_TEXTURE_FORMATS); - gpu_state_print_fl(GL_CONVOLUTION_1D); - gpu_state_print_fl(GL_CONVOLUTION_2D); - gpu_state_print_fl(GL_CULL_FACE); - gpu_state_print_fl(GL_CULL_FACE_MODE); - gpu_state_print_fl(GL_CURRENT_COLOR); - gpu_state_print_fl(GL_CURRENT_FOG_COORD); - gpu_state_print_fl(GL_CURRENT_INDEX); - gpu_state_print_fl(GL_CURRENT_NORMAL); - gpu_state_print_fl(GL_CURRENT_PROGRAM); - gpu_state_print_fl(GL_CURRENT_RASTER_COLOR); - gpu_state_print_fl(GL_CURRENT_RASTER_DISTANCE); - gpu_state_print_fl(GL_CURRENT_RASTER_INDEX); - gpu_state_print_fl(GL_CURRENT_RASTER_POSITION); - gpu_state_print_fl(GL_CURRENT_RASTER_POSITION_VALID); - gpu_state_print_fl(GL_CURRENT_RASTER_SECONDARY_COLOR); - gpu_state_print_fl(GL_CURRENT_RASTER_TEXTURE_COORDS); - gpu_state_print_fl(GL_CURRENT_SECONDARY_COLOR); - gpu_state_print_fl(GL_CURRENT_TEXTURE_COORDS); - gpu_state_print_fl(GL_DEPTH_BIAS); - gpu_state_print_fl(GL_DEPTH_BITS); - gpu_state_print_fl(GL_DEPTH_CLEAR_VALUE); - gpu_state_print_fl(GL_DEPTH_FUNC); - gpu_state_print_fl(GL_DEPTH_RANGE); - gpu_state_print_fl(GL_DEPTH_SCALE); - gpu_state_print_fl(GL_DEPTH_TEST); - gpu_state_print_fl(GL_DEPTH_WRITEMASK); - gpu_state_print_fl(GL_DITHER); - gpu_state_print_fl(GL_DOUBLEBUFFER); - gpu_state_print_fl(GL_DRAW_BUFFER); - gpu_state_print_fl(GL_DRAW_BUFFER0); - gpu_state_print_fl(GL_EDGE_FLAG); - gpu_state_print_fl(GL_EDGE_FLAG_ARRAY); - gpu_state_print_fl(GL_EDGE_FLAG_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_EDGE_FLAG_ARRAY_STRIDE); - gpu_state_print_fl(GL_ELEMENT_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_FEEDBACK_BUFFER_SIZE); - gpu_state_print_fl(GL_FEEDBACK_BUFFER_TYPE); - gpu_state_print_fl(GL_FOG); - gpu_state_print_fl(GL_FOG_COLOR); - gpu_state_print_fl(GL_FOG_COORD_ARRAY); - gpu_state_print_fl(GL_FOG_COORD_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_FOG_COORD_ARRAY_STRIDE); - gpu_state_print_fl(GL_FOG_COORD_ARRAY_TYPE); - gpu_state_print_fl(GL_FOG_COORD_SRC); - gpu_state_print_fl(GL_FOG_DENSITY); - gpu_state_print_fl(GL_FOG_END); - gpu_state_print_fl(GL_FOG_HINT); - gpu_state_print_fl(GL_FOG_INDEX); - gpu_state_print_fl(GL_FOG_MODE); - gpu_state_print_fl(GL_FOG_START); - gpu_state_print_fl(GL_FRAGMENT_PROGRAM_ARB); /* TODO: remove ARB program support */ - gpu_state_print_fl(GL_FRAGMENT_SHADER_DERIVATIVE_HINT); - gpu_state_print_fl(GL_FRONT_FACE); - gpu_state_print_fl(GL_GENERATE_MIPMAP_HINT); - gpu_state_print_fl(GL_GREEN_BIAS); - gpu_state_print_fl(GL_GREEN_BITS); - gpu_state_print_fl(GL_GREEN_SCALE); - gpu_state_print_fl(GL_HISTOGRAM); - gpu_state_print_fl(GL_INDEX_ARRAY); - gpu_state_print_fl(GL_INDEX_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_INDEX_ARRAY_STRIDE); - gpu_state_print_fl(GL_INDEX_ARRAY_TYPE); - gpu_state_print_fl(GL_INDEX_BITS); - gpu_state_print_fl(GL_INDEX_CLEAR_VALUE); - gpu_state_print_fl(GL_INDEX_LOGIC_OP); - gpu_state_print_fl(GL_INDEX_MODE); - gpu_state_print_fl(GL_INDEX_OFFSET); - gpu_state_print_fl(GL_INDEX_SHIFT); - gpu_state_print_fl(GL_INDEX_WRITEMASK); - gpu_state_print_fl(GL_LIGHT0); - gpu_state_print_fl(GL_LIGHT1); - gpu_state_print_fl(GL_LIGHT2); - gpu_state_print_fl(GL_LIGHT3); - gpu_state_print_fl(GL_LIGHT4); - gpu_state_print_fl(GL_LIGHT5); - gpu_state_print_fl(GL_LIGHT6); - gpu_state_print_fl(GL_LIGHT7); - gpu_state_print_fl(GL_LIGHTING); - gpu_state_print_fl(GL_LIGHT_MODEL_AMBIENT); - gpu_state_print_fl(GL_LIGHT_MODEL_COLOR_CONTROL); - gpu_state_print_fl(GL_LIGHT_MODEL_LOCAL_VIEWER); - gpu_state_print_fl(GL_LIGHT_MODEL_TWO_SIDE); - gpu_state_print_fl(GL_LINE_SMOOTH); - gpu_state_print_fl(GL_LINE_SMOOTH_HINT); - gpu_state_print_fl(GL_LINE_STIPPLE); - gpu_state_print_fl(GL_LINE_STIPPLE_PATTERN); - gpu_state_print_fl(GL_LINE_STIPPLE_REPEAT); - gpu_state_print_fl(GL_LINE_WIDTH); - gpu_state_print_fl(GL_LINE_WIDTH_GRANULARITY); - gpu_state_print_fl(GL_LINE_WIDTH_RANGE); - gpu_state_print_fl(GL_LIST_BASE); - gpu_state_print_fl(GL_LIST_INDEX); - gpu_state_print_fl(GL_LIST_MODE); - gpu_state_print_fl(GL_LOGIC_OP); - gpu_state_print_fl(GL_LOGIC_OP_MODE); - gpu_state_print_fl(GL_MAP1_COLOR_4); - gpu_state_print_fl(GL_MAP1_GRID_DOMAIN); - gpu_state_print_fl(GL_MAP1_GRID_SEGMENTS); - gpu_state_print_fl(GL_MAP1_INDEX); - gpu_state_print_fl(GL_MAP1_NORMAL); - gpu_state_print_fl(GL_MAP1_TEXTURE_COORD_1); - gpu_state_print_fl(GL_MAP1_TEXTURE_COORD_2); - gpu_state_print_fl(GL_MAP1_TEXTURE_COORD_3); - gpu_state_print_fl(GL_MAP1_TEXTURE_COORD_4); - gpu_state_print_fl(GL_MAP1_VERTEX_3); - gpu_state_print_fl(GL_MAP1_VERTEX_4); - gpu_state_print_fl(GL_MAP2_COLOR_4); - gpu_state_print_fl(GL_MAP2_GRID_DOMAIN); - gpu_state_print_fl(GL_MAP2_GRID_SEGMENTS); - gpu_state_print_fl(GL_MAP2_INDEX); - gpu_state_print_fl(GL_MAP2_NORMAL); - gpu_state_print_fl(GL_MAP2_TEXTURE_COORD_1); - gpu_state_print_fl(GL_MAP2_TEXTURE_COORD_2); - gpu_state_print_fl(GL_MAP2_TEXTURE_COORD_3); - gpu_state_print_fl(GL_MAP2_TEXTURE_COORD_4); - gpu_state_print_fl(GL_MAP2_VERTEX_3); - gpu_state_print_fl(GL_MAP2_VERTEX_4); - gpu_state_print_fl(GL_MAP_COLOR); - gpu_state_print_fl(GL_MAP_STENCIL); - gpu_state_print_fl(GL_MATRIX_MODE); - gpu_state_print_fl(GL_MAX_3D_TEXTURE_SIZE); - gpu_state_print_fl(GL_MAX_ATTRIB_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_CLIENT_ATTRIB_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_CLIP_PLANES); - gpu_state_print_fl(GL_MAX_COLOR_MATRIX_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); - gpu_state_print_fl(GL_MAX_CUBE_MAP_TEXTURE_SIZE); - gpu_state_print_fl(GL_MAX_DRAW_BUFFERS); - gpu_state_print_fl(GL_MAX_ELEMENTS_INDICES); - gpu_state_print_fl(GL_MAX_ELEMENTS_VERTICES); - gpu_state_print_fl(GL_MAX_EVAL_ORDER); - gpu_state_print_fl(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS); - gpu_state_print_fl(GL_MAX_LIGHTS); - gpu_state_print_fl(GL_MAX_LIST_NESTING); - gpu_state_print_fl(GL_MAX_MODELVIEW_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_NAME_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_PIXEL_MAP_TABLE); - gpu_state_print_fl(GL_MAX_PROJECTION_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_TEXTURE_COORDS); - gpu_state_print_fl(GL_MAX_TEXTURE_IMAGE_UNITS); - gpu_state_print_fl(GL_MAX_TEXTURE_LOD_BIAS); - gpu_state_print_fl(GL_MAX_TEXTURE_SIZE); - gpu_state_print_fl(GL_MAX_TEXTURE_STACK_DEPTH); - gpu_state_print_fl(GL_MAX_TEXTURE_UNITS); - gpu_state_print_fl(GL_MAX_VARYING_FLOATS); - gpu_state_print_fl(GL_MAX_VERTEX_ATTRIBS); - gpu_state_print_fl(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS); - gpu_state_print_fl(GL_MAX_VERTEX_UNIFORM_COMPONENTS); - gpu_state_print_fl(GL_MAX_VIEWPORT_DIMS); - gpu_state_print_fl(GL_MINMAX); - gpu_state_print_fl(GL_MODELVIEW_MATRIX); - gpu_state_print_fl(GL_MODELVIEW_STACK_DEPTH); - gpu_state_print_fl(GL_MULTISAMPLE); - gpu_state_print_fl(GL_NAME_STACK_DEPTH); - gpu_state_print_fl(GL_NORMALIZE); - gpu_state_print_fl(GL_NORMAL_ARRAY); - gpu_state_print_fl(GL_NORMAL_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_NORMAL_ARRAY_STRIDE); - gpu_state_print_fl(GL_NORMAL_ARRAY_TYPE); - gpu_state_print_fl(GL_NUM_COMPRESSED_TEXTURE_FORMATS); - gpu_state_print_fl(GL_PACK_ALIGNMENT); - gpu_state_print_fl(GL_PACK_IMAGE_HEIGHT); - gpu_state_print_fl(GL_PACK_LSB_FIRST); - gpu_state_print_fl(GL_PACK_ROW_LENGTH); - gpu_state_print_fl(GL_PACK_SKIP_IMAGES); - gpu_state_print_fl(GL_PACK_SKIP_PIXELS); - gpu_state_print_fl(GL_PACK_SKIP_ROWS); - gpu_state_print_fl(GL_PACK_SWAP_BYTES); - gpu_state_print_fl(GL_PERSPECTIVE_CORRECTION_HINT); - gpu_state_print_fl(GL_PIXEL_MAP_A_TO_A_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_B_TO_B_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_G_TO_G_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_I_TO_A_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_I_TO_B_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_I_TO_G_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_I_TO_I_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_I_TO_R_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_R_TO_R_SIZE); - gpu_state_print_fl(GL_PIXEL_MAP_S_TO_S_SIZE); - gpu_state_print_fl(GL_PIXEL_PACK_BUFFER_BINDING); - gpu_state_print_fl(GL_PIXEL_UNPACK_BUFFER_BINDING); - gpu_state_print_fl(GL_POINT_DISTANCE_ATTENUATION); - gpu_state_print_fl(GL_POINT_FADE_THRESHOLD_SIZE); - gpu_state_print_fl(GL_POINT_SIZE); - gpu_state_print_fl(GL_POINT_SIZE_GRANULARITY); - gpu_state_print_fl(GL_POINT_SIZE_MAX); - gpu_state_print_fl(GL_POINT_SIZE_MIN); - gpu_state_print_fl(GL_POINT_SIZE_RANGE); - gpu_state_print_fl(GL_POINT_SMOOTH); - gpu_state_print_fl(GL_POINT_SMOOTH_HINT); - gpu_state_print_fl(GL_POINT_SPRITE); - gpu_state_print_fl(GL_POLYGON_MODE); - gpu_state_print_fl(GL_POLYGON_OFFSET_FACTOR); - gpu_state_print_fl(GL_POLYGON_OFFSET_FILL); - gpu_state_print_fl(GL_POLYGON_OFFSET_LINE); - gpu_state_print_fl(GL_POLYGON_OFFSET_POINT); - gpu_state_print_fl(GL_POLYGON_OFFSET_UNITS); - gpu_state_print_fl(GL_POLYGON_SMOOTH); - gpu_state_print_fl(GL_POLYGON_SMOOTH_HINT); - gpu_state_print_fl(GL_POLYGON_STIPPLE); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_ALPHA_BIAS); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_ALPHA_SCALE); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_BLUE_BIAS); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_BLUE_SCALE); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_COLOR_TABLE); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_GREEN_BIAS); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_GREEN_SCALE); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_RED_BIAS); - gpu_state_print_fl(GL_POST_COLOR_MATRIX_RED_SCALE); - gpu_state_print_fl(GL_POST_CONVOLUTION_ALPHA_BIAS); - gpu_state_print_fl(GL_POST_CONVOLUTION_ALPHA_SCALE); - gpu_state_print_fl(GL_POST_CONVOLUTION_BLUE_BIAS); - gpu_state_print_fl(GL_POST_CONVOLUTION_BLUE_SCALE); - gpu_state_print_fl(GL_POST_CONVOLUTION_COLOR_TABLE); - gpu_state_print_fl(GL_POST_CONVOLUTION_GREEN_BIAS); - gpu_state_print_fl(GL_POST_CONVOLUTION_GREEN_SCALE); - gpu_state_print_fl(GL_POST_CONVOLUTION_RED_BIAS); - gpu_state_print_fl(GL_POST_CONVOLUTION_RED_SCALE); - gpu_state_print_fl(GL_PROJECTION_MATRIX); - gpu_state_print_fl(GL_PROJECTION_STACK_DEPTH); - gpu_state_print_fl(GL_READ_BUFFER); - gpu_state_print_fl(GL_RED_BIAS); - gpu_state_print_fl(GL_RED_BITS); - gpu_state_print_fl(GL_RED_SCALE); - gpu_state_print_fl(GL_RENDER_MODE); - gpu_state_print_fl(GL_RESCALE_NORMAL); - gpu_state_print_fl(GL_RGBA_MODE); - gpu_state_print_fl(GL_SAMPLES); - gpu_state_print_fl(GL_SAMPLE_BUFFERS); - gpu_state_print_fl(GL_SAMPLE_COVERAGE_INVERT); - gpu_state_print_fl(GL_SAMPLE_COVERAGE_VALUE); - gpu_state_print_fl(GL_SCISSOR_BOX); - gpu_state_print_fl(GL_SCISSOR_TEST); - gpu_state_print_fl(GL_SECONDARY_COLOR_ARRAY); - gpu_state_print_fl(GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_SECONDARY_COLOR_ARRAY_SIZE); - gpu_state_print_fl(GL_SECONDARY_COLOR_ARRAY_STRIDE); - gpu_state_print_fl(GL_SECONDARY_COLOR_ARRAY_TYPE); - gpu_state_print_fl(GL_SELECTION_BUFFER_SIZE); - gpu_state_print_fl(GL_SEPARABLE_2D); - gpu_state_print_fl(GL_SHADE_MODEL); - gpu_state_print_fl(GL_SMOOTH_LINE_WIDTH_GRANULARITY); - gpu_state_print_fl(GL_SMOOTH_LINE_WIDTH_RANGE); - gpu_state_print_fl(GL_SMOOTH_POINT_SIZE_GRANULARITY); - gpu_state_print_fl(GL_SMOOTH_POINT_SIZE_RANGE); - gpu_state_print_fl(GL_STENCIL_BACK_FAIL); - gpu_state_print_fl(GL_STENCIL_BACK_FUNC); - gpu_state_print_fl(GL_STENCIL_BACK_PASS_DEPTH_FAIL); - gpu_state_print_fl(GL_STENCIL_BACK_PASS_DEPTH_PASS); - gpu_state_print_fl(GL_STENCIL_BACK_REF); - gpu_state_print_fl(GL_STENCIL_BACK_VALUE_MASK); - gpu_state_print_fl(GL_STENCIL_BACK_WRITEMASK); - gpu_state_print_fl(GL_STENCIL_BITS); - gpu_state_print_fl(GL_STENCIL_CLEAR_VALUE); - gpu_state_print_fl(GL_STENCIL_FAIL); - gpu_state_print_fl(GL_STENCIL_FUNC); - gpu_state_print_fl(GL_STENCIL_PASS_DEPTH_FAIL); - gpu_state_print_fl(GL_STENCIL_PASS_DEPTH_PASS); - gpu_state_print_fl(GL_STENCIL_REF); - gpu_state_print_fl(GL_STENCIL_TEST); - gpu_state_print_fl(GL_STENCIL_VALUE_MASK); - gpu_state_print_fl(GL_STENCIL_WRITEMASK); - gpu_state_print_fl(GL_STEREO); - gpu_state_print_fl(GL_SUBPIXEL_BITS); - gpu_state_print_fl(GL_TEXTURE_1D); - gpu_state_print_fl(GL_TEXTURE_2D); - gpu_state_print_fl(GL_TEXTURE_3D); - gpu_state_print_fl(GL_TEXTURE_BINDING_1D); - gpu_state_print_fl(GL_TEXTURE_BINDING_2D); - gpu_state_print_fl(GL_TEXTURE_BINDING_3D); - gpu_state_print_fl(GL_TEXTURE_BINDING_CUBE_MAP); - gpu_state_print_fl(GL_TEXTURE_COMPRESSION_HINT); - gpu_state_print_fl(GL_TEXTURE_COORD_ARRAY); - gpu_state_print_fl(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_TEXTURE_COORD_ARRAY_SIZE); - gpu_state_print_fl(GL_TEXTURE_COORD_ARRAY_STRIDE); - gpu_state_print_fl(GL_TEXTURE_COORD_ARRAY_TYPE); - gpu_state_print_fl(GL_TEXTURE_CUBE_MAP); - gpu_state_print_fl(GL_TEXTURE_GEN_Q); - gpu_state_print_fl(GL_TEXTURE_GEN_R); - gpu_state_print_fl(GL_TEXTURE_GEN_S); - gpu_state_print_fl(GL_TEXTURE_GEN_T); - gpu_state_print_fl(GL_TEXTURE_MATRIX); - gpu_state_print_fl(GL_TEXTURE_STACK_DEPTH); - gpu_state_print_fl(GL_TRANSPOSE_COLOR_MATRIX); - gpu_state_print_fl(GL_TRANSPOSE_MODELVIEW_MATRIX); - gpu_state_print_fl(GL_TRANSPOSE_PROJECTION_MATRIX); - gpu_state_print_fl(GL_TRANSPOSE_TEXTURE_MATRIX); - gpu_state_print_fl(GL_UNPACK_ALIGNMENT); - gpu_state_print_fl(GL_UNPACK_IMAGE_HEIGHT); - gpu_state_print_fl(GL_UNPACK_LSB_FIRST); - gpu_state_print_fl(GL_UNPACK_ROW_LENGTH); - gpu_state_print_fl(GL_UNPACK_SKIP_IMAGES); - gpu_state_print_fl(GL_UNPACK_SKIP_PIXELS); - gpu_state_print_fl(GL_UNPACK_SKIP_ROWS); - gpu_state_print_fl(GL_UNPACK_SWAP_BYTES); - gpu_state_print_fl(GL_VERTEX_ARRAY); - gpu_state_print_fl(GL_VERTEX_ARRAY_BUFFER_BINDING); - gpu_state_print_fl(GL_VERTEX_ARRAY_SIZE); - gpu_state_print_fl(GL_VERTEX_ARRAY_STRIDE); - gpu_state_print_fl(GL_VERTEX_ARRAY_TYPE); - gpu_state_print_fl(GL_VERTEX_PROGRAM_POINT_SIZE); - gpu_state_print_fl(GL_VERTEX_PROGRAM_TWO_SIDE); - gpu_state_print_fl(GL_VIEWPORT); - gpu_state_print_fl(GL_ZOOM_X); - gpu_state_print_fl(GL_ZOOM_Y); -} - -#undef gpu_state_print_fl diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 947cd43d27b..7bfebb702a1 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -31,15 +31,13 @@ * Utility functions for dealing with OpenGL texture & material context, * mipmap generation and light objects. * - * These are some obscure rendering functions shared between the - * game engine and the blender, in this module to avoid duplication + * These are some obscure rendering functions shared between the game engine (not anymore) + * and the blender, in this module to avoid duplication * and abstract them away from the rest a bit. */ #include <string.h> -#include "GPU_glew.h" - #include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_linklist.h" @@ -72,15 +70,12 @@ #include "BKE_node.h" #include "BKE_scene.h" #include "BKE_DerivedMesh.h" -#ifdef WITH_GAMEENGINE -# include "BKE_object.h" -#endif #include "GPU_basic_shader.h" -#include "GPU_buffers.h" #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_material.h" +#include "GPU_matrix.h" #include "GPU_shader.h" #include "GPU_texture.h" @@ -99,141 +94,20 @@ extern Material defmaterial; /* from material.c */ -/* Text Rendering */ - -static void gpu_mcol(unsigned int ucol) -{ - /* mcol order is swapped */ - const char *cp = (char *)&ucol; - glColor3ub(cp[3], cp[2], cp[1]); -} - -void GPU_render_text( - MTexPoly *mtexpoly, int mode, - const char *textstr, int textlen, unsigned int *col, - const float *v_quad[4], const float *uv_quad[4], - int glattrib) -{ - if ((mode & GEMAT_TEXT) && (textlen > 0) && mtexpoly->tpage) { - const float *v1 = v_quad[0]; - const float *v2 = v_quad[1]; - const float *v3 = v_quad[2]; - const float *v4 = v_quad[3]; - Image *ima = (Image *)mtexpoly->tpage; - const size_t textlen_st = textlen; - float centerx, centery, sizex, sizey, transx, transy, movex, movey, advance; - - /* multiline */ - float line_start = 0.0f, line_height; - - if (v4) - line_height = max_ffff(v1[1], v2[1], v3[1], v4[2]) - min_ffff(v1[1], v2[1], v3[1], v4[2]); - else - line_height = max_fff(v1[1], v2[1], v3[1]) - min_fff(v1[1], v2[1], v3[1]); - line_height *= 1.2f; /* could be an option? */ - /* end multiline */ - - - /* color has been set */ - if (mtexpoly->mode & TF_OBCOL) - col = NULL; - else if (!col) - glColor3f(1.0f, 1.0f, 1.0f); - - glPushMatrix(); - - /* get the tab width */ - ImBuf *first_ibuf = BKE_image_get_first_ibuf(ima); - matrixGlyph(first_ibuf, ' ', ¢erx, ¢ery, - &sizex, &sizey, &transx, &transy, &movex, &movey, &advance); - - float advance_tab = advance * 4; /* tab width could also be an option */ - - - for (size_t index = 0; index < textlen_st; ) { - unsigned int character; - float uv[4][2]; - - /* lets calculate offset stuff */ - character = BLI_str_utf8_as_unicode_and_size_safe(textstr + index, &index); - - if (character == '\n') { - glTranslatef(line_start, -line_height, 0.0f); - line_start = 0.0f; - continue; - } - else if (character == '\t') { - glTranslatef(advance_tab, 0.0f, 0.0f); - line_start -= advance_tab; /* so we can go back to the start of the line */ - continue; - - } - else if (character > USHRT_MAX) { - /* not much we can do here bmfonts take ushort */ - character = '?'; - } - - /* space starts at offset 1 */ - /* character = character - ' ' + 1; */ - matrixGlyph(first_ibuf, character, & centerx, ¢ery, - &sizex, &sizey, &transx, &transy, &movex, &movey, &advance); - - uv[0][0] = (uv_quad[0][0] - centerx) * sizex + transx; - uv[0][1] = (uv_quad[0][1] - centery) * sizey + transy; - uv[1][0] = (uv_quad[1][0] - centerx) * sizex + transx; - uv[1][1] = (uv_quad[1][1] - centery) * sizey + transy; - uv[2][0] = (uv_quad[2][0] - centerx) * sizex + transx; - uv[2][1] = (uv_quad[2][1] - centery) * sizey + transy; - - glBegin(GL_POLYGON); - if (glattrib >= 0) glVertexAttrib2fv(glattrib, uv[0]); - else glTexCoord2fv(uv[0]); - if (col) gpu_mcol(col[0]); - glVertex3f(sizex * v1[0] + movex, sizey * v1[1] + movey, v1[2]); - - if (glattrib >= 0) glVertexAttrib2fv(glattrib, uv[1]); - else glTexCoord2fv(uv[1]); - if (col) gpu_mcol(col[1]); - glVertex3f(sizex * v2[0] + movex, sizey * v2[1] + movey, v2[2]); - - if (glattrib >= 0) glVertexAttrib2fv(glattrib, uv[2]); - else glTexCoord2fv(uv[2]); - if (col) gpu_mcol(col[2]); - glVertex3f(sizex * v3[0] + movex, sizey * v3[1] + movey, v3[2]); - - if (v4) { - uv[3][0] = (uv_quad[3][0] - centerx) * sizex + transx; - uv[3][1] = (uv_quad[3][1] - centery) * sizey + transy; - - if (glattrib >= 0) glVertexAttrib2fv(glattrib, uv[3]); - else glTexCoord2fv(uv[3]); - if (col) gpu_mcol(col[3]); - glVertex3f(sizex * v4[0] + movex, sizey * v4[1] + movey, v4[2]); - } - glEnd(); - - glTranslatef(advance, 0.0f, 0.0f); - line_start -= advance; /* so we can go back to the start of the line */ - } - glPopMatrix(); - - BKE_image_release_ibuf(ima, first_ibuf, NULL); - } -} - -/* Checking powers of two for images since OpenGL ES requires it */ - +//* Checking powers of two for images since OpenGL ES requires it */ +#ifdef WITH_DDS static bool is_power_of_2_resolution(int w, int h) { return is_power_of_2_i(w) && is_power_of_2_i(h); } +#endif static bool is_over_resolution_limit(GLenum textarget, int w, int h) { int size = (textarget == GL_TEXTURE_2D) ? GPU_max_texture_size() : GPU_max_cube_map_size(); int reslimit = (U.glreslimit != 0) ? - min_ii(U.glreslimit, size) : size; + min_ii(U.glreslimit, size) : size; return (w > reslimit || h > reslimit); } @@ -253,10 +127,6 @@ static int smaller_power_of_2_limit(int num) /* Current OpenGL state caching for GPU_set_tpage */ static struct GPUTextureState { - int curtile, tile; - int curtilemode, tilemode; - int curtileXRep, tileXRep; - int curtileYRep, tileYRep; Image *ima, *curima; /* also controls min/mag filtering */ @@ -269,8 +139,7 @@ static struct GPUTextureState { int alphablend; float anisotropic; int gpu_mipmap; - MTexPoly *lasttface; -} GTS = {0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, 1, 0, 0, -1, 1.0f, 0, NULL}; +} GTS = {NULL, NULL, 1, 0, 0, -1, 1.0f, 0}; /* Mipmap settings */ @@ -279,36 +148,13 @@ void GPU_set_gpu_mipmapping(int gpu_mipmap) int old_value = GTS.gpu_mipmap; /* only actually enable if it's supported */ - GTS.gpu_mipmap = gpu_mipmap && GLEW_EXT_framebuffer_object; + GTS.gpu_mipmap = gpu_mipmap; if (old_value != GTS.gpu_mipmap) { GPU_free_images(); } } -static void gpu_generate_mipmap(GLenum target) -{ - const bool is_ati = GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY); - int target_enabled = 0; - - /* work around bug in ATI driver, need to have GL_TEXTURE_2D enabled - * http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation */ - if (is_ati) { - target_enabled = glIsEnabled(target); - if (!target_enabled) - glEnable(target); - } - - /* TODO: simplify when we transition to GL >= 3 */ - if (GLEW_VERSION_3_0 || GLEW_ARB_framebuffer_object) - glGenerateMipmap(target); - else if (GLEW_EXT_framebuffer_object) - glGenerateMipmapEXT(target); - - if (is_ati && !target_enabled) - glDisable(target); -} - void GPU_set_mipmap(bool mipmap) { if (GTS.domipmap != mipmap) { @@ -381,28 +227,6 @@ float GPU_get_anisotropic(void) /* Set OpenGL state for an MTFace */ -static void gpu_make_repbind(Image *ima) -{ - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - if (ibuf == NULL) - return; - - if (ima->repbind) { - glDeleteTextures(ima->totbind, (GLuint *)ima->repbind); - MEM_freeN(ima->repbind); - ima->repbind = NULL; - ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; - } - - ima->totbind = ima->xrep * ima->yrep; - - if (ima->totbind > 1) { - ima->repbind = MEM_callocN(sizeof(int) * ima->totbind, "repbind"); - } - - BKE_image_release_ibuf(ima, ibuf, NULL); -} - static unsigned int *gpu_get_image_bindcode(Image *ima, GLenum textarget) { unsigned int *bind = 0; @@ -415,104 +239,6 @@ static unsigned int *gpu_get_image_bindcode(Image *ima, GLenum textarget) return bind; } -void GPU_clear_tpage(bool force) -{ - if (GTS.lasttface == NULL && !force) - return; - - GTS.lasttface = NULL; - GTS.curtile = 0; - GTS.curima = NULL; - if (GTS.curtilemode != 0) { - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - } - GTS.curtilemode = 0; - GTS.curtileXRep = 0; - GTS.curtileYRep = 0; - GTS.alphablend = -1; - - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - glDisable(GL_ALPHA_TEST); -} - -static void gpu_set_alpha_blend(GPUBlendMode alphablend) -{ - if (alphablend == GPU_BLEND_SOLID) { - glDisable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else if (alphablend == GPU_BLEND_ADD) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); - glDisable(GL_ALPHA_TEST); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - else if (ELEM(alphablend, GPU_BLEND_ALPHA, GPU_BLEND_ALPHA_SORT)) { - glEnable(GL_BLEND); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - - /* for OpenGL render we use the alpha channel, this makes alpha blend correct */ - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - /* if U.glalphaclip == 1.0, some cards go bonkers... - * turn off alpha test in this case */ - - /* added after 2.45 to clip alpha */ - if (U.glalphaclip == 1.0f) { - glDisable(GL_ALPHA_TEST); - } - else { - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, U.glalphaclip); - } - } - else if (alphablend == GPU_BLEND_CLIP) { - glDisable(GL_BLEND); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); - } - else if (alphablend == GPU_BLEND_ALPHA_TO_COVERAGE) { - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, U.glalphaclip); - glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } -} - -static void gpu_verify_alpha_blend(int alphablend) -{ - /* verify alpha blending modes */ - if (GTS.alphablend == alphablend) - return; - - gpu_set_alpha_blend(alphablend); - GTS.alphablend = alphablend; -} - -static void gpu_verify_reflection(Image *ima) -{ - if (ima && (ima->flag & IMA_REFLECT)) { - /* enable reflection mapping */ - glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); - glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); - - glEnable(GL_TEXTURE_GEN_S); - glEnable(GL_TEXTURE_GEN_T); - } - else { - /* disable reflection mapping */ - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - } -} - typedef struct VerifyThreadData { ImBuf *ibuf; float *srgb_frect; @@ -534,8 +260,6 @@ static void gpu_verify_high_bit_srgb_buffer_slice(float *srgb_frect, ibuf->x, height, ibuf->x, ibuf->x); IMB_buffer_float_unpremultiply(current_srgb_frect, ibuf->x, height); - /* Clamp buffer colors to 1.0 to avoid artifacts due to glu for hdr images. */ - IMB_buffer_float_clamp(current_srgb_frect, ibuf->x, height); } static void verify_thread_do(void *data_v, @@ -567,7 +291,7 @@ static void gpu_verify_high_bit_srgb_buffer(float *srgb_frect, int GPU_verify_image( Image *ima, ImageUser *iuser, - int textarget, int tftile, bool compare, bool mipmap, bool is_data) + int textarget, bool compare, bool mipmap, bool is_data) { unsigned int *bind = NULL; int tpx = 0, tpy = 0; @@ -577,46 +301,12 @@ int GPU_verify_image( /* flag to determine whether deep format is used */ bool use_high_bit_depth = false, do_color_management = false; - /* initialize tile mode and number of repeats */ GTS.ima = ima; - GTS.tilemode = (ima && (ima->tpageflag & (IMA_TILES | IMA_TWINANIM))); - GTS.tileXRep = 0; - GTS.tileYRep = 0; - - /* setting current tile according to frame */ - if (ima && (ima->tpageflag & IMA_TWINANIM)) - GTS.tile = ima->lastframe; - else - GTS.tile = tftile; - GTS.tile = MAX2(0, GTS.tile); - - if (ima) { - GTS.tileXRep = ima->xrep; - GTS.tileYRep = ima->yrep; - } - - /* if same image & tile, we're done */ - if (compare && ima == GTS.curima && GTS.curtile == GTS.tile && - GTS.tilemode == GTS.curtilemode && GTS.curtileXRep == GTS.tileXRep && - GTS.curtileYRep == GTS.tileYRep) - { + if (compare && ima == GTS.curima) { return (ima != NULL); } - /* if tiling mode or repeat changed, change texture matrix to fit */ - if (GTS.tilemode != GTS.curtilemode || GTS.curtileXRep != GTS.tileXRep || - GTS.curtileYRep != GTS.tileYRep) - { - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - - if (ima && (ima->tpageflag & IMA_TILES)) - glScalef(ima->xrep, ima->yrep, 1.0f); - - glMatrixMode(GL_MODELVIEW); - } - /* check if we have a valid image */ if (ima == NULL || ima->ok == 0) return 0; @@ -653,47 +343,7 @@ int GPU_verify_image( ima->tpageflag &= ~IMA_TPAGE_REFRESH; } - if (GTS.tilemode) { - /* tiled mode */ - if (ima->repbind == NULL) gpu_make_repbind(ima); - if (GTS.tile >= ima->totbind) GTS.tile = 0; - - /* this happens when you change repeat buttons */ - if (ima->repbind && textarget == GL_TEXTURE_2D) bind = &ima->repbind[GTS.tile]; - else bind = gpu_get_image_bindcode(ima, textarget); - - if (*bind == 0) { - short texwindx = ibuf->x / ima->xrep; - short texwindy = ibuf->y / ima->yrep; - - if (GTS.tile >= ima->xrep * ima->yrep) - GTS.tile = ima->xrep * ima->yrep - 1; - - short texwinsy = GTS.tile / ima->xrep; - short texwinsx = GTS.tile - texwinsy * ima->xrep; - - texwinsx *= texwindx; - texwinsy *= texwindy; - - tpx = texwindx; - tpy = texwindy; - - if (use_high_bit_depth) { - if (do_color_management) { - srgb_frect = MEM_mallocN(ibuf->x * ibuf->y * sizeof(float) * 4, "floar_buf_col_cor"); - gpu_verify_high_bit_srgb_buffer(srgb_frect, ibuf); - frect = srgb_frect + (4 * (texwinsy * ibuf->x + texwinsx)); - } - else { - frect = ibuf->rect_float + (ibuf->channels * (texwinsy * ibuf->x + texwinsx)); - } - } - else { - rect = ibuf->rect + texwinsy * ibuf->x + texwinsx; - } - } - } - else { + { /* regular image mode */ bind = gpu_get_image_bindcode(ima, textarget); @@ -722,37 +372,6 @@ int GPU_verify_image( const int rectw = tpx; const int recth = tpy; - unsigned *tilerect = NULL; - float *ftilerect = NULL; - - /* for tiles, copy only part of image into buffer */ - if (GTS.tilemode) { - if (use_high_bit_depth) { - ftilerect = MEM_mallocN(rectw * recth * sizeof(*ftilerect), "tilerect"); - - for (int y = 0; y < recth; y++) { - const float *frectrow = &frect[y * ibuf->x]; - float *ftilerectrow = &ftilerect[y * rectw]; - - memcpy(ftilerectrow, frectrow, tpx * sizeof(*frectrow)); - } - - frect = ftilerect; - } - else { - tilerect = MEM_mallocN(rectw * recth * sizeof(*tilerect), "tilerect"); - - for (int y = 0; y < recth; y++) { - const unsigned *rectrow = &rect[y * ibuf->x]; - unsigned *tilerectrow = &tilerect[y * rectw]; - - memcpy(tilerectrow, rectrow, tpx * sizeof(*rectrow)); - } - - rect = tilerect; - } - } - #ifdef WITH_DDS if (ibuf->ftype == IMB_FTYPE_DDS) GPU_create_gl_tex_compressed(bind, rect, rectw, recth, textarget, mipmap, ima, ibuf); @@ -769,10 +388,6 @@ int GPU_verify_image( } /* clean up */ - if (tilerect) - MEM_freeN(tilerect); - if (ftilerect) - MEM_freeN(ftilerect); if (srgb_frect) MEM_freeN(srgb_frect); @@ -858,30 +473,6 @@ void GPU_create_gl_tex( int tpx = rectw; int tpy = recth; - /* scale if not a power of two. this is not strictly necessary for newer - * GPUs (OpenGL version >= 2.0) since they support non-power-of-two-textures - * Then don't bother scaling for hardware that supports NPOT textures! */ - if (textarget == GL_TEXTURE_2D && - ((!GPU_full_non_power_of_two_support() && !is_power_of_2_resolution(rectw, recth)) || - is_over_resolution_limit(textarget, rectw, recth))) - { - rectw = smaller_power_of_2_limit(rectw); - recth = smaller_power_of_2_limit(recth); - - if (use_high_bit_depth) { - ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy); - IMB_scaleImBuf(ibuf, rectw, recth); - - frect = ibuf->rect_float; - } - else { - ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy); - IMB_scaleImBuf(ibuf, rectw, recth); - - rect = ibuf->rect; - } - } - /* create image */ glGenTextures(1, (GLuint *)bind); glBindTexture(textarget, *bind); @@ -889,7 +480,7 @@ void GPU_create_gl_tex( if (textarget == GL_TEXTURE_2D) { if (use_high_bit_depth) { if (GLEW_ARB_texture_float) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); } @@ -900,7 +491,7 @@ void GPU_create_gl_tex( if (GPU_get_mipmap() && mipmap) { if (GTS.gpu_mipmap) { - gpu_generate_mipmap(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); } else { int i; @@ -918,7 +509,7 @@ void GPU_create_gl_tex( ImBuf *mip = ibuf->mipmap[i - 1]; if (use_high_bit_depth) { if (GLEW_ARB_texture_float) - glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA16F_ARB, mip->x, mip->y, 0, GL_RGBA, GL_FLOAT, mip->rect_float); + glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA16F, mip->x, mip->y, 0, GL_RGBA, GL_FLOAT, mip->rect_float); else glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA16, mip->x, mip->y, 0, GL_RGBA, GL_FLOAT, mip->rect_float); } @@ -940,7 +531,7 @@ void GPU_create_gl_tex( if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) { void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth, use_high_bit_depth); - GLenum informat = use_high_bit_depth ? (GLEW_ARB_texture_float ? GL_RGBA16F_ARB : GL_RGBA16) : GL_RGBA8; + GLenum informat = use_high_bit_depth ? (GLEW_ARB_texture_float ? GL_RGBA16F : GL_RGBA16) : GL_RGBA8; GLenum type = use_high_bit_depth ? GL_FLOAT : GL_UNSIGNED_BYTE; if (cube_map) @@ -951,7 +542,7 @@ void GPU_create_gl_tex( if (GPU_get_mipmap() && mipmap) { if (GTS.gpu_mipmap) { - gpu_generate_mipmap(GL_TEXTURE_CUBE_MAP); + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); } else { if (!ibuf) { @@ -975,7 +566,7 @@ void GPU_create_gl_tex( if (mip_cube_map) { for (int j = 0; j < 6; j++) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, - informat, mipw, miph, 0, GL_RGBA, type, mip_cube_map[j]); + informat, mipw, miph, 0, GL_RGBA, type, mip_cube_map[j]); } } gpu_del_cube_map(mip_cube_map); @@ -1056,7 +647,7 @@ bool GPU_upload_dxt_texture(ImBuf *ibuf) size = ((width + 3) / 4) * ((height + 3) / 4) * blocksize; glCompressedTexImage2D(GL_TEXTURE_2D, i, format, width, height, - 0, size, ibuf->dds_data.data + offset); + 0, size, ibuf->dds_data.data + offset); offset += size; width >>= 1; @@ -1091,62 +682,6 @@ void GPU_create_gl_tex_compressed( } #endif } -static void gpu_verify_repeat(Image *ima) -{ - /* set either clamp or repeat in X/Y */ - if (ima->tpageflag & IMA_CLAMP_U) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - - if (ima->tpageflag & IMA_CLAMP_V) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); -} - -int GPU_set_tpage(MTexPoly *mtexpoly, int mipmap, int alphablend) -{ - /* check if we need to clear the state */ - if (mtexpoly == NULL) { - GPU_clear_tpage(false); - return 0; - } - - Image *ima = mtexpoly->tpage; - GTS.lasttface = mtexpoly; - - gpu_verify_alpha_blend(alphablend); - gpu_verify_reflection(ima); - - if (GPU_verify_image(ima, NULL, GL_TEXTURE_2D, mtexpoly->tile, 1, mipmap, false)) { - GTS.curtile = GTS.tile; - GTS.curima = GTS.ima; - GTS.curtilemode = GTS.tilemode; - GTS.curtileXRep = GTS.tileXRep; - GTS.curtileYRep = GTS.tileYRep; - - glEnable(GL_TEXTURE_2D); - } - else { - glDisable(GL_TEXTURE_2D); - - GTS.curtile = 0; - GTS.curima = NULL; - GTS.curtilemode = 0; - GTS.curtileXRep = 0; - GTS.curtileYRep = 0; - - return 0; - } - - gpu_verify_repeat(ima); - - /* Did this get lost in the image recode? */ - /* BKE_image_tag_time(ima);*/ - - return 1; -} /* these two functions are called on entering and exiting texture paint mode, * temporary disabling/enabling mipmapping on all images for quick texture @@ -1206,9 +741,7 @@ void GPU_paint_set_mipmap(bool mipmap) /* check if image has been downscaled and do scaled partial update */ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, int y, int w, int h) { - if ((!GPU_full_non_power_of_two_support() && !is_power_of_2_resolution(ibuf->x, ibuf->y)) || - is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y)) - { + if (is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y)) { int x_limit = smaller_power_of_2_limit(ibuf->x); int y_limit = smaller_power_of_2_limit(ibuf->y); @@ -1261,7 +794,7 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, } if (GPU_get_mipmap()) { - gpu_generate_mipmap(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); } else { ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; @@ -1277,8 +810,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ima->repbind || - (!GTS.gpu_mipmap && GPU_get_mipmap()) || + if ((!GTS.gpu_mipmap && GPU_get_mipmap()) || (ima->bindcode[TEXTARGET_TEXTURE_2D] == 0) || (ibuf == NULL) || (w == 0) || (h == 0)) @@ -1311,7 +843,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i /* we have already accounted for the case where GTS.gpu_mipmap is false * so we will be using GPU mipmap generation here */ if (GPU_get_mipmap()) { - gpu_generate_mipmap(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); } else { ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; @@ -1337,7 +869,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i glPixelStorei(GL_UNPACK_SKIP_ROWS, y); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, - GL_UNSIGNED_BYTE, ibuf->rect); + GL_UNSIGNED_BYTE, ibuf->rect); glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels); @@ -1345,7 +877,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i /* see comment above as to why we are using gpu mipmap generation here */ if (GPU_get_mipmap()) { - gpu_generate_mipmap(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); } else { ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; @@ -1355,61 +887,6 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i BKE_image_release_ibuf(ima, ibuf, NULL); } -void GPU_update_images_framechange(void) -{ - for (Image *ima = G.main->image.first; ima; ima = ima->id.next) { - if (ima->tpageflag & IMA_TWINANIM) { - if (ima->twend >= ima->xrep * ima->yrep) - ima->twend = ima->xrep * ima->yrep - 1; - - /* check: is bindcode not in the array? free. (to do) */ - - ima->lastframe++; - if (ima->lastframe > ima->twend) - ima->lastframe = ima->twsta; - } - } -} - -int GPU_update_image_time(Image *ima, double time) -{ - if (!ima) - return 0; - - if (ima->lastupdate < 0) - ima->lastupdate = 0; - - if (ima->lastupdate > (float)time) - ima->lastupdate = (float)time; - - int inc = 0; - - if (ima->tpageflag & IMA_TWINANIM) { - if (ima->twend >= ima->xrep * ima->yrep) ima->twend = ima->xrep * ima->yrep - 1; - - /* check: is the bindcode not in the array? Then free. (still to do) */ - - float diff = (float)((float)time - ima->lastupdate); - inc = (int)(diff * (float)ima->animspeed); - - ima->lastupdate += ((float)inc / (float)ima->animspeed); - - int newframe = ima->lastframe + inc; - - if (newframe > (int)ima->twend) { - if (ima->twend - ima->twsta != 0) - newframe = (int)ima->twsta - 1 + (newframe - ima->twend) % (ima->twend - ima->twsta); - else - newframe = ima->twsta; - } - - ima->lastframe = newframe; - } - - return inc; -} - - void GPU_free_smoke(SmokeModifierData *smd) { if (smd->type & MOD_SMOKE_TYPE_DOMAIN && smd->domain) { @@ -1437,31 +914,60 @@ void GPU_create_smoke(SmokeModifierData *smd, int highres) if (smoke_has_colors(sds->fluid)) { float *data = MEM_callocN(sizeof(float) * sds->total_cells * 4, "smokeColorTexture"); smoke_get_rgba(sds->fluid, data, 0); - sds->tex = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], 4, data); + sds->tex = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], GPU_RGBA8, data, NULL); MEM_freeN(data); } /* density only */ else { - sds->tex = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], 1, smoke_get_density(sds->fluid)); + sds->tex = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], + GPU_R8, smoke_get_density(sds->fluid), NULL); + + /* Swizzle the RGBA components to read the Red channel so + * that the shader stay the same for colored and non color + * density textures. */ + GPU_texture_bind(sds->tex, 0); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_A, GL_RED); + GPU_texture_unbind(sds->tex); } - sds->tex_flame = (smoke_has_fuel(sds->fluid)) ? GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], 1, smoke_get_flame(sds->fluid)) : NULL; + sds->tex_flame = (smoke_has_fuel(sds->fluid)) ? + GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], + GPU_R8, smoke_get_flame(sds->fluid), NULL) : + NULL; } else if (!sds->tex && highres) { /* rgba texture for color + density */ if (smoke_turbulence_has_colors(sds->wt)) { float *data = MEM_callocN(sizeof(float) * smoke_turbulence_get_cells(sds->wt) * 4, "smokeColorTexture"); smoke_turbulence_get_rgba(sds->wt, data, 0); - sds->tex = GPU_texture_create_3D(sds->res_wt[0], sds->res_wt[1], sds->res_wt[2], 4, data); + sds->tex = GPU_texture_create_3D(sds->res_wt[0], sds->res_wt[1], sds->res_wt[2], GPU_RGBA8, data, NULL); MEM_freeN(data); } /* density only */ else { - sds->tex = GPU_texture_create_3D(sds->res_wt[0], sds->res_wt[1], sds->res_wt[2], 1, smoke_turbulence_get_density(sds->wt)); + sds->tex = GPU_texture_create_3D(sds->res_wt[0], sds->res_wt[1], sds->res_wt[2], + GPU_R8, smoke_turbulence_get_density(sds->wt), NULL); + + /* Swizzle the RGBA components to read the Red channel so + * that the shader stay the same for colored and non color + * density textures. */ + GPU_texture_bind(sds->tex, 0); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_A, GL_RED); + GPU_texture_unbind(sds->tex); } - sds->tex_flame = (smoke_turbulence_has_fuel(sds->wt)) ? GPU_texture_create_3D(sds->res_wt[0], sds->res_wt[1], sds->res_wt[2], 1, smoke_turbulence_get_flame(sds->wt)) : NULL; + sds->tex_flame = (smoke_turbulence_has_fuel(sds->wt)) ? + GPU_texture_create_3D(sds->res_wt[0], sds->res_wt[1], sds->res_wt[2], + GPU_R8, smoke_turbulence_get_flame(sds->wt), NULL) : + NULL; } - sds->tex_shadow = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], 1, sds->shadow); + sds->tex_shadow = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], + GPU_R8, sds->shadow, NULL); } #else // WITH_SMOKE (void)highres; @@ -1499,9 +1005,6 @@ void GPU_free_unused_buffers(void) BLI_linklist_free(image_free_queue, NULL); image_free_queue = NULL; - /* vbo buffers */ - GPU_global_buffer_pool_free_unused(); - BLI_thread_unlock(LOCK_OPENGL); } @@ -1525,14 +1028,6 @@ void GPU_free_image(Image *ima) } } - /* free repeated image binding */ - if (ima->repbind) { - glDeleteTextures(ima->totbind, (GLuint *)ima->repbind); - - MEM_freeN(ima->repbind); - ima->repbind = NULL; - } - ima->tpageflag &= ~(IMA_MIPMAP_COMPLETE | IMA_GLBIND_IS_DATA); } @@ -1576,7 +1071,7 @@ void GPU_free_images_old(void) if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { /* If it's in GL memory, deallocate and set time tag to current time * This gives textures a "second chance" to be used before dying. */ - if (BKE_image_has_bindcode(ima) || ima->repbind) { + if (BKE_image_has_bindcode(ima)) { GPU_free_image(ima); ima->lastused = ctime; } @@ -1589,679 +1084,7 @@ void GPU_free_images_old(void) } } - -/* OpenGL Materials */ - -#define FIXEDMAT 8 - -/* OpenGL state caching for materials */ - -typedef struct GPUMaterialFixed { - float diff[3]; - float spec[3]; - int hard; - float alpha; -} GPUMaterialFixed; - -static struct GPUMaterialState { - GPUMaterialFixed (*matbuf); - GPUMaterialFixed matbuf_fixed[FIXEDMAT]; - int totmat; - - /* set when called inside GPU_begin_object_materials / GPU_end_object_materials - * otherwise calling GPU_object_material_bind returns zero */ - bool is_enabled; - - Material **gmatbuf; - Material *gmatbuf_fixed[FIXEDMAT]; - Material *gboundmat; - Object *gob; - DupliObject *dob; - Scene *gscene; - int glay; - bool gscenelock; - float (*gviewmat)[4]; - float (*gviewinv)[4]; - float (*gviewcamtexcofac); - - bool backface_culling; - bool two_sided_lighting; - - GPUBlendMode *alphablend; - GPUBlendMode alphablend_fixed[FIXEDMAT]; - bool use_alpha_pass, is_alpha_pass; - bool use_matcaps; - - int lastmatnr, lastretval; - GPUBlendMode lastalphablend; - bool is_opensubdiv; -} GMS = {NULL}; - -/* fixed function material, alpha handed by caller */ -static void gpu_material_to_fixed( - GPUMaterialFixed *smat, const Material *bmat, const int gamma, const Object *ob, - const int new_shading_nodes, const bool dimdown) -{ - if (bmat->mode & MA_SHLESS) { - copy_v3_v3(smat->diff, &bmat->r); - - if (gamma) - linearrgb_to_srgb_v3_v3(smat->diff, smat->diff); - - zero_v3(smat->spec); - smat->alpha = 1.0f; - smat->hard = 0; - } - else if (new_shading_nodes) { - copy_v3_v3(smat->diff, &bmat->r); - copy_v3_v3(smat->spec, &bmat->specr); - smat->alpha = 1.0f; - smat->hard = CLAMPIS(bmat->har, 0, 128); - - if (dimdown) { - mul_v3_fl(smat->diff, 0.8f); - mul_v3_fl(smat->spec, 0.5f); - } - - if (gamma) { - linearrgb_to_srgb_v3_v3(smat->diff, smat->diff); - linearrgb_to_srgb_v3_v3(smat->spec, smat->spec); - } - } - else { - mul_v3_v3fl(smat->diff, &bmat->r, bmat->ref + bmat->emit); - - if (bmat->shade_flag & MA_OBCOLOR) - mul_v3_v3(smat->diff, ob->col); - - mul_v3_v3fl(smat->spec, &bmat->specr, bmat->spec); - smat->hard = CLAMPIS(bmat->har, 1, 128); - smat->alpha = 1.0f; - - if (gamma) { - linearrgb_to_srgb_v3_v3(smat->diff, smat->diff); - linearrgb_to_srgb_v3_v3(smat->spec, smat->spec); - } - } -} - -static Material *gpu_active_node_material(Material *ma) -{ - if (ma && ma->use_nodes && ma->nodetree) { - bNode *node = nodeGetActiveID(ma->nodetree, ID_MA); - - if (node) - return (Material *)node->id; - else - return NULL; - } - - return ma; -} - -void GPU_begin_dupli_object(DupliObject *dob) -{ - GMS.dob = dob; -} - -void GPU_end_dupli_object(void) -{ - GMS.dob = NULL; -} - -void GPU_begin_object_materials( - View3D *v3d, RegionView3D *rv3d, Scene *scene, Object *ob, - bool glsl, bool *do_alpha_after) -{ - Material *ma; - GPUMaterial *gpumat; - GPUBlendMode alphablend; - DupliObject *dob; - int a; - const bool gamma = BKE_scene_check_color_management_enabled(scene); - const bool new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); - const bool use_matcap = (v3d->flag2 & V3D_SHOW_SOLID_MATCAP) != 0; /* assumes v3d->defmaterial->preview is set */ - bool use_opensubdiv = false; - -#ifdef WITH_OPENSUBDIV - { - DerivedMesh *derivedFinal = NULL; - if (ob->type == OB_MESH) { - Mesh *me = ob->data; - BMEditMesh *em = me->edit_btmesh; - if (em != NULL) { - derivedFinal = em->derivedFinal; - } - else { - derivedFinal = ob->derivedFinal; - } - } - else { - derivedFinal = ob->derivedFinal; - } - - if (derivedFinal != NULL && derivedFinal->type == DM_TYPE_CCGDM) { - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) derivedFinal; - use_opensubdiv = ccgdm->useGpuBackend; - } - } -#endif - -#ifdef WITH_GAMEENGINE - if (rv3d->rflag & RV3D_IS_GAME_ENGINE) { - ob = BKE_object_lod_matob_get(ob, scene); - } -#endif - - /* initialize state */ - /* DupliObject must be restored */ - dob = GMS.dob; - memset(&GMS, 0, sizeof(GMS)); - GMS.is_enabled = true; - GMS.dob = dob; - GMS.lastmatnr = -1; - GMS.lastretval = -1; - GMS.lastalphablend = GPU_BLEND_SOLID; - GMS.use_matcaps = use_matcap; - - GMS.backface_culling = (v3d->flag2 & V3D_BACKFACE_CULLING) != 0; - - GMS.two_sided_lighting = false; - if (ob && ob->type == OB_MESH) - GMS.two_sided_lighting = (((Mesh *)ob->data)->flag & ME_TWOSIDED) != 0; - - GMS.gob = ob; - GMS.gscene = scene; - GMS.is_opensubdiv = use_opensubdiv; - GMS.totmat = use_matcap ? 1 : ob->totcol + 1; /* materials start from 1, default material is 0 */ - GMS.glay = (v3d->localvd) ? v3d->localvd->lay : v3d->lay; /* keep lamps visible in local view */ - GMS.gscenelock = (v3d->scenelock != 0); - GMS.gviewmat = rv3d->viewmat; - GMS.gviewinv = rv3d->viewinv; - GMS.gviewcamtexcofac = rv3d->viewcamtexcofac; - - /* alpha pass setup. there's various cases to handle here: - * - object transparency on: only solid materials draw in the first pass, - * and only transparent in the second 'alpha' pass. - * - object transparency off: for glsl we draw both in a single pass, and - * for solid we don't use transparency at all. */ - GMS.use_alpha_pass = (do_alpha_after != NULL); - GMS.is_alpha_pass = (v3d->transp != false); - if (GMS.use_alpha_pass) - *do_alpha_after = false; - - if (GMS.totmat > FIXEDMAT) { - GMS.matbuf = MEM_callocN(sizeof(GPUMaterialFixed) * GMS.totmat, "GMS.matbuf"); - GMS.gmatbuf = MEM_callocN(sizeof(*GMS.gmatbuf) * GMS.totmat, "GMS.matbuf"); - GMS.alphablend = MEM_callocN(sizeof(*GMS.alphablend) * GMS.totmat, "GMS.matbuf"); - } - else { - GMS.matbuf = GMS.matbuf_fixed; - GMS.gmatbuf = GMS.gmatbuf_fixed; - GMS.alphablend = GMS.alphablend_fixed; - } - - /* viewport material, setup in space_view3d, defaults to matcap using ma->preview now */ - if (use_matcap) { - GMS.gmatbuf[0] = v3d->defmaterial; - GPU_material_matcap(scene, v3d->defmaterial, use_opensubdiv); - - /* do material 1 too, for displists! */ - memcpy(&GMS.matbuf[1], &GMS.matbuf[0], sizeof(GPUMaterialFixed)); - - GMS.alphablend[0] = GPU_BLEND_SOLID; - } - else { - - /* no materials assigned? */ - if (ob->totcol == 0) { - gpu_material_to_fixed(&GMS.matbuf[0], &defmaterial, 0, ob, new_shading_nodes, true); - - /* do material 1 too, for displists! */ - memcpy(&GMS.matbuf[1], &GMS.matbuf[0], sizeof(GPUMaterialFixed)); - - if (glsl) { - GMS.gmatbuf[0] = &defmaterial; - GPU_material_from_blender(GMS.gscene, &defmaterial, GMS.is_opensubdiv); - } - - GMS.alphablend[0] = GPU_BLEND_SOLID; - } - - /* setup materials */ - for (a = 1; a <= ob->totcol; a++) { - /* find a suitable material */ - ma = give_current_material(ob, a); - if (!glsl && !new_shading_nodes) ma = gpu_active_node_material(ma); - if (ma == NULL) ma = &defmaterial; - - /* create glsl material if requested */ - gpumat = glsl ? GPU_material_from_blender(GMS.gscene, ma, GMS.is_opensubdiv) : NULL; - - if (gpumat) { - /* do glsl only if creating it succeed, else fallback */ - GMS.gmatbuf[a] = ma; - alphablend = GPU_material_alpha_blend(gpumat, ob->col); - } - else { - /* fixed function opengl materials */ - gpu_material_to_fixed(&GMS.matbuf[a], ma, gamma, ob, new_shading_nodes, false); - - if (GMS.use_alpha_pass && ((ma->mode & MA_TRANSP) || (new_shading_nodes && ma->alpha != 1.0f))) { - GMS.matbuf[a].alpha = ma->alpha; - alphablend = (ma->alpha == 1.0f) ? GPU_BLEND_SOLID: GPU_BLEND_ALPHA; - } - else { - GMS.matbuf[a].alpha = 1.0f; - alphablend = GPU_BLEND_SOLID; - } - } - - /* setting 'do_alpha_after = true' indicates this object needs to be - * drawn in a second alpha pass for improved blending */ - if (do_alpha_after && !GMS.is_alpha_pass) - if (ELEM(alphablend, GPU_BLEND_ALPHA, GPU_BLEND_ADD, GPU_BLEND_ALPHA_SORT)) - *do_alpha_after = true; - - GMS.alphablend[a] = alphablend; - } - } - - /* let's start with a clean state */ - GPU_object_material_unbind(); -} - -static int gpu_get_particle_info(GPUParticleInfo *pi) -{ - DupliObject *dob = GMS.dob; - if (dob->particle_system) { - int ind; - if (dob->persistent_id[0] < dob->particle_system->totpart) - ind = dob->persistent_id[0]; - else { - ind = dob->particle_system->child[dob->persistent_id[0] - dob->particle_system->totpart].parent; - } - if (ind >= 0) { - ParticleData *p = &dob->particle_system->particles[ind]; - - pi->scalprops[0] = ind; - pi->scalprops[1] = GMS.gscene->r.cfra - p->time; - pi->scalprops[2] = p->lifetime; - pi->scalprops[3] = p->size; - - copy_v3_v3(pi->location, p->state.co); - pi->location[3] = BLI_hash_int_01(ind); - - copy_v3_v3(pi->velocity, p->state.vel); - copy_v3_v3(pi->angular_velocity, p->state.ave); - return 1; - } - else return 0; - } - else - return 0; -} - -static void GPU_get_object_info(float oi[3], Material *mat) -{ - Object *ob = GMS.gob; - oi[0] = ob->index; - oi[1] = mat->index; - unsigned int random; - if (GMS.dob) { - random = GMS.dob->random_id; - } - else { - random = BLI_hash_int_2d(BLI_hash_string(GMS.gob->id.name + 2), 0); - } - oi[2] = random * (1.0f / (float)0xFFFFFFFF); -} - -int GPU_object_material_bind(int nr, void *attribs) -{ - GPUVertexAttribs *gattribs = attribs; - - /* no GPU_begin_object_materials, use default material */ - if (!GMS.matbuf) { - memset(&GMS, 0, sizeof(GMS)); - - float diffuse[3], specular[3]; - mul_v3_v3fl(diffuse, &defmaterial.r, defmaterial.ref + defmaterial.emit); - mul_v3_v3fl(specular, &defmaterial.specr, defmaterial.spec); - GPU_basic_shader_colors(diffuse, specular, 35, 1.0f); - - if (GMS.two_sided_lighting) - GPU_basic_shader_bind(GPU_SHADER_LIGHTING | GPU_SHADER_TWO_SIDED); - else - GPU_basic_shader_bind(GPU_SHADER_LIGHTING); - - return 0; - } - - /* prevent index to use un-initialized array items */ - if (nr >= GMS.totmat) - nr = 0; - - if (gattribs) - memset(gattribs, 0, sizeof(*gattribs)); - - /* keep current material */ - if (nr == GMS.lastmatnr) - return GMS.lastretval; - - /* unbind glsl material */ - if (GMS.gboundmat) { - if (GMS.is_alpha_pass) glDepthMask(0); - GPU_material_unbind(GPU_material_from_blender(GMS.gscene, GMS.gboundmat, GMS.is_opensubdiv)); - GMS.gboundmat = NULL; - } - - /* draw materials with alpha in alpha pass */ - GMS.lastmatnr = nr; - GMS.lastretval = 1; - - if (GMS.use_alpha_pass) { - GMS.lastretval = ELEM(GMS.alphablend[nr], GPU_BLEND_SOLID, GPU_BLEND_CLIP); - if (GMS.is_alpha_pass) - GMS.lastretval = !GMS.lastretval; - } - else - GMS.lastretval = !GMS.is_alpha_pass; - - if (GMS.lastretval) { - /* for alpha pass, use alpha blend */ - GPUBlendMode alphablend = GMS.alphablend[nr]; - - if (gattribs && GMS.gmatbuf[nr]) { - /* bind glsl material and get attributes */ - Material *mat = GMS.gmatbuf[nr]; - GPUParticleInfo partile_info; - float object_info[3] = {0}; - - float auto_bump_scale; - - GPUMaterial *gpumat = GPU_material_from_blender(GMS.gscene, mat, GMS.is_opensubdiv); - GPU_material_vertex_attributes(gpumat, gattribs); - - if (GMS.dob) { - gpu_get_particle_info(&partile_info); - } - - if (GPU_get_material_builtins(gpumat) & GPU_OBJECT_INFO) { - GPU_get_object_info(object_info, mat); - } - - GPU_material_bind( - gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT), - GMS.gviewmat, GMS.gviewinv, GMS.gviewcamtexcofac, GMS.gscenelock); - - auto_bump_scale = GMS.gob->derivedFinal != NULL ? GMS.gob->derivedFinal->auto_bump_scale : 1.0f; - GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale, &partile_info, object_info); - GMS.gboundmat = mat; - - /* for glsl use alpha blend mode, unless it's set to solid and - * we are already drawing in an alpha pass */ - if (mat->game.alpha_blend != GPU_BLEND_SOLID) - alphablend = mat->game.alpha_blend; - - if (GMS.is_alpha_pass) glDepthMask(1); - - if (GMS.backface_culling) { - if (mat->game.flag) - glEnable(GL_CULL_FACE); - else - glDisable(GL_CULL_FACE); - } - - if (GMS.use_matcaps) - glColor3f(1.0f, 1.0f, 1.0f); - } - else { - /* or do fixed function opengl material */ - GPU_basic_shader_colors( - GMS.matbuf[nr].diff, - GMS.matbuf[nr].spec, GMS.matbuf[nr].hard, GMS.matbuf[nr].alpha); - - if (GMS.two_sided_lighting) - GPU_basic_shader_bind(GPU_SHADER_LIGHTING | GPU_SHADER_TWO_SIDED); - else - GPU_basic_shader_bind(GPU_SHADER_LIGHTING); - } - - /* set (alpha) blending mode */ - GPU_set_material_alpha_blend(alphablend); - } - - return GMS.lastretval; -} - -int GPU_object_material_visible(int nr, void *attribs) -{ - GPUVertexAttribs *gattribs = attribs; - int visible; - - if (!GMS.matbuf) - return 0; - - if (gattribs) - memset(gattribs, 0, sizeof(*gattribs)); - - if (nr >= GMS.totmat) - nr = 0; - - if (GMS.use_alpha_pass) { - visible = ELEM(GMS.alphablend[nr], GPU_BLEND_SOLID, GPU_BLEND_CLIP); - if (GMS.is_alpha_pass) - visible = !visible; - } - else - visible = !GMS.is_alpha_pass; - - return visible; -} - -void GPU_set_material_alpha_blend(int alphablend) -{ - if (GMS.lastalphablend == alphablend) - return; - - gpu_set_alpha_blend(alphablend); - GMS.lastalphablend = alphablend; -} - -int GPU_get_material_alpha_blend(void) -{ - return GMS.lastalphablend; -} - -void GPU_object_material_unbind(void) -{ - GMS.lastmatnr = -1; - GMS.lastretval = 1; - - if (GMS.gboundmat) { - if (GMS.backface_culling) - glDisable(GL_CULL_FACE); - - if (GMS.is_alpha_pass) glDepthMask(0); - GPU_material_unbind(GPU_material_from_blender(GMS.gscene, GMS.gboundmat, GMS.is_opensubdiv)); - GMS.gboundmat = NULL; - } - else - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); - - GPU_set_material_alpha_blend(GPU_BLEND_SOLID); -} - -void GPU_material_diffuse_get(int nr, float diff[4]) -{ - /* prevent index to use un-initialized array items */ - if (nr >= GMS.totmat) - nr = 0; - - /* no GPU_begin_object_materials, use default material */ - if (!GMS.matbuf) { - mul_v3_v3fl(diff, &defmaterial.r, defmaterial.ref + defmaterial.emit); - } - else { - copy_v3_v3(diff, GMS.matbuf[nr].diff); - diff[3] = GMS.matbuf[nr].alpha; - } -} - -bool GPU_material_use_matcaps_get(void) -{ - return GMS.use_matcaps; -} - -bool GPU_object_materials_check(void) -{ - return GMS.is_enabled; -} - -void GPU_end_object_materials(void) -{ - GPU_object_material_unbind(); - - GMS.is_enabled = false; - - if (GMS.matbuf && GMS.matbuf != GMS.matbuf_fixed) { - MEM_freeN(GMS.matbuf); - MEM_freeN(GMS.gmatbuf); - MEM_freeN(GMS.alphablend); - } - - GMS.matbuf = NULL; - GMS.gmatbuf = NULL; - GMS.alphablend = NULL; - GMS.two_sided_lighting = false; - - /* resetting the texture matrix after the scaling needed for tiled textures */ - if (GTS.tilemode) { - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - } -} - -/* Lights */ - -int GPU_default_lights(void) -{ - /* initialize */ - if (U.light[0].flag == 0 && U.light[1].flag == 0 && U.light[2].flag == 0) { - U.light[0].flag = 1; - U.light[0].vec[0] = -0.3; U.light[0].vec[1] = 0.3; U.light[0].vec[2] = 0.9; - U.light[0].col[0] = 0.8; U.light[0].col[1] = 0.8; U.light[0].col[2] = 0.8; - U.light[0].spec[0] = 0.5; U.light[0].spec[1] = 0.5; U.light[0].spec[2] = 0.5; - U.light[0].spec[3] = 1.0; - - U.light[1].flag = 0; - U.light[1].vec[0] = 0.5; U.light[1].vec[1] = 0.5; U.light[1].vec[2] = 0.1; - U.light[1].col[0] = 0.4; U.light[1].col[1] = 0.4; U.light[1].col[2] = 0.8; - U.light[1].spec[0] = 0.3; U.light[1].spec[1] = 0.3; U.light[1].spec[2] = 0.5; - U.light[1].spec[3] = 1.0; - - U.light[2].flag = 0; - U.light[2].vec[0] = 0.3; U.light[2].vec[1] = -0.3; U.light[2].vec[2] = -0.2; - U.light[2].col[0] = 0.8; U.light[2].col[1] = 0.5; U.light[2].col[2] = 0.4; - U.light[2].spec[0] = 0.5; U.light[2].spec[1] = 0.4; U.light[2].spec[2] = 0.3; - U.light[2].spec[3] = 1.0; - } - - GPU_basic_shader_light_set_viewer(false); - - int count = 0; - - for (int a = 0; a < 8; a++) { - if (a < 3 && U.light[a].flag) { - GPULightData light = {0}; - - light.type = GPU_LIGHT_SUN; - - normalize_v3_v3(light.direction, U.light[a].vec); - copy_v3_v3(light.diffuse, U.light[a].col); - copy_v3_v3(light.specular, U.light[a].spec); - - GPU_basic_shader_light_set(a, &light); - - count++; - } - else - GPU_basic_shader_light_set(a, NULL); - } - - return count; -} - -int GPU_scene_object_lights(Scene *scene, Object *ob, int lay, float viewmat[4][4], int ortho) -{ - /* disable all lights */ - for (int count = 0; count < 8; count++) - GPU_basic_shader_light_set(count, NULL); - - /* view direction for specular is not computed correct by default in - * opengl, so we set the settings ourselves */ - GPU_basic_shader_light_set_viewer(!ortho); - - int count = 0; - - for (Base *base = scene->base.first; base; base = base->next) { - if (base->object->type != OB_LAMP) - continue; - - if (!(base->lay & lay) || !(base->lay & ob->lay)) - continue; - - Lamp *la = base->object->data; - - /* setup lamp transform */ - glPushMatrix(); - glLoadMatrixf((float *)viewmat); - - /* setup light */ - GPULightData light = {0}; - - mul_v3_v3fl(light.diffuse, &la->r, la->energy); - mul_v3_v3fl(light.specular, &la->r, la->energy); - - if (la->type == LA_SUN) { - /* directional sun light */ - light.type = GPU_LIGHT_SUN; - normalize_v3_v3(light.direction, base->object->obmat[2]); - } - else { - /* other lamps with position attenuation */ - copy_v3_v3(light.position, base->object->obmat[3]); - - light.constant_attenuation = 1.0f; - light.linear_attenuation = la->att1 / la->dist; - light.quadratic_attenuation = la->att2 / (la->dist * la->dist); - - if (la->type == LA_SPOT) { - light.type = GPU_LIGHT_SPOT; - negate_v3_v3(light.direction, base->object->obmat[2]); - normalize_v3(light.direction); - light.spot_cutoff = RAD2DEGF(la->spotsize * 0.5f); - light.spot_exponent = 128.0f * la->spotblend; - } - else - light.type = GPU_LIGHT_POINT; - } - - GPU_basic_shader_light_set(count, &light); - - glPopMatrix(); - - count++; - if (count == 8) - break; - } - - return count; -} - -static void gpu_multisample(bool enable) +static void gpu_disable_multisample(void) { #ifdef __linux__ /* changing multisample from the default (enabled) causes problems on some @@ -2277,115 +1100,51 @@ static void gpu_multisample(bool enable) } if (toggle_ok) { - if (enable) - glEnable(GL_MULTISAMPLE); - else - glDisable(GL_MULTISAMPLE); + glDisable(GL_MULTISAMPLE); } #else - if (enable) - glEnable(GL_MULTISAMPLE); - else - glDisable(GL_MULTISAMPLE); + glDisable(GL_MULTISAMPLE); #endif } /* Default OpenGL State * - * This is called on startup, for opengl offscreen render and to restore state - * for the game engine. Generally we should always return to this state when + * This is called on startup, for opengl offscreen render. + * Generally we should always return to this state when * temporarily modifying the state for drawing, though that are (undocumented) * exceptions that we should try to get rid of. */ void GPU_state_init(void) { - float mat_ambient[] = { 0.0, 0.0, 0.0, 0.0 }; - float mat_specular[] = { 0.5, 0.5, 0.5, 1.0 }; - - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient); - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_specular); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); - glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 35); - glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + GPU_disable_program_point_size(); - GPU_default_lights(); + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); glDepthFunc(GL_LEQUAL); - /* scaling matrices */ - glEnable(GL_NORMALIZE); - glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); - glDisable(GL_FOG); - glDisable(GL_LIGHTING); - glDisable(GL_COLOR_MATERIAL); - glDisable(GL_LOGIC_OP); + glDisable(GL_COLOR_LOGIC_OP); glDisable(GL_STENCIL_TEST); - glDisable(GL_TEXTURE_1D); - glDisable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - /* default disabled, enable should be local per function */ - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glPixelTransferi(GL_MAP_COLOR, GL_FALSE); - glPixelTransferi(GL_RED_SCALE, 1); - glPixelTransferi(GL_RED_BIAS, 0); - glPixelTransferi(GL_GREEN_SCALE, 1); - glPixelTransferi(GL_GREEN_BIAS, 0); - glPixelTransferi(GL_BLUE_SCALE, 1); - glPixelTransferi(GL_BLUE_BIAS, 0); - glPixelTransferi(GL_ALPHA_SCALE, 1); - glPixelTransferi(GL_ALPHA_BIAS, 0); - - glPixelTransferi(GL_DEPTH_BIAS, 0); - glPixelTransferi(GL_DEPTH_SCALE, 1); - glDepthRange(0.0, 1.0); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); + glDepthRange(0.0, 1.0); glFrontFace(GL_CCW); glCullFace(GL_BACK); glDisable(GL_CULL_FACE); - gpu_multisample(false); - - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + gpu_disable_multisample(); } -#ifdef WITH_OPENSUBDIV -/* Update face-varying variables offset which might be - * different from mesh to mesh sharing the same material. - */ -void GPU_draw_update_fvar_offset(DerivedMesh *dm) +void GPU_enable_program_point_size(void) { - /* Sanity check to be sure we only do this for OpenSubdiv draw. */ - BLI_assert(dm->type == DM_TYPE_CCGDM); - BLI_assert(GMS.is_opensubdiv); - - for (int i = 0; i < GMS.totmat; ++i) { - Material *material = GMS.gmatbuf[i]; - GPUMaterial *gpu_material; - - if (material == NULL) { - continue; - } - - gpu_material = GPU_material_from_blender(GMS.gscene, - material, - GMS.is_opensubdiv); - - GPU_material_update_fvar_offset(gpu_material, dm); - } + glEnable(GL_PROGRAM_POINT_SIZE); } -#endif +void GPU_disable_program_point_size(void) +{ + glDisable(GL_PROGRAM_POINT_SIZE); +} /** \name Framebuffer color depth, for selection codes * \{ */ @@ -2502,10 +1261,10 @@ void GPU_select_to_index_array(unsigned int *col, const unsigned int size) { #define INDEX_BUF_ARRAY(INDEX_FROM_BUF_BITS) \ for (i = size; i--; col++) { \ - if ((c = *col)) { \ - *col = INDEX_FROM_BUF_BITS(c); \ - } \ - } ((void)0) + if ((c = *col)) { \ + *col = INDEX_FROM_BUF_BITS(c); \ + } \ + } ((void)0) if (size > 0) { unsigned int i, c; @@ -2533,4 +1292,173 @@ void GPU_select_to_index_array(unsigned int *col, const unsigned int size) #undef INDEX_BUF_ARRAY } +#define STATE_STACK_DEPTH 16 + +typedef struct { + eGPUAttribMask mask; + + /* GL_ENABLE_BIT */ + unsigned int is_blend : 1; + unsigned int is_cull_face : 1; + unsigned int is_depth_test : 1; + unsigned int is_dither : 1; + unsigned int is_lighting : 1; + unsigned int is_line_smooth : 1; + unsigned int is_color_logic_op : 1; + unsigned int is_multisample : 1; + unsigned int is_polygon_offset_line : 1; + unsigned int is_polygon_offset_fill : 1; + unsigned int is_polygon_smooth : 1; + unsigned int is_sample_alpha_to_coverage : 1; + unsigned int is_scissor_test : 1; + unsigned int is_stencil_test : 1; + + bool is_clip_plane[6]; + + /* GL_DEPTH_BUFFER_BIT */ + /* unsigned int is_depth_test : 1; */ + int depth_func; + double depth_clear_value; + bool depth_write_mask; + + /* GL_SCISSOR_BIT */ + int scissor_box[4]; + /* unsigned int is_scissor_test : 1; */ + + /* GL_VIEWPORT_BIT */ + int viewport[4]; + double near_far[2]; +} GPUAttribValues; + +typedef struct { + GPUAttribValues attrib_stack[STATE_STACK_DEPTH]; + unsigned int top; +} GPUAttribStack; + +static GPUAttribStack state = { + .top = 0 +}; + +#define AttribStack state +#define Attrib state.attrib_stack[state.top] + +/** + * Replacement for glPush/PopAttributes + * + * We don't need to cover all the options of legacy OpenGL + * but simply the ones used by Blender. + */ +void gpuPushAttrib(eGPUAttribMask mask) +{ + Attrib.mask = mask; + + if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { + Attrib.is_depth_test = glIsEnabled(GL_DEPTH_TEST); + glGetIntegerv(GL_DEPTH_FUNC, &Attrib.depth_func); + glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Attrib.depth_clear_value); + glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attrib.depth_write_mask); + } + + if ((mask & GPU_ENABLE_BIT) != 0) { + Attrib.is_blend = glIsEnabled(GL_BLEND); + + for (int i = 0; i < 6; i++) { + Attrib.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i); + } + + Attrib.is_cull_face = glIsEnabled(GL_CULL_FACE); + Attrib.is_depth_test = glIsEnabled(GL_DEPTH_TEST); + Attrib.is_dither = glIsEnabled(GL_DITHER); + Attrib.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH); + Attrib.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP); + Attrib.is_multisample = glIsEnabled(GL_MULTISAMPLE); + Attrib.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE); + Attrib.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL); + Attrib.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH); + Attrib.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); + Attrib.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + Attrib.is_stencil_test = glIsEnabled(GL_STENCIL_TEST); + } + + if ((mask & GPU_SCISSOR_BIT) != 0) { + Attrib.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attrib.scissor_box); + } + + if ((mask & GPU_VIEWPORT_BIT) != 0) { + glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attrib.near_far); + glGetIntegerv(GL_VIEWPORT, (GLint *)&Attrib.viewport); + } + + if ((mask & GPU_BLEND_BIT) != 0) { + Attrib.is_blend = glIsEnabled(GL_BLEND); + } + + BLI_assert(AttribStack.top < STATE_STACK_DEPTH); + AttribStack.top++; +} + +static void restore_mask(GLenum cap, const bool value) +{ + if (value) { + glEnable(cap); + } + else { + glDisable(cap); + } +} + +void gpuPopAttrib(void) +{ + BLI_assert(AttribStack.top > 0); + AttribStack.top--; + + GLint mask = Attrib.mask; + + if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { + restore_mask(GL_DEPTH_TEST, Attrib.is_depth_test); + glDepthFunc(Attrib.depth_func); + glClearDepth(Attrib.depth_clear_value); + glDepthMask(Attrib.depth_write_mask); + } + + if ((mask & GPU_ENABLE_BIT) != 0) { + restore_mask(GL_BLEND, Attrib.is_blend); + + for (int i = 0; i < 6; i++) { + restore_mask(GL_CLIP_PLANE0 + i, Attrib.is_clip_plane[i]); + } + + restore_mask(GL_CULL_FACE, Attrib.is_cull_face); + restore_mask(GL_DEPTH_TEST, Attrib.is_depth_test); + restore_mask(GL_DITHER, Attrib.is_dither); + restore_mask(GL_LINE_SMOOTH, Attrib.is_line_smooth); + restore_mask(GL_COLOR_LOGIC_OP, Attrib.is_color_logic_op); + restore_mask(GL_MULTISAMPLE, Attrib.is_multisample); + restore_mask(GL_POLYGON_OFFSET_LINE, Attrib.is_polygon_offset_line); + restore_mask(GL_POLYGON_OFFSET_FILL, Attrib.is_polygon_offset_fill); + restore_mask(GL_POLYGON_SMOOTH, Attrib.is_polygon_smooth); + restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Attrib.is_sample_alpha_to_coverage); + restore_mask(GL_SCISSOR_TEST, Attrib.is_scissor_test); + restore_mask(GL_STENCIL_TEST, Attrib.is_stencil_test); + } + + if ((mask & GPU_VIEWPORT_BIT) != 0) { + glViewport(Attrib.viewport[0], Attrib.viewport[1], Attrib.viewport[2], Attrib.viewport[3]); + glDepthRange(Attrib.near_far[0], Attrib.near_far[1]); + } + + if ((mask & GPU_SCISSOR_BIT) != 0) { + restore_mask(GL_SCISSOR_TEST, Attrib.is_scissor_test); + glScissor(Attrib.scissor_box[0], Attrib.scissor_box[1], Attrib.scissor_box[2], Attrib.scissor_box[3]); + } + + if ((mask & GPU_BLEND_BIT) != 0) { + restore_mask(GL_BLEND, Attrib.is_blend); + } +} + +#undef Attrib +#undef AttribStack + /** \} */ diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index e0ce87d0e68..73e86c1b391 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -56,14 +56,11 @@ /* Extensions support */ /* -- extension: version of GL that absorbs it + * EXT_gpu_shader4: 3.0 * ARB_framebuffer object: 3.0 - * EXT_framebuffer_object: 3.0 - * EXT_framebuffer_blit: 3.0 - * EXT_framebuffer_multisample: 3.0 * EXT_framebuffer_multisample_blit_scaled: ??? * ARB_draw_instanced: 3.1 * ARB_texture_multisample: 3.2 - * EXT_geometry_shader4: 3.2 * ARB_texture_query_lod: 4.0 */ @@ -71,7 +68,8 @@ static struct GPUGlobal { GLint maxtexsize; GLint maxcubemapsize; GLint maxtextures; - bool extdisabled; + GLint maxubosize; + GLint maxubobinds; int colordepth; int samples_color_texture_max; GPUDeviceType device; @@ -93,11 +91,6 @@ bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver) /* GPU Extensions */ -void GPU_extensions_disable(void) -{ - GG.extdisabled = true; -} - int GPU_max_texture_size(void) { return GG.maxtexsize; @@ -123,6 +116,16 @@ int GPU_max_cube_map_size(void) return GG.maxcubemapsize; } +int GPU_max_ubo_binds(void) +{ + return GG.maxubobinds; +} + +int GPU_max_ubo_size(void) +{ + return GG.maxubosize; +} + void GPU_get_dfdy_factors(float fac[2]) { copy_v2_v2(fac, GG.dfdyfactors); @@ -130,8 +133,11 @@ void GPU_get_dfdy_factors(float fac[2]) void gpu_extensions_init(void) { - /* BLI_assert(GLEW_VERSION_2_1); */ - /* ^-- maybe a bit extreme? */ + /* during 2.8 development each platform has its own OpenGL minimum requirements + * final 2.8 release will be unified on OpenGL 3.3 core profile, no required extensions + * see developer.blender.org/T49012 for details + */ + BLI_assert(GLEW_VERSION_3_3); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &GG.maxtextures); @@ -143,11 +149,22 @@ void gpu_extensions_init(void) else GG.max_anisotropy = 1.0f; + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GG.maxubobinds); + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GG.maxubosize); + +#ifndef NDEBUG + GLint ret; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &ret); + /* We expect FRONT_LEFT to be the default buffer. */ + BLI_assert(ret == GL_FRAMEBUFFER_DEFAULT); +#endif + GLint r, g, b; - glGetIntegerv(GL_RED_BITS, &r); - glGetIntegerv(GL_GREEN_BITS, &g); - glGetIntegerv(GL_BLUE_BITS, &b); - GG.colordepth = r + g + b; /* assumes same depth for RGB */ + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &r); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &g); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, &b); + GG.colordepth = r + g + b; /* Assumes same depth for RGB. */ if (GLEW_VERSION_3_2 || GLEW_ARB_texture_multisample) { glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max); @@ -201,10 +218,6 @@ void gpu_extensions_init(void) GG.driver = GPU_DRIVER_ANY; } - /* make sure double side isn't used by default and only getting enabled in places where it's - * really needed to prevent different unexpected behaviors like with intel gme965 card (sergey) */ - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); - #ifdef _WIN32 GG.os = GPU_OS_WIN; #elif defined(__APPLE__) @@ -246,43 +259,6 @@ void gpu_extensions_exit(void) GPU_invalid_tex_free(); } -bool GPU_legacy_support(void) -{ - /* return whether or not current GL context is compatible with legacy OpenGL */ - static bool checked = false; - static bool support = true; - - if (!checked) { - if (GLEW_VERSION_3_2) { - GLint profile; - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile); - - if (G.debug & G_DEBUG_GPU) { - printf("GL_CONTEXT_PROFILE_MASK = %#x (%s profile)\n", (unsigned int)profile, - (profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) ? "compatibility" : - (profile & GL_CONTEXT_CORE_PROFILE_BIT) ? "core" : "unknown"); - } - - if (profile == 0) { - /* workaround for nVidia's Linux driver */ - support = GLEW_ARB_compatibility; - } - else { - support = profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; - } - } - else if (GLEW_VERSION_3_1) { - support = GLEW_ARB_compatibility; - } - - /* any OpenGL version <= 3.0 is legacy, so support remains true */ - - checked = true; - } - - return support; -} - bool GPU_full_non_power_of_two_support(void) { /* always supported on full GL but still relevant for OpenGL ES 2.0 where @@ -290,39 +266,11 @@ bool GPU_full_non_power_of_two_support(void) return true; } -bool GPU_display_list_support(void) -{ - /* deprecated in GL 3 - * supported on older GL and compatibility profile - * still queried by game engine - */ - return true; -} - bool GPU_bicubic_bump_support(void) { return GLEW_VERSION_4_0 || (GLEW_ARB_texture_query_lod && GLEW_VERSION_3_0); } -bool GPU_geometry_shader_support(void) -{ - /* in GL 3.2 geometry shaders are fully supported - * core profile clashes with our other shaders so accept compatibility only - * other GL versions can use EXT_geometry_shader4 if available - */ - return (GLEW_VERSION_3_2 && GPU_legacy_support()) || GLEW_EXT_geometry_shader4; -} - -bool GPU_geometry_shader_support_via_extension(void) -{ - return GLEW_EXT_geometry_shader4 && !(GLEW_VERSION_3_2 && GPU_legacy_support()); -} - -bool GPU_instanced_drawing_support(void) -{ - return GLEW_VERSION_3_1 || GLEW_ARB_draw_instanced; -} - int GPU_color_depth(void) { return GG.colordepth; @@ -330,12 +278,14 @@ int GPU_color_depth(void) bool GPU_mem_stats_supported(void) { - return (GLEW_NVX_gpu_memory_info || (GLEW_ATI_meminfo)) && (G.debug & G_DEBUG_GPU_MEM); + return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo) && (G.debug & G_DEBUG_GPU_MEM); } void GPU_mem_stats_get(int *totalmem, int *freemem) { + /* TODO(merwin): use Apple's platform API to get this info */ + if (GLEW_NVX_gpu_memory_info) { /* returned value in Kb */ glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, totalmem); diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c index eafa29e6739..f86da2eb064 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.c @@ -28,70 +28,138 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_math_base.h" #include "BKE_global.h" -#include "GPU_debug.h" -#include "GPU_glew.h" +#include "GPU_batch.h" +#include "GPU_draw.h" +#include "GPU_extensions.h" #include "GPU_framebuffer.h" +#include "GPU_matrix.h" #include "GPU_shader.h" #include "GPU_texture.h" -static struct GPUFrameBufferGlobal { - GLuint currentfb; -} GG = {0}; +static ThreadLocal(GLuint) g_currentfb; -/* Number of maximum output slots. - * We support 4 outputs for now (usually we wouldn't need more to preserve fill rate) */ -#define GPU_FB_MAX_SLOTS 4 +typedef enum { + GPU_FB_DEPTH_ATTACHMENT = 0, + GPU_FB_DEPTH_STENCIL_ATTACHMENT, + GPU_FB_COLOR_ATTACHMENT0, + GPU_FB_COLOR_ATTACHMENT1, + GPU_FB_COLOR_ATTACHMENT2, + GPU_FB_COLOR_ATTACHMENT3, + GPU_FB_COLOR_ATTACHMENT4, + /* Number of maximum output slots. + * We support 5 outputs for now (usually we wouldn't need more to preserve fill rate). */ + /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to + * the maximum number of COLOR attachments specified by glDrawBuffers. */ + GPU_FB_MAX_ATTACHEMENT +} GPUAttachmentType; + +#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) + +#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) + +#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0) +#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type)) struct GPUFrameBuffer { GLuint object; - GPUTexture *colortex[GPU_FB_MAX_SLOTS]; - GPUTexture *depthtex; + GPUAttachment attachments[GPU_FB_MAX_ATTACHEMENT]; + uint16_t dirty_flag; + int width, height; + bool multisample; + /* TODO Check that we always use the right context when binding + * (FBOs are not shared accross ogl contexts). */ + // void *ctx; }; +static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) +{ + static const GLenum table[] = { + [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT, + [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT, + [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0, + [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1, + [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2, + [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3, + [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4 + }; + return table[type]; +} + +static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) +{ + switch (GPU_texture_format(tex)) { + case GPU_DEPTH_COMPONENT32F: + case GPU_DEPTH_COMPONENT24: + case GPU_DEPTH_COMPONENT16: + return GPU_FB_DEPTH_ATTACHMENT; + case GPU_DEPTH24_STENCIL8: + return GPU_FB_DEPTH_STENCIL_ATTACHMENT; + default: + return GPU_FB_COLOR_ATTACHMENT0 + slot; + } +} + +static GLenum convert_buffer_bits_to_gl(GPUFrameBufferBits bits) +{ + GLbitfield mask = 0; + mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0; + mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0; + mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0; + return mask; +} + +static GPUTexture *framebuffer_get_depth_tex(GPUFrameBuffer *fb) +{ + if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex) + return fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex; + else + return fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex;; +} + +static GPUTexture *framebuffer_get_color_tex(GPUFrameBuffer *fb, int slot) +{ + return fb->attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; +} + static void gpu_print_framebuffer_error(GLenum status, char err_out[256]) { + const char *format = "GPUFrameBuffer: framebuffer status %s\n"; const char *err = "unknown"; +#define format_status(X) \ + case GL_FRAMEBUFFER_##X: err = "GL_FRAMEBUFFER_"#X; \ + break; + switch (status) { - case GL_FRAMEBUFFER_COMPLETE_EXT: - break; - case GL_INVALID_OPERATION: - err = "Invalid operation"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - err = "Incomplete attachment"; - break; - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - err = "Unsupported framebuffer format"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: - err = "Missing attachment"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: - err = "Attached images must have same dimensions"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: - err = "Attached images must have same format"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: - err = "Missing draw buffer"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: - err = "Missing read buffer"; - break; + /* success */ + format_status(COMPLETE) + /* errors shared by OpenGL desktop & ES */ + format_status(INCOMPLETE_ATTACHMENT) + format_status(INCOMPLETE_MISSING_ATTACHMENT) + format_status(UNSUPPORTED) +#if 0 /* for OpenGL ES only */ + format_status(INCOMPLETE_DIMENSIONS) +#else /* for desktop GL only */ + format_status(INCOMPLETE_DRAW_BUFFER) + format_status(INCOMPLETE_READ_BUFFER) + format_status(INCOMPLETE_MULTISAMPLE) + format_status(UNDEFINED) +#endif } +#undef format_status + if (err_out) { - BLI_snprintf(err_out, 256, "GPUFrameBuffer: framebuffer incomplete error %d '%s'", - (int)status, err); + BLI_snprintf(err_out, 256, format, err); } else { - fprintf(stderr, "GPUFrameBuffer: framebuffer incomplete error %d '%s'\n", - (int)status, err); + fprintf(stderr, format, err); } } @@ -99,237 +167,272 @@ static void gpu_print_framebuffer_error(GLenum status, char err_out[256]) GPUFrameBuffer *GPU_framebuffer_create(void) { - GPUFrameBuffer *fb; + /* We generate the FB object later at first use in order to + * create the framebuffer in the right opengl context. */ + return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer");; +} - if (!(GLEW_VERSION_3_0 || GLEW_ARB_framebuffer_object || - (GLEW_EXT_framebuffer_object && GLEW_EXT_framebuffer_blit))) - { - return NULL; +static void gpu_framebuffer_init(GPUFrameBuffer *fb) +{ + glGenFramebuffers(1, &fb->object); +} + +void GPU_framebuffer_free(GPUFrameBuffer *fb) +{ + for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + if (fb->attachments[type].tex != NULL) { + GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex); + } } - fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); - glGenFramebuffersEXT(1, &fb->object); + /* This restores the framebuffer if it was bound */ + glDeleteFramebuffers(1, &fb->object); - if (!fb->object) { - fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed. %d\n", - (int)glGetError()); - GPU_framebuffer_free(fb); - return NULL; + if (g_currentfb == fb->object) { + g_currentfb = 0; } - /* make sure no read buffer is enabled, so completeness check will not fail. We set those at binding time */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - glReadBuffer(GL_NONE); - glDrawBuffer(GL_NONE); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - - return fb; + MEM_freeN(fb); } -int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) -{ - GLenum attachment; - GLenum error; +/* ---------- Attach ----------- */ - if (slot >= GPU_FB_MAX_SLOTS) { +static void gpu_framebuffer_texture_attach_ex(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) +{ + if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) { fprintf(stderr, "Attaching to index %d framebuffer slot unsupported. " - "Use at most %d\n", slot, GPU_FB_MAX_SLOTS); - return 0; - } - - if ((G.debug & G_DEBUG)) { - if (GPU_texture_bound_number(tex) != -1) { - fprintf(stderr, - "Feedback loop warning!: " - "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n"); - } + "Use at most %d\n", slot, GPU_FB_MAX_COLOR_ATTACHMENT); + return; } - if (GPU_texture_depth(tex)) - attachment = GL_DEPTH_ATTACHMENT_EXT; - else - attachment = GL_COLOR_ATTACHMENT0_EXT + slot; - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - GG.currentfb = fb->object; - - /* Clean glError buffer. */ - while (glGetError() != GL_NO_ERROR) {} - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, - GPU_texture_target(tex), GPU_texture_opengl_bindcode(tex), 0); + GPUAttachmentType type = attachment_type_from_tex(tex, slot); + GPUAttachment *attachment = &fb->attachments[type]; - error = glGetError(); + if ((attachment->tex == tex) && + (attachment->mip == mip) && + (attachment->layer == layer)) + { + return; /* Exact same texture already bound here. */ + } + else if (attachment->tex != NULL) { + GPU_framebuffer_texture_detach(fb, attachment->tex); + } - if (error == GL_INVALID_OPERATION) { - GPU_framebuffer_restore(); - gpu_print_framebuffer_error(error, err_out); - return 0; + if (attachment->tex == NULL) { + GPU_texture_attach_framebuffer(tex, fb, type); } - if (GPU_texture_depth(tex)) - fb->depthtex = tex; - else - fb->colortex[slot] = tex; + attachment->tex = tex; + attachment->mip = mip; + attachment->layer = layer; + GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); +} - GPU_texture_framebuffer_set(tex, fb, slot); +void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip) +{ + gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip); +} - return 1; +void GPU_framebuffer_texture_layer_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) +{ + /* NOTE: We could support 1D ARRAY texture. */ + BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY); + gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip); } -void GPU_framebuffer_texture_detach(GPUTexture *tex) +void GPU_framebuffer_texture_cubeface_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip) { - GLenum attachment; - GPUFrameBuffer *fb = GPU_texture_framebuffer(tex); - int fb_attachment = GPU_texture_framebuffer_attachment(tex); + BLI_assert(GPU_texture_cube(tex)); + gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip); +} - if (!fb) - return; +/* ---------- Detach ----------- */ - if (GG.currentfb != fb->object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - GG.currentfb = fb->object; - } +void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type) +{ + GPUAttachment *attachment = &fb->attachments[type]; - if (GPU_texture_depth(tex)) { - fb->depthtex = NULL; - attachment = GL_DEPTH_ATTACHMENT_EXT; - } - else { - BLI_assert(fb->colortex[fb_attachment] == tex); - fb->colortex[fb_attachment] = NULL; - attachment = GL_COLOR_ATTACHMENT0_EXT + fb_attachment; + if (attachment->tex != tex) { + fprintf(stderr, + "Warning, attempting to detach Texture %p from framebuffer %p " + "but texture is not attached.\n", tex, fb); + return; } - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, GPU_texture_target(tex), 0, 0); + attachment->tex = NULL; + GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); +} - GPU_texture_framebuffer_set(tex, NULL, -1); +void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) +{ + GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb); + GPU_framebuffer_texture_detach_slot(fb, tex, type); } -void GPU_texture_bind_as_framebuffer(GPUTexture *tex) +/* ---------- Config (Attach & Detach) ----------- */ + +/** + * First GPUAttachment in *config is always the depth/depth_stencil buffer. + * Following GPUAttachments are color buffers. + * Setting GPUAttachment.mip to -1 will leave the texture in this slot. + * Setting GPUAttachment.tex to NULL will detach the texture in this slot. + **/ +void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_ct) { - GPUFrameBuffer *fb = GPU_texture_framebuffer(tex); - int fb_attachment = GPU_texture_framebuffer_attachment(tex); + if (config[0].tex) { + BLI_assert(GPU_texture_depth(config[0].tex)); + gpu_framebuffer_texture_attach_ex(fb, config[0].tex, 0, config[0].layer, config[0].mip); + } + else if (config[0].mip == -1) { + /* Leave texture attached */ + } + else if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex != NULL) { + GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex); + } + else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) { + GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex); + } - if (!fb) { - fprintf(stderr, "Error, texture not bound to framebuffer!\n"); - return; + int slot = 0; + for (int i = 1; i < config_ct; ++i, ++slot) { + if (config[i].tex != NULL) { + BLI_assert(GPU_texture_depth(config[i].tex) == false); + gpu_framebuffer_texture_attach_ex(fb, config[i].tex, slot, config[i].layer, config[i].mip); + } + else if (config[i].mip != -1) { + GPUTexture *tex = framebuffer_get_color_tex(fb, slot); + if (tex != NULL) { + GPU_framebuffer_texture_detach(fb, tex); + } + } } +} - /* push attributes */ - glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); - glDisable(GL_SCISSOR_TEST); +/* ---------- Bind / Restore ----------- */ - /* bind framebuffer */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); +static void gpu_framebuffer_attachment_attach(GPUAttachment *attach, GPUAttachmentType attach_type) +{ + int tex_bind = GPU_texture_opengl_bindcode(attach->tex); + GLenum gl_attachment = convert_attachment_type_to_gl(attach_type); - if (GPU_texture_depth(tex)) { - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); + if (attach->layer > -1) { + if (GPU_texture_cube(attach->tex)) { + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach->layer, + tex_bind, attach->mip); + } + else { + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip, attach->layer); + } } else { - /* last bound prevails here, better allow explicit control here too */ - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + fb_attachment); - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + fb_attachment); + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip); } +} - if (GPU_texture_target(tex) == GL_TEXTURE_2D_MULTISAMPLE) { - glEnable(GL_MULTISAMPLE); - } - - /* push matrices and set default viewport and matrix */ - glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex)); - GG.currentfb = fb->object; - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); +static void gpu_framebuffer_attachment_detach(GPUAttachment *UNUSED(attachment), GPUAttachmentType attach_type) +{ + GLenum gl_attachment = convert_attachment_type_to_gl(attach_type); + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0); } -void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot) +static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) { - int numslots = 0, i; - GLenum attachments[4]; + GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; + int numslots = 0; - if (!fb->colortex[slot]) { - fprintf(stderr, "Error, framebuffer slot empty!\n"); - return; - } + BLI_assert(g_currentfb == fb->object); - for (i = 0; i < 4; i++) { - if (fb->colortex[i]) { - attachments[numslots] = GL_COLOR_ATTACHMENT0_EXT + i; + /* Update attachments */ + for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) { + + if (type >= GPU_FB_COLOR_ATTACHMENT0) { + if (fb->attachments[type].tex) { + gl_attachments[numslots] = convert_attachment_type_to_gl(type); + } + else { + gl_attachments[numslots] = GL_NONE; + } numslots++; } + + if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) { + continue; + } + else if (fb->attachments[type].tex != NULL) { + gpu_framebuffer_attachment_attach(&fb->attachments[type], type); + + fb->multisample = (GPU_texture_samples(fb->attachments[type].tex) > 0); + fb->width = GPU_texture_width(fb->attachments[type].tex); + fb->height = GPU_texture_height(fb->attachments[type].tex); + } + else { + gpu_framebuffer_attachment_detach(&fb->attachments[type], type); + } } + fb->dirty_flag = 0; - /* push attributes */ - glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); - glDisable(GL_SCISSOR_TEST); + /* Update draw buffers (color targets) + * This state is saved in the FBO */ + if (numslots) + glDrawBuffers(numslots, gl_attachments); + else + glDrawBuffer(GL_NONE); +} - /* bind framebuffer */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); +void GPU_framebuffer_bind(GPUFrameBuffer *fb) +{ + if (fb->object == 0) + gpu_framebuffer_init(fb); - /* last bound prevails here, better allow explicit control here too */ - glDrawBuffers(numslots, attachments); - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); + if (g_currentfb != fb->object) + glBindFramebuffer(GL_FRAMEBUFFER, fb->object); - /* push matrices and set default viewport and matrix */ - glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot])); - GG.currentfb = fb->object; + g_currentfb = fb->object; - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); -} + if (fb->dirty_flag != 0) + gpu_framebuffer_update_attachments(fb); + /* TODO manually check for errors? */ +#if 0 + char err_out[256]; + if (!GPU_framebuffer_check_valid(fb, err_out)) { + printf("Invalid %s\n", err_out); + } +#endif -void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex)) -{ - /* restore matrix */ - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); + if (fb->multisample) + glEnable(GL_MULTISAMPLE); - /* restore attributes */ - glPopAttrib(); + glViewport(0, 0, fb->width, fb->height); } -void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot) +void GPU_framebuffer_restore(void) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - /* last bound prevails here, better allow explicit control here too */ - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); - - /* push matrices and set default viewport and matrix */ - glViewport(0, 0, GPU_texture_width(fb->colortex[slot]), GPU_texture_height(fb->colortex[slot])); - GG.currentfb = fb->object; - GG.currentfb = fb->object; + if (g_currentfb != 0) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + g_currentfb = 0; + } } bool GPU_framebuffer_bound(GPUFrameBuffer *fb) { - return fb->object == GG.currentfb; + return (fb->object == g_currentfb) && (fb->object != 0); } -bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) +unsigned int GPU_framebuffer_current_get(void) { - GLenum status; - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - GG.currentfb = fb->object; + return g_currentfb; +} - /* Clean glError buffer. */ - while (glGetError() != GL_NO_ERROR) {} +bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) +{ + if (!GPU_framebuffer_bound(fb)) + GPU_framebuffer_bind(fb); - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + if (status != GL_FRAMEBUFFER_COMPLETE) { GPU_framebuffer_restore(); gpu_print_framebuffer_error(status, err_out); return false; @@ -338,109 +441,206 @@ bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) return true; } -void GPU_framebuffer_free(GPUFrameBuffer *fb) +/* ---------- Framebuffer Operations ----------- */ + +#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \ + BLI_assert(GPU_framebuffer_bound(_fb)); \ + UNUSED_VARS_NDEBUG(_fb); + +/* Needs to be done after binding. */ +void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h) { - int i; - if (fb->depthtex) - GPU_framebuffer_texture_detach(fb->depthtex); + CHECK_FRAMEBUFFER_IS_BOUND(fb); - for (i = 0; i < GPU_FB_MAX_SLOTS; i++) { - if (fb->colortex[i]) { - GPU_framebuffer_texture_detach(fb->colortex[i]); - } - } + glViewport(x, y, w, h); +} - if (fb->object) { - glDeleteFramebuffersEXT(1, &fb->object); +void GPU_framebuffer_clear( + GPUFrameBuffer *fb, GPUFrameBufferBits buffers, + const float clear_col[4], float clear_depth, unsigned int clear_stencil) +{ + CHECK_FRAMEBUFFER_IS_BOUND(fb); - if (GG.currentfb == fb->object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - GG.currentfb = 0; - } + if (buffers & GPU_COLOR_BIT) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); + } + if (buffers & GPU_DEPTH_BIT) { + glDepthMask(GL_TRUE); + glClearDepth(clear_depth); + } + if (buffers & GPU_STENCIL_BIT) { + glStencilMask(clear_stencil); } - MEM_freeN(fb); + GLbitfield mask = convert_buffer_bits_to_gl(buffers); + glClear(mask); } -void GPU_framebuffer_restore(void) +void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data) { - if (GG.currentfb != 0) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - GG.currentfb = 0; + CHECK_FRAMEBUFFER_IS_BOUND(fb); + + GLenum type = GL_DEPTH_COMPONENT; + glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */ + glReadPixels(x, y, w, h, type, GL_FLOAT, data); +} + +void GPU_framebuffer_read_color( + GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data) +{ + CHECK_FRAMEBUFFER_IS_BOUND(fb); + + GLenum type; + switch (channels) { + case 1: type = GL_RED; break; + case 2: type = GL_RG; break; + case 3: type = GL_RGB; break; + case 4: type = GL_RGBA; break; + default: + BLI_assert(false && "wrong number of read channels"); + return; } + glReadBuffer(GL_COLOR_ATTACHMENT0 + slot); + glReadPixels(x, y, w, h, type, GL_FLOAT, data); } -void GPU_framebuffer_blur( - GPUFrameBuffer *fb, GPUTexture *tex, - GPUFrameBuffer *blurfb, GPUTexture *blurtex) +/* read_slot and write_slot are only used for color buffers. */ +void GPU_framebuffer_blit( + GPUFrameBuffer *fb_read, int read_slot, + GPUFrameBuffer *fb_write, int write_slot, + GPUFrameBufferBits blit_buffers) { - const float scaleh[2] = {1.0f / GPU_texture_width(blurtex), 0.0f}; - const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(tex)}; + BLI_assert(blit_buffers != 0); - GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR); - int scale_uniform, texture_source_uniform; + GLuint prev_fb = g_currentfb; - if (!blur_shader) - return; + /* Framebuffers must be up to date. This simplify this function. */ + if (fb_read->dirty_flag != 0 || fb_read->object == 0) { + GPU_framebuffer_bind(fb_read); + } + if (fb_write->dirty_flag != 0 || fb_write->object == 0) { + GPU_framebuffer_bind(fb_write); + } - scale_uniform = GPU_shader_get_uniform(blur_shader, "ScaleU"); - texture_source_uniform = GPU_shader_get_uniform(blur_shader, "textureSource"); + const bool do_color = (blit_buffers & GPU_COLOR_BIT); + const bool do_depth = (blit_buffers & GPU_DEPTH_BIT); + const bool do_stencil = (blit_buffers & GPU_STENCIL_BIT); - /* Blurring horizontally */ + GPUTexture *read_tex = (do_depth || do_stencil) + ? framebuffer_get_depth_tex(fb_read) + : framebuffer_get_color_tex(fb_read, read_slot); + GPUTexture *write_tex = (do_depth || do_stencil) + ? framebuffer_get_depth_tex(fb_write) + : framebuffer_get_color_tex(fb_write, read_slot); - /* We do the bind ourselves rather than using GPU_framebuffer_texture_bind() to avoid - * pushing unnecessary matrices onto the OpenGL stack. */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, blurfb->object); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + if (do_depth) { + BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex)); + BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); + } + if (do_stencil) { + BLI_assert(GPU_texture_stencil(read_tex) && GPU_texture_stencil(write_tex)); + BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); + } + if (GPU_texture_samples(write_tex) != 0 || + GPU_texture_samples(read_tex) != 0) + { + /* Can only blit multisample textures to another texture of the same size. */ + BLI_assert((fb_read->width == fb_write->width) && + (fb_read->height == fb_write->height)); + } - /* avoid warnings from texture binding */ - GG.currentfb = blurfb->object; + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object); - GPU_shader_bind(blur_shader); - GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, scaleh); - GPU_shader_uniform_texture(blur_shader, texture_source_uniform, tex); - glViewport(0, 0, GPU_texture_width(blurtex), GPU_texture_height(blurtex)); + if (do_color) { + glReadBuffer(GL_COLOR_ATTACHMENT0 + read_slot); + glDrawBuffer(GL_COLOR_ATTACHMENT0 + write_slot); + /* XXX we messed with the glDrawBuffer, this will reset the + * glDrawBuffers the next time we bind fb_write. */ + fb_write->dirty_flag = GPU_FB_DIRTY_DRAWBUFFER; + } - /* Preparing to draw quad */ - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers); - glDisable(GL_DEPTH_TEST); + glBlitFramebuffer(0, 0, fb_read->width, fb_read->height, + 0, 0, fb_write->width, fb_write->height, + mask, GL_NEAREST); - GPU_texture_bind(tex, 0); + /* Restore previous framebuffer */ + if (fb_write->object == prev_fb) { + GPU_framebuffer_bind(fb_write); /* To update drawbuffers */ + } + else { + glBindFramebuffer(GL_FRAMEBUFFER, prev_fb); + g_currentfb = prev_fb; + } +} - /* Drawing quad */ - glBegin(GL_QUADS); - glTexCoord2d(0, 0); glVertex2f(1, 1); - glTexCoord2d(1, 0); glVertex2f(-1, 1); - glTexCoord2d(1, 1); glVertex2f(-1, -1); - glTexCoord2d(0, 1); glVertex2f(1, -1); - glEnd(); +/** + * Use this if you need to custom downsample your texture and use the previous mip level as input. + * This function only takes care of the correct texture handling. It execute the callback for each texture level. + **/ +void GPU_framebuffer_recursive_downsample( + GPUFrameBuffer *fb, int max_lvl, + void (*callback)(void *userData, int level), void *userData) +{ + /* Framebuffer must be up to date and bound. This simplify this function. */ + if (g_currentfb != fb->object || fb->dirty_flag != 0 || fb->object == 0) { + GPU_framebuffer_bind(fb); + } + /* HACK: We make the framebuffer appear not bound in order to + * not trigger any error in GPU_texture_bind(). */ + GLuint prev_fb = g_currentfb; + g_currentfb = 0; - /* Blurring vertically */ + int i; + int current_dim[2] = {fb->width, fb->height}; + for (i = 1; i < max_lvl + 1; i++) { + /* calculate next viewport size */ + current_dim[0] = max_ii(current_dim[0] / 2, 1); + current_dim[1] = max_ii(current_dim[1] / 2, 1); + + for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) { + if (fb->attachments[type].tex != NULL) { + /* bind next level for rendering but first restrict fetches only to previous level */ + GPUTexture *tex = fb->attachments[type].tex; + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1); + GPU_texture_unbind(tex); + /* copy attachment and replace miplevel. */ + GPUAttachment attachment = fb->attachments[type]; + attachment.mip = i; + gpu_framebuffer_attachment_attach(&attachment, type); + } + } - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER)); - GG.currentfb = fb->object; + glViewport(0, 0, current_dim[0], current_dim[1]); + callback(userData, i); - glViewport(0, 0, GPU_texture_width(tex), GPU_texture_height(tex)); - GPU_shader_uniform_vector(blur_shader, scale_uniform, 2, 1, scalev); - GPU_shader_uniform_texture(blur_shader, texture_source_uniform, blurtex); - GPU_texture_bind(blurtex, 0); + if (current_dim[0] == 1 && current_dim[1] == 1) + break; + } - glBegin(GL_QUADS); - glTexCoord2d(0, 0); glVertex2f(1, 1); - glTexCoord2d(1, 0); glVertex2f(-1, 1); - glTexCoord2d(1, 1); glVertex2f(-1, -1); - glTexCoord2d(0, 1); glVertex2f(1, -1); - glEnd(); + for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; ++type) { + if (fb->attachments[type].tex != NULL) { + /* reset mipmap level range */ + GPUTexture *tex = fb->attachments[type].tex; + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1); + GPU_texture_unbind(tex); + /* Reattach original level */ + /* NOTE: This is not necessary but this makes the FBO config + * remain in sync with the GPUFrameBuffer config. */ + gpu_framebuffer_attachment_attach(&fb->attachments[type], type); + } + } - GPU_shader_unbind(); + g_currentfb = prev_fb; } /* GPUOffScreen */ @@ -451,63 +651,42 @@ struct GPUOffScreen { GPUTexture *depth; }; -GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256]) +GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, bool depth, bool high_bitdepth, char err_out[256]) { GPUOffScreen *ofs; ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); - ofs->fb = GPU_framebuffer_create(); - if (!ofs->fb) { - GPU_offscreen_free(ofs); - return NULL; - } + ofs->color = GPU_texture_create_2D_multisample(width, height, + (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, samples, err_out); - if (samples) { - if (!GLEW_EXT_framebuffer_multisample || - !GLEW_ARB_texture_multisample || - /* Only needed for GPU_offscreen_read_pixels. - * We could add an arg if we intend to use multi-sample - * offscreen buffers w/o reading their pixels */ - !GLEW_EXT_framebuffer_blit || - /* This is required when blitting from a multi-sampled buffers, - * even though we're not scaling. */ - !GLEW_EXT_framebuffer_multisample_blit_scaled) - { - samples = 0; - } - } - - ofs->depth = GPU_texture_create_depth_multisample(width, height, samples, err_out); - if (!ofs->depth) { - GPU_offscreen_free(ofs); - return NULL; + if (depth) { + ofs->depth = GPU_texture_create_2D_multisample(width, height, GPU_DEPTH24_STENCIL8, NULL, samples, err_out); } - if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) { + if ((depth && !ofs->depth) || !ofs->color) { GPU_offscreen_free(ofs); return NULL; } - ofs->color = GPU_texture_create_2D_multisample(width, height, NULL, GPU_HDR_NONE, samples, err_out); - if (!ofs->color) { - GPU_offscreen_free(ofs); - return NULL; - } + gpuPushAttrib(GPU_VIEWPORT_BIT); - if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) { - GPU_offscreen_free(ofs); - return NULL; - } + GPU_framebuffer_ensure_config(&ofs->fb, { + GPU_ATTACHMENT_TEXTURE(ofs->depth), + GPU_ATTACHMENT_TEXTURE(ofs->color) + }); /* check validity at the very end! */ if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) { GPU_offscreen_free(ofs); + gpuPopAttrib(); return NULL; } GPU_framebuffer_restore(); + gpuPopAttrib(); + return ofs; } @@ -525,20 +704,37 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { - glDisable(GL_SCISSOR_TEST); - if (save) - GPU_texture_bind_as_framebuffer(ofs->color); - else { - GPU_framebuffer_bind_no_save(ofs->fb, 0); + if (save) { + gpuPushAttrib(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT); } + glDisable(GL_SCISSOR_TEST); + GPU_framebuffer_bind(ofs->fb); } -void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) +void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore) { - if (restore) - GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); GPU_framebuffer_restore(); - glEnable(GL_SCISSOR_TEST); + if (restore) { + gpuPopAttrib(); + } +} + +void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) +{ + const int w = GPU_texture_width(ofs->color); + const int h = GPU_texture_height(ofs->color); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs->fb->object); + GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + + if (status == GL_FRAMEBUFFER_COMPLETE) { + glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else { + gpu_print_framebuffer_error(status, NULL); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) @@ -546,74 +742,46 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); + BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT); + if (GPU_texture_target(ofs->color) == GL_TEXTURE_2D_MULTISAMPLE) { /* For a multi-sample texture, * we need to create an intermediate buffer to blit to, * before its copied using 'glReadPixels' */ - - /* not needed since 'ofs' needs to be bound to the framebuffer already */ -// #define USE_FBO_CTX_SWITCH - GLuint fbo_blit = 0; GLuint tex_blit = 0; - GLenum status; /* create texture for new 'fbo_blit' */ glGenTextures(1, &tex_blit); - if (!tex_blit) { - goto finally; - } - glBindTexture(GL_TEXTURE_2D, tex_blit); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, type, 0); - -#ifdef USE_FBO_CTX_SWITCH - /* read from multi-sample buffer */ - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, ofs->color->fb->object); - glFramebufferTexture2DEXT( - GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + ofs->color->fb_attachment, - GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0); - status = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - goto finally; - } -#endif + glTexImage2D(GL_TEXTURE_2D, 0, (type == GL_FLOAT) ? GL_RGBA16F : GL_RGBA8, + w, h, 0, GL_RGBA, type, 0); /* write into new single-sample buffer */ - glGenFramebuffersEXT(1, &fbo_blit); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit); - glFramebufferTexture2DEXT( - GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, tex_blit, 0); - status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + glGenFramebuffers(1, &fbo_blit); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tex_blit, 0); + + GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { goto finally; } /* perform the copy */ - glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); /* read the results */ - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fbo_blit); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_blit); glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); -#ifdef USE_FBO_CTX_SWITCH /* restore the original frame-bufer */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ofs->color->fb->object); -#undef USE_FBO_CTX_SWITCH -#endif - + glBindFramebuffer(GL_FRAMEBUFFER, ofs->fb->object); finally: /* cleanup */ - if (tex_blit) { - glDeleteTextures(1, &tex_blit); - } - if (fbo_blit) { - glDeleteFramebuffersEXT(1, &fbo_blit); - } - - GPU_ASSERT_NO_GL_ERRORS("Read Multi-Sample Pixels"); + glDeleteTextures(1, &tex_blit); + glDeleteFramebuffers(1, &fbo_blit); } else { glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); @@ -630,8 +798,17 @@ int GPU_offscreen_height(const GPUOffScreen *ofs) return GPU_texture_height(ofs->color); } -int GPU_offscreen_color_texture(const GPUOffScreen *ofs) +GPUTexture *GPU_offscreen_color_texture(const GPUOffScreen *ofs) { - return GPU_texture_opengl_bindcode(ofs->color); + return ofs->color; } +/* only to be used by viewport code! */ +void GPU_offscreen_viewport_data_get( + GPUOffScreen *ofs, + GPUFrameBuffer **r_fb, GPUTexture **r_color, GPUTexture **r_depth) +{ + *r_fb = ofs->fb; + *r_color = ofs->color; + *r_depth = ofs->depth; +} diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.c new file mode 100644 index 00000000000..5f22b7f9279 --- /dev/null +++ b/source/blender/gpu/intern/gpu_immediate.c @@ -0,0 +1,88 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Mike Erwin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "UI_resources.h" +#include "BLI_utildefines.h" + +#include "gpu_shader_private.h" + +void immBindBuiltinProgram(GPUBuiltinShader shader_id) +{ + GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); + immBindProgram(shader->program, shader->interface); +} + +void immUniformThemeColor(int color_id) +{ + float color[4]; + UI_GetThemeColor4fv(color_id, color); + immUniformColor4fv(color); +} + +void immUniformThemeColor3(int color_id) +{ + float color[3]; + UI_GetThemeColor3fv(color_id, color); + immUniformColor3fv(color); +} + +void immUniformThemeColorShade(int color_id, int offset) +{ + float color[4]; + UI_GetThemeColorShade4fv(color_id, offset, color); + immUniformColor4fv(color); +} + +void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset) +{ + float color[4]; + UI_GetThemeColorShadeAlpha4fv(color_id, color_offset, alpha_offset, color); + immUniformColor4fv(color); +} + +void immUniformThemeColorBlendShade(int color_id1, int color_id2, float fac, int offset) +{ + float color[4]; + UI_GetThemeColorBlendShade4fv(color_id1, color_id2, fac, offset, color); + immUniformColor4fv(color); +} + +void immUniformThemeColorBlend(int color_id1, int color_id2, float fac) +{ + uint8_t color[3]; + UI_GetThemeColorBlend3ubv(color_id1, color_id2, fac, color); + immUniformColor3ubv(color); +} + +void immThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset) +{ + unsigned char col[4]; + UI_GetThemeColorShadeAlpha4ubv(colorid, coloffset, alphaoffset, col); + immUniformColor4ub(col[0], col[1], col[2], col[3]); +} diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c new file mode 100644 index 00000000000..bad878ef4bf --- /dev/null +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -0,0 +1,424 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file source/blender/gpu/intern/gpu_immediate_util.c + * \ingroup gpu + */ + +#include <stdio.h> +#include <string.h> + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "GPU_basic_shader.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" + +static const float cube_coords[8][3] = { + {-1, -1, -1}, + {-1, -1, +1}, + {-1, +1, -1}, + {-1, +1, +1}, + {+1, -1, -1}, + {+1, -1, +1}, + {+1, +1, -1}, + {+1, +1, +1}, +}; +static const int cube_quad_index[6][4] = { + {0, 1, 3, 2}, + {0, 2, 6, 4}, + {0, 4, 5, 1}, + {1, 5, 7, 3}, + {2, 3, 7, 6}, + {4, 6, 7, 5}, +}; +static const int cube_line_index[12][2] = { + {0, 1}, + {0, 2}, + {0, 4}, + {1, 3}, + {1, 5}, + {2, 3}, + {2, 6}, + {3, 7}, + {4, 5}, + {4, 6}, + {5, 7}, + {6, 7}, +}; + +/** + * Pack color into 3 bytes + * + * This define converts a numerical value to the equivalent 24-bit + * color, while not being endian-sensitive. On little-endians, this + * is the same as doing a 'naive' indexing, on big-endian, it is not! + * + * \note BGR format (i.e. 0xBBGGRR)... + * + * \param x color. + */ +void imm_cpack(unsigned int x) +{ + immUniformColor3ub(((x) & 0xFF), + (((x) >> 8) & 0xFF), + (((x) >> 16) & 0xFF)); +} + +static void imm_draw_circle( + Gwn_PrimType prim_type, const uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments) +{ + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; ++i) { + const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex2f(shdr_pos, x + (rad_x * cosf(angle)), y + (rad_y * sinf(angle))); + } + immEnd(); +} + +/** + * Draw a circle outline with the given \a radius. + * The circle is centered at \a x, \a y and drawn in the XY plane. + * + * \param shdr_pos The vertex attribute number for position. + * \param x Horizontal center. + * \param y Vertical center. + * \param radius The circle's radius. + * \param nsegments The number of segments to use in drawing (more = smoother). + */ +void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float rad, int nsegments) +{ + imm_draw_circle(GWN_PRIM_LINE_LOOP, shdr_pos, x, y, rad, rad, nsegments); +} + +/** + * Draw a filled circle with the given \a radius. + * The circle is centered at \a x, \a y and drawn in the XY plane. + * + * \param shdr_pos The vertex attribute number for position. + * \param x Horizontal center. + * \param y Vertical center. + * \param radius The circle's radius. + * \param nsegments The number of segments to use in drawing (more = smoother). + */ +void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float rad, int nsegments) +{ + imm_draw_circle(GWN_PRIM_TRI_FAN, shdr_pos, x, y, rad, rad, nsegments); +} + +void imm_draw_circle_wire_aspect_2d(uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments) +{ + imm_draw_circle(GWN_PRIM_LINE_LOOP, shdr_pos, x, y, rad_x, rad_y, nsegments); +} +void imm_draw_circle_fill_aspect_2d(uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments) +{ + imm_draw_circle(GWN_PRIM_TRI_FAN, shdr_pos, x, y, rad_x, rad_y, nsegments); +} + +/** + * \note We could have `imm_draw_lined_disk_partial` but currently there is no need. + */ +static void imm_draw_disk_partial( + Gwn_PrimType prim_type, unsigned pos, float x, float y, + float rad_inner, float rad_outer, int nsegments, float start, float sweep) +{ + /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ + const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2); + const float angle_end = -(DEG2RADF(sweep) - angle_start); + nsegments += 1; + immBegin(prim_type, nsegments * 2); + for (int i = 0; i < nsegments; ++i) { + const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1))); + const float angle_sin = sinf(angle); + const float angle_cos = cosf(angle); + immVertex2f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin); + immVertex2f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin); + } + immEnd(); +} + +/** + * Draw a filled arc with the given inner and outer radius. + * The circle is centered at \a x, \a y and drawn in the XY plane. + * + * \note Arguments are `gluPartialDisk` compatible. + * + * \param pos: The vertex attribute number for position. + * \param x: Horizontal center. + * \param y: Vertical center. + * \param radius_inner: The inner circle's radius. + * \param radius_outer: The outer circle's radius (can be zero). + * \param nsegments: The number of segments to use in drawing (more = smoother). + * \param start: Specifies the starting angle, in degrees, of the disk portion. + * \param sweep: Specifies the sweep angle, in degrees, of the disk portion. + */ +void imm_draw_disk_partial_fill_2d( + unsigned pos, float x, float y, + float rad_inner, float rad_outer, int nsegments, float start, float sweep) +{ + imm_draw_disk_partial(GWN_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep); +} + +static void imm_draw_circle_3D( + Gwn_PrimType prim_type, unsigned pos, float x, float y, + float rad, int nsegments) +{ + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; ++i) { + float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex3f(pos, x + rad * cosf(angle), y + rad * sinf(angle), 0.0f); + } + immEnd(); +} + +void imm_draw_circle_wire_3d(unsigned pos, float x, float y, float rad, int nsegments) +{ + imm_draw_circle_3D(GWN_PRIM_LINE_LOOP, pos, x, y, rad, nsegments); +} + +void imm_draw_circle_fill_3d(unsigned pos, float x, float y, float rad, int nsegments) +{ + imm_draw_circle_3D(GWN_PRIM_TRI_FAN, pos, x, y, rad, nsegments); +} + +/** +* Draw a lined box. +* +* \param pos The vertex attribute number for position. +* \param x1 left. +* \param y1 bottom. +* \param x2 right. +* \param y2 top. +*/ +void imm_draw_box_wire_2d(unsigned pos, float x1, float y1, float x2, float y2) +{ + immBegin(GWN_PRIM_LINE_LOOP, 4); + immVertex2f(pos, x1, y1); + immVertex2f(pos, x1, y2); + immVertex2f(pos, x2, y2); + immVertex2f(pos, x2, y1); + immEnd(); +} + +void imm_draw_box_wire_3d(unsigned pos, float x1, float y1, float x2, float y2) +{ + /* use this version when Gwn_VertFormat has a vec3 position */ + immBegin(GWN_PRIM_LINE_LOOP, 4); + immVertex3f(pos, x1, y1, 0.0f); + immVertex3f(pos, x1, y2, 0.0f); + immVertex3f(pos, x2, y2, 0.0f); + immVertex3f(pos, x2, y1, 0.0f); + immEnd(); +} + +/** + * Draw a standard checkerboard to indicate transparent backgrounds. + */ +void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2) +{ + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_CHECKER); + + immUniform4f("color1", 0.15f, 0.15f, 0.15f, 1.0f); + immUniform4f("color2", 0.2f, 0.2f, 0.2f, 1.0f); + immUniform1i("size", 8); + + immRectf(pos, x1, y1, x2, y2); + + immUnbindProgram(); +} + +void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) +{ + float coords[ARRAY_SIZE(cube_coords)][3]; + + for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { + madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + } + + immBegin(GWN_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2); + for (int i = 0; i < ARRAY_SIZE(cube_quad_index); i++) { + immVertex3fv(pos, coords[cube_quad_index[i][0]]); + immVertex3fv(pos, coords[cube_quad_index[i][1]]); + immVertex3fv(pos, coords[cube_quad_index[i][2]]); + + immVertex3fv(pos, coords[cube_quad_index[i][0]]); + immVertex3fv(pos, coords[cube_quad_index[i][2]]); + immVertex3fv(pos, coords[cube_quad_index[i][3]]); + } + immEnd(); +} + +void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) +{ + float coords[ARRAY_SIZE(cube_coords)][3]; + + for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { + madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + } + + immBegin(GWN_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2); + for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) { + immVertex3fv(pos, coords[cube_line_index[i][0]]); + immVertex3fv(pos, coords[cube_line_index[i][1]]); + } + immEnd(); +} + +/** +* Draw a cylinder. Replacement for gluCylinder. +* _warning_ : Slow, better use it only if you no other choices. +* +* \param pos The vertex attribute number for position. +* \param nor The vertex attribute number for normal. +* \param base Specifies the radius of the cylinder at z = 0. +* \param top Specifies the radius of the cylinder at z = height. +* \param height Specifies the height of the cylinder. +* \param slices Specifies the number of subdivisions around the z axis. +* \param stacks Specifies the number of subdivisions along the z axis. +*/ +void imm_draw_cylinder_fill_normal_3d( + unsigned int pos, unsigned int nor, float base, float top, float height, int slices, int stacks) +{ + immBegin(GWN_PRIM_TRIS, 6 * slices * stacks); + for (int i = 0; i < slices; ++i) { + const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices); + const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices); + const float cos1 = cosf(angle1); + const float sin1 = sinf(angle1); + const float cos2 = cosf(angle2); + const float sin2 = sinf(angle2); + + for (int j = 0; j < stacks; ++j) { + float fac1 = (float)j / (float)stacks; + float fac2 = (float)(j + 1) / (float)stacks; + float r1 = base * (1.f - fac1) + top * fac1; + float r2 = base * (1.f - fac2) + top * fac2; + float h1 = height * ((float)j / (float)stacks); + float h2 = height * ((float)(j + 1) / (float)stacks); + + float v1[3] = {r1 *cos2, r1 * sin2, h1}; + float v2[3] = {r2 *cos2, r2 * sin2, h2}; + float v3[3] = {r2 *cos1, r2 * sin1, h2}; + float v4[3] = {r1 *cos1, r1 * sin1, h1}; + float n1[3], n2[3]; + + /* calc normals */ + sub_v3_v3v3(n1, v2, v1); + normalize_v3(n1); + n1[0] = cos1; n1[1] = sin1; n1[2] = 1 - n1[2]; + + sub_v3_v3v3(n2, v3, v4); + normalize_v3(n2); + n2[0] = cos2; n2[1] = sin2; n2[2] = 1 - n2[2]; + + /* first tri */ + immAttrib3fv(nor, n2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immAttrib3fv(nor, n1); + immVertex3fv(pos, v3); + + /* second tri */ + immVertex3fv(pos, v3); + immVertex3fv(pos, v4); + immAttrib3fv(nor, n2); + immVertex3fv(pos, v1); + } + } + immEnd(); +} + +void imm_draw_cylinder_wire_3d(unsigned int pos, float base, float top, float height, int slices, int stacks) +{ + immBegin(GWN_PRIM_LINES, 6 * slices * stacks); + for (int i = 0; i < slices; ++i) { + const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices); + const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices); + const float cos1 = cosf(angle1); + const float sin1 = sinf(angle1); + const float cos2 = cosf(angle2); + const float sin2 = sinf(angle2); + + for (int j = 0; j < stacks; ++j) { + float fac1 = (float)j / (float)stacks; + float fac2 = (float)(j + 1) / (float)stacks; + float r1 = base * (1.f - fac1) + top * fac1; + float r2 = base * (1.f - fac2) + top * fac2; + float h1 = height * ((float)j / (float)stacks); + float h2 = height * ((float)(j + 1) / (float)stacks); + + float v1[3] = {r1 * cos2, r1 * sin2, h1}; + float v2[3] = {r2 * cos2, r2 * sin2, h2}; + float v3[3] = {r2 * cos1, r2 * sin1, h2}; + float v4[3] = {r1 * cos1, r1 * sin1, h1}; + + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + + immVertex3fv(pos, v2); + immVertex3fv(pos, v3); + + immVertex3fv(pos, v1); + immVertex3fv(pos, v4); + } + } + immEnd(); +} + +void imm_draw_cylinder_fill_3d(unsigned int pos, float base, float top, float height, int slices, int stacks) +{ + immBegin(GWN_PRIM_TRIS, 6 * slices * stacks); + for (int i = 0; i < slices; ++i) { + const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices); + const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices); + const float cos1 = cosf(angle1); + const float sin1 = sinf(angle1); + const float cos2 = cosf(angle2); + const float sin2 = sinf(angle2); + + for (int j = 0; j < stacks; ++j) { + float fac1 = (float)j / (float)stacks; + float fac2 = (float)(j + 1) / (float)stacks; + float r1 = base * (1.f - fac1) + top * fac1; + float r2 = base * (1.f - fac2) + top * fac2; + float h1 = height * ((float)j / (float)stacks); + float h2 = height * ((float)(j + 1) / (float)stacks); + + float v1[3] = {r1 * cos2, r1 * sin2, h1}; + float v2[3] = {r2 * cos2, r2 * sin2, h2}; + float v3[3] = {r2 * cos1, r2 * sin1, h2}; + float v4[3] = {r1 * cos1, r1 * sin1, h1}; + + /* first tri */ + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immVertex3fv(pos, v3); + + /* second tri */ + immVertex3fv(pos, v3); + immVertex3fv(pos, v4); + immVertex3fv(pos, v1); + } + } + immEnd(); +} diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index c72c83b6b07..7b596e6d76b 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -30,8 +30,11 @@ */ #include "BLI_sys_types.h" +#include "GPU_buffers.h" #include "GPU_init_exit.h" /* interface */ - +#include "GPU_immediate.h" +#include "GPU_batch.h" +#include "GPU_texture.h" #include "BKE_global.h" #include "intern/gpu_codegen.h" @@ -54,19 +57,36 @@ void GPU_init(void) gpu_extensions_init(); /* must come first */ + GPU_texture_init_orphans(); gpu_codegen_init(); if (G.debug & G_DEBUG_GPU) gpu_debug_init(); + gpu_batch_init(); + + if (!G.background) { + immInit(); + } + + GPU_pbvh_fix_linking(); } void GPU_exit(void) { + if (!G.background) { + immDestroy(); + } + + gpu_batch_exit(); + + GPU_texture_exit_orphans(); + if (G.debug & G_DEBUG_GPU) gpu_debug_exit(); + gpu_codegen_exit(); gpu_extensions_exit(); /* must come last */ diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 40ff9ca629b..7b3750c970b 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -45,16 +45,17 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_rand.h" #include "BKE_anim.h" #include "BKE_colorband.h" #include "BKE_colortools.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_scene.h" -#include "BKE_group.h" #include "IMB_imbuf_types.h" @@ -63,6 +64,9 @@ #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_uniformbuffer.h" + +#include "DRW_engine.h" #include "gpu_codegen.h" @@ -72,32 +76,17 @@ /* Structs */ -typedef enum DynMatProperty { - DYN_LAMP_CO = 1, - DYN_LAMP_VEC = 2, - DYN_LAMP_IMAT = 4, - DYN_LAMP_PERSMAT = 8, -} DynMatProperty; - -static struct GPUWorld { - float mistenabled; - float mistype; - float miststart; - float mistdistance; - float mistintensity; - float mistcol[4]; - float horicol[3]; - float ambcol[4]; - float zencol[3]; -} GPUWorld; - struct GPUMaterial { - Scene *scene; + Scene *scene; /* DEPRECATED was only usefull for lamps */ Material *ma; /* material for mesh surface, worlds or something else. * some code generation is done differently depending on the use case */ - int type; + int type; /* DEPRECATED */ + GPUMaterialStatus status; + + const void *engine_type; /* attached engine type */ + int options; /* to identify shader variations (shadow, probe, world background...) */ /* for creating the material */ ListBase nodes; @@ -105,6 +94,7 @@ struct GPUMaterial { /* for binding the material */ GPUPass *pass; + ListBase inputs; /* GPUInput */ GPUVertexAttribs attribs; int builtins; int alpha, obcolalpha; @@ -124,284 +114,64 @@ struct GPUMaterial { int objectinfoloc; - ListBase lamps; - bool bound; - bool is_opensubdiv; -}; - -struct GPULamp { - Scene *scene; - Object *ob; - Object *par; - Lamp *la; - - int type, mode, lay, hide; - - float dynenergy, dyncol[3]; - float energy, col[3]; - - float co[3], vec[3]; - float dynco[3], dynvec[3]; - float obmat[4][4]; - float imat[4][4]; - float dynimat[4][4]; - float spotsi, spotbl, k; - float spotvec[2]; - float dyndist, dynatt1, dynatt2; - float dist, att1, att2; - float coeff_const, coeff_lin, coeff_quad; - float shadow_color[3]; - - float bias, d, clipend; - int size; - - int falloff_type; - struct CurveMapping *curfalloff; - - float winmat[4][4]; - float viewmat[4][4]; - float persmat[4][4]; - float dynpersmat[4][4]; - - GPUFrameBuffer *fb; - GPUFrameBuffer *blurfb; - GPUTexture *tex; - GPUTexture *depthtex; - GPUTexture *blurtex; - - ListBase materials; + /* XXX: Should be in Material. But it depends on the output node + * used and since the output selection is difference for GPUMaterial... + */ + int domain; + + /* Used by 2.8 pipeline */ + GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ + + /* Eevee SSS */ + GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */ + GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ + float *sss_radii; /* UBO containing SSS profile. */ + int sss_samples; + short int *sss_falloff; + float *sss_sharpness; + bool sss_dirty; }; -/* Forward declaration so shade_light_textures() can use this, while still keeping the code somewhat organized */ -static void texture_rgb_blend( - GPUMaterial *mat, GPUNodeLink *tex, GPUNodeLink *out, GPUNodeLink *fact, GPUNodeLink *facg, - int blendtype, GPUNodeLink **in); +enum { + GPU_DOMAIN_SURFACE = (1 << 0), + GPU_DOMAIN_VOLUME = (1 << 1), + GPU_DOMAIN_SSS = (1 << 2) +}; /* Functions */ -static GPUMaterial *GPU_material_construct_begin(Material *ma) -{ - GPUMaterial *material = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"); - - material->ma = ma; - - return material; -} - -static void gpu_material_set_attrib_id(GPUMaterial *material) -{ - GPUVertexAttribs *attribs = &material->attribs; - GPUPass *pass = material->pass; - if (!pass) { - attribs->totlayer = 0; - return; - } - - GPUShader *shader = GPU_pass_shader(pass); - if (!shader) { - attribs->totlayer = 0; - return; - } - - /* convert from attribute number to the actual id assigned by opengl, - * in case the attrib does not get a valid index back, it was probably - * removed by the glsl compiler by dead code elimination */ - - int b = 0; - for (int a = 0; a < attribs->totlayer; a++) { - char name[32]; - BLI_snprintf(name, sizeof(name), "att%d", attribs->layer[a].attribid); - attribs->layer[a].glindex = GPU_shader_get_attribute(shader, name); - - BLI_snprintf(name, sizeof(name), "att%d_info", attribs->layer[a].attribid); - attribs->layer[a].glinfoindoex = GPU_shader_get_uniform(shader, name); - - if (attribs->layer[a].glindex >= 0) { - attribs->layer[b] = attribs->layer[a]; - b++; - } - } - - attribs->totlayer = b; -} - -static int gpu_material_construct_end(GPUMaterial *material, const char *passname) -{ - if (material->outlink) { - GPUNodeLink *outlink = material->outlink; - material->pass = GPU_generate_pass(&material->nodes, outlink, - &material->attribs, &material->builtins, material->type, - passname, - material->is_opensubdiv, - GPU_material_use_new_shading_nodes(material)); - - if (!material->pass) - return 0; - - gpu_material_set_attrib_id(material); - - GPUShader *shader = GPU_pass_shader(material->pass); - - if (material->builtins & GPU_VIEW_MATRIX) - material->viewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_VIEW_MATRIX)); - if (material->builtins & GPU_INVERSE_VIEW_MATRIX) - material->invviewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_INVERSE_VIEW_MATRIX)); - if (material->builtins & GPU_OBJECT_MATRIX) - material->obmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBJECT_MATRIX)); - if (material->builtins & GPU_INVERSE_OBJECT_MATRIX) - material->invobmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_INVERSE_OBJECT_MATRIX)); - if (material->builtins & GPU_LOC_TO_VIEW_MATRIX) - material->localtoviewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_LOC_TO_VIEW_MATRIX)); - if (material->builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) - material->invlocaltoviewmatloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_INVERSE_LOC_TO_VIEW_MATRIX)); - if (material->builtins & GPU_OBCOLOR) - material->obcolloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBCOLOR)); - if (material->builtins & GPU_AUTO_BUMPSCALE) - material->obautobumpscaleloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_AUTO_BUMPSCALE)); - if (material->builtins & GPU_CAMERA_TEXCO_FACTORS) - material->cameratexcofacloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_CAMERA_TEXCO_FACTORS)); - if (material->builtins & GPU_PARTICLE_SCALAR_PROPS) - material->partscalarpropsloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_SCALAR_PROPS)); - if (material->builtins & GPU_PARTICLE_LOCATION) - material->partcoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_LOCATION)); - if (material->builtins & GPU_PARTICLE_VELOCITY) - material->partvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_VELOCITY)); - if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) - material->partangvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_ANG_VELOCITY)); - if (material->builtins & GPU_OBJECT_INFO) - material->objectinfoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBJECT_INFO)); - return 1; - } - else { - GPU_pass_free_nodes(&material->nodes); - } - - return 0; -} - void GPU_material_free(ListBase *gpumaterial) { for (LinkData *link = gpumaterial->first; link; link = link->next) { GPUMaterial *material = link->data; - if (material->pass) - GPU_pass_free(material->pass); + /* Cancel / wait any pending lazy compilation. */ + DRW_deferred_shader_remove(material); - for (LinkData *nlink = material->lamps.first; nlink; nlink = nlink->next) { - GPULamp *lamp = nlink->data; + GPU_pass_free_nodes(&material->nodes); + GPU_inputs_free(&material->inputs); - if (material->ma) { - Material *ma = material->ma; + if (material->pass) + GPU_pass_release(material->pass); - LinkData *next = NULL; - for (LinkData *mlink = lamp->materials.first; mlink; mlink = next) { - next = mlink->next; - if (mlink->data == ma) - BLI_freelinkN(&lamp->materials, mlink); - } - } + if (material->ubo != NULL) { + GPU_uniformbuffer_free(material->ubo); } - BLI_freelistN(&material->lamps); - - MEM_freeN(material); - } - - BLI_freelistN(gpumaterial); -} - -bool GPU_lamp_visible(GPULamp *lamp, SceneRenderLayer *srl, Material *ma) -{ - if (lamp->hide) - return false; - else if (srl && srl->light_override) - return BKE_group_object_exists(srl->light_override, lamp->ob); - else if (ma && ma->group) - return BKE_group_object_exists(ma->group, lamp->ob); - else - return true; -} - -void GPU_material_bind( - GPUMaterial *material, int oblay, int viewlay, double time, int mipmap, - float viewmat[4][4], float viewinv[4][4], float camerafactors[4], bool scenelock) -{ - if (material->pass) { - GPUShader *shader = GPU_pass_shader(material->pass); - SceneRenderLayer *srl = scenelock ? BLI_findlink(&material->scene->r.layers, material->scene->r.actlay) : NULL; - - if (srl) - viewlay &= srl->lay; - - /* handle layer lamps */ - if (material->type == GPU_MATERIAL_TYPE_MESH) { - for (LinkData *nlink = material->lamps.first; nlink; nlink = nlink->next) { - GPULamp *lamp = nlink->data; - - if ((lamp->lay & viewlay) && (!(lamp->mode & LA_LAYER) || (lamp->lay & oblay)) && - GPU_lamp_visible(lamp, srl, material->ma)) - { - lamp->dynenergy = lamp->energy; - copy_v3_v3(lamp->dyncol, lamp->col); - } - else { - lamp->dynenergy = 0.0f; - lamp->dyncol[0] = lamp->dyncol[1] = lamp->dyncol[2] = 0.0f; - } - - if (material->dynproperty & DYN_LAMP_VEC) { - copy_v3_v3(lamp->dynvec, lamp->vec); - normalize_v3(lamp->dynvec); - negate_v3(lamp->dynvec); - mul_mat3_m4_v3(viewmat, lamp->dynvec); - } - - if (material->dynproperty & DYN_LAMP_CO) { - copy_v3_v3(lamp->dynco, lamp->co); - mul_m4_v3(viewmat, lamp->dynco); - } - - if (material->dynproperty & DYN_LAMP_IMAT) { - mul_m4_m4m4(lamp->dynimat, lamp->imat, viewinv); - } - - if (material->dynproperty & DYN_LAMP_PERSMAT) { - /* The lamp matrices are already updated if we're using shadow buffers */ - if (!GPU_lamp_has_shadow_buffer(lamp)) { - GPU_lamp_update_buffer_mats(lamp); - } - mul_m4_m4m4(lamp->dynpersmat, lamp->persmat, viewinv); - } - } + if (material->sss_tex_profile != NULL) { + GPU_texture_free(material->sss_tex_profile); } - /* note material must be bound before setting uniforms */ - GPU_pass_bind(material->pass, time, mipmap); - - /* handle per material built-ins */ - if (material->builtins & GPU_VIEW_MATRIX) { - GPU_shader_uniform_vector(shader, material->viewmatloc, 16, 1, (float *)viewmat); - } - if (material->builtins & GPU_INVERSE_VIEW_MATRIX) { - GPU_shader_uniform_vector(shader, material->invviewmatloc, 16, 1, (float *)viewinv); + if (material->sss_profile != NULL) { + GPU_uniformbuffer_free(material->sss_profile); } - if (material->builtins & GPU_CAMERA_TEXCO_FACTORS) { - if (camerafactors) { - GPU_shader_uniform_vector(shader, material->cameratexcofacloc, 4, 1, (float *)camerafactors); - } - else { - /* use default, no scaling no offset */ - float borders[4] = {1.0f, 1.0f, 0.0f, 0.0f}; - GPU_shader_uniform_vector(shader, material->cameratexcofacloc, 4, 1, (float *)borders); - } - } - - GPU_pass_update_uniforms(material->pass); - material->bound = 1; + MEM_freeN(material); } + + BLI_freelistN(gpumaterial); } GPUBuiltin GPU_get_material_builtins(GPUMaterial *material) @@ -409,77 +179,6 @@ GPUBuiltin GPU_get_material_builtins(GPUMaterial *material) return material->builtins; } -void GPU_material_bind_uniforms( - GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4], - float autobumpscale, GPUParticleInfo *pi, float object_info[3]) -{ - if (material->pass) { - GPUShader *shader = GPU_pass_shader(material->pass); - float invmat[4][4], col[4]; - float localtoviewmat[4][4]; - float invlocaltoviewmat[4][4]; - - /* handle per object builtins */ - if (material->builtins & GPU_OBJECT_MATRIX) { - GPU_shader_uniform_vector(shader, material->obmatloc, 16, 1, (float *)obmat); - } - if (material->builtins & GPU_INVERSE_OBJECT_MATRIX) { - invert_m4_m4(invmat, obmat); - GPU_shader_uniform_vector(shader, material->invobmatloc, 16, 1, (float *)invmat); - } - if (material->builtins & GPU_LOC_TO_VIEW_MATRIX) { - if (viewmat) { - mul_m4_m4m4(localtoviewmat, viewmat, obmat); - GPU_shader_uniform_vector(shader, material->localtoviewmatloc, 16, 1, (float *)localtoviewmat); - } - } - if (material->builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - if (viewmat) { - mul_m4_m4m4(localtoviewmat, viewmat, obmat); - invert_m4_m4(invlocaltoviewmat, localtoviewmat); - GPU_shader_uniform_vector(shader, material->invlocaltoviewmatloc, 16, 1, (float *)invlocaltoviewmat); - } - } - if (material->builtins & GPU_OBCOLOR) { - copy_v4_v4(col, obcol); - CLAMP(col[3], 0.0f, 1.0f); - GPU_shader_uniform_vector(shader, material->obcolloc, 4, 1, col); - } - if (material->builtins & GPU_AUTO_BUMPSCALE) { - GPU_shader_uniform_vector(shader, material->obautobumpscaleloc, 1, 1, &autobumpscale); - } - if (material->builtins & GPU_PARTICLE_SCALAR_PROPS) { - GPU_shader_uniform_vector(shader, material->partscalarpropsloc, 4, 1, pi->scalprops); - } - if (material->builtins & GPU_PARTICLE_LOCATION) { - GPU_shader_uniform_vector(shader, material->partcoloc, 4, 1, pi->location); - } - if (material->builtins & GPU_PARTICLE_VELOCITY) { - GPU_shader_uniform_vector(shader, material->partvel, 3, 1, pi->velocity); - } - if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) { - GPU_shader_uniform_vector(shader, material->partangvel, 3, 1, pi->angular_velocity); - } - if (material->builtins & GPU_OBJECT_INFO) { - GPU_shader_uniform_vector(shader, material->objectinfoloc, 3, 1, object_info); - } - - } -} - -void GPU_material_unbind(GPUMaterial *material) -{ - if (material->pass) { - material->bound = 0; - GPU_pass_unbind(material->pass); - } -} - -bool GPU_material_bound(GPUMaterial *material) -{ - return material->bound; -} - Scene *GPU_material_scene(GPUMaterial *material) { return material->scene; @@ -490,1654 +189,450 @@ GPUMatType GPU_Material_get_type(GPUMaterial *material) return material->type; } - -void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs) +GPUPass *GPU_material_get_pass(GPUMaterial *material) { - *attribs = material->attribs; + return material->pass; } -void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) +ListBase *GPU_material_get_inputs(GPUMaterial *material) { - if (!material->outlink) - material->outlink = link; + return &material->inputs; } -void GPU_material_enable_alpha(GPUMaterial *material) +GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material) { - material->alpha = 1; + return material->ubo; } -GPUBlendMode GPU_material_alpha_blend(GPUMaterial *material, float obcol[4]) +/** + * Create dynamic UBO from parameters + * \param ListBase of BLI_genericNodeN(GPUInput) + */ +void GPU_material_create_uniform_buffer(GPUMaterial *material, ListBase *inputs) { - if (material->alpha || (material->obcolalpha && obcol[3] < 1.0f)) - return GPU_BLEND_ALPHA; - else - return GPU_BLEND_SOLID; + material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL); } -void gpu_material_add_node(GPUMaterial *material, GPUNode *node) +void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials) { - BLI_addtail(&material->nodes, node); + for (LinkData *link = gpumaterials->first; link; link = link->next) { + GPUMaterial *material = link->data; + if (material->ubo != NULL) { + GPU_uniformbuffer_tag_dirty(material->ubo); + } + if (material->sss_profile != NULL) { + material->sss_dirty = true; + } + } } -/* Code generation */ - -bool GPU_material_do_color_management(GPUMaterial *mat) -{ - if (!BKE_scene_check_color_management_enabled(mat->scene)) - return false; - - return true; -} +/* Eevee Subsurface scattering. */ +/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ -bool GPU_material_use_new_shading_nodes(GPUMaterial *mat) -{ - return BKE_scene_use_new_shading_nodes(mat->scene); -} +#define SSS_SAMPLES 65 +#define SSS_EXPONENT 2.0f /* Importance sampling exponent */ -bool GPU_material_use_world_space_shading(GPUMaterial *mat) -{ - return BKE_scene_use_world_space_shading(mat->scene); -} +typedef struct GPUSssKernelData { + float kernel[SSS_SAMPLES][4]; + float param[3], max_radius; + int samples; +} GPUSssKernelData; -static GPUNodeLink *lamp_get_visibility(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **lv, GPUNodeLink **dist) +static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent) { - GPUNodeLink *visifac; - - /* from get_lamp_visibility */ - if (lamp->type == LA_SUN || lamp->type == LA_HEMI) { - mat->dynproperty |= DYN_LAMP_VEC; - GPU_link(mat, "lamp_visibility_sun_hemi", - GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), lv, dist, &visifac); - return visifac; - } - else { - mat->dynproperty |= DYN_LAMP_CO; - GPU_link(mat, "lamp_visibility_other", - GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_uniform(lamp->dynco, GPU_DYNAMIC_LAMP_DYNCO, lamp->ob), lv, dist, &visifac); - - if (lamp->type == LA_AREA) - return visifac; - - switch (lamp->falloff_type) { - case LA_FALLOFF_CONSTANT: - break; - case LA_FALLOFF_INVLINEAR: - GPU_link(mat, "lamp_falloff_invlinear", - GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), *dist, &visifac); - break; - case LA_FALLOFF_INVSQUARE: - GPU_link(mat, "lamp_falloff_invsquare", - GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), *dist, &visifac); - break; - case LA_FALLOFF_SLIDERS: - GPU_link(mat, "lamp_falloff_sliders", - GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), - GPU_dynamic_uniform(&lamp->att1, GPU_DYNAMIC_LAMP_ATT1, lamp->ob), - GPU_dynamic_uniform(&lamp->att2, GPU_DYNAMIC_LAMP_ATT2, lamp->ob), *dist, &visifac); - break; - case LA_FALLOFF_INVCOEFFICIENTS: - GPU_link(mat, "lamp_falloff_invcoefficients", - GPU_dynamic_uniform(&lamp->coeff_const, GPU_DYNAMIC_LAMP_COEFFCONST, lamp->ob), - GPU_dynamic_uniform(&lamp->coeff_lin, GPU_DYNAMIC_LAMP_COEFFLIN, lamp->ob), - GPU_dynamic_uniform(&lamp->coeff_quad, GPU_DYNAMIC_LAMP_COEFFQUAD, lamp->ob), *dist, &visifac); - break; - case LA_FALLOFF_CURVE: - { - float *array; - int size; - - curvemapping_initialize(lamp->curfalloff); - curvemapping_table_RGBA(lamp->curfalloff, &array, &size); - GPU_link(mat, "lamp_falloff_curve", - GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), - GPU_texture(size, array), *dist, &visifac); - - break; - } - } - - if (lamp->mode & LA_SPHERE) - GPU_link(mat, "lamp_visibility_sphere", - GPU_dynamic_uniform(&lamp->dist, GPU_DYNAMIC_LAMP_DISTANCE, lamp->ob), - *dist, visifac, &visifac); - - if (lamp->type == LA_SPOT) { - GPUNodeLink *inpr; - - if (lamp->mode & LA_SQUARE) { - mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_IMAT; - GPU_link(mat, "lamp_visibility_spot_square", - GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), - GPU_dynamic_uniform((float *)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob), - GPU_dynamic_uniform((float *)lamp->spotvec, GPU_DYNAMIC_LAMP_SPOTSCALE, lamp->ob), *lv, &inpr); - } - else { - mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_IMAT; - GPU_link(mat, "lamp_visibility_spot_circle", - GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), - GPU_dynamic_uniform((float *)lamp->dynimat, GPU_DYNAMIC_LAMP_DYNIMAT, lamp->ob), - GPU_dynamic_uniform((float *)lamp->spotvec, GPU_DYNAMIC_LAMP_SPOTSCALE, lamp->ob), *lv, &inpr); - } - - GPU_link(mat, "lamp_visibility_spot", - GPU_dynamic_uniform(&lamp->spotsi, GPU_DYNAMIC_LAMP_SPOTSIZE, lamp->ob), - GPU_dynamic_uniform(&lamp->spotbl, GPU_DYNAMIC_LAMP_SPOTBLEND, lamp->ob), - inpr, visifac, &visifac); - } - - GPU_link(mat, "lamp_visibility_clamp", visifac, &visifac); - - return visifac; + float step = 2.0f / (float)(count - 1); + for (int i = 0; i < count; i++) { + float o = ((float)i) * step - 1.0f; + float sign = (o < 0.0f) ? -1.0f : 1.0f; + float ofs = sign * fabsf(powf(o, exponent)); + kd->kernel[i][3] = ofs; } } -#if 0 -static void area_lamp_vectors(LampRen *lar) +#define GAUSS_TRUNCATE 12.46f +static float gaussian_profile(float r, float radius) { - float xsize = 0.5f * lar->area_size, ysize = 0.5f * lar->area_sizey; - - /* make it smaller, so area light can be multisampled */ - float multifac = 1.0f / sqrtf((float)lar->ray_totsamp); - xsize *= multifac; - ysize *= multifac; - - /* corner vectors */ - lar->area[0][0] = lar->co[0] - xsize * lar->mat[0][0] - ysize * lar->mat[1][0]; - lar->area[0][1] = lar->co[1] - xsize * lar->mat[0][1] - ysize * lar->mat[1][1]; - lar->area[0][2] = lar->co[2] - xsize * lar->mat[0][2] - ysize * lar->mat[1][2]; - - /* corner vectors */ - lar->area[1][0] = lar->co[0] - xsize * lar->mat[0][0] + ysize * lar->mat[1][0]; - lar->area[1][1] = lar->co[1] - xsize * lar->mat[0][1] + ysize * lar->mat[1][1]; - lar->area[1][2] = lar->co[2] - xsize * lar->mat[0][2] + ysize * lar->mat[1][2]; - - /* corner vectors */ - lar->area[2][0] = lar->co[0] + xsize * lar->mat[0][0] + ysize * lar->mat[1][0]; - lar->area[2][1] = lar->co[1] + xsize * lar->mat[0][1] + ysize * lar->mat[1][1]; - lar->area[2][2] = lar->co[2] + xsize * lar->mat[0][2] + ysize * lar->mat[1][2]; - - /* corner vectors */ - lar->area[3][0] = lar->co[0] + xsize * lar->mat[0][0] - ysize * lar->mat[1][0]; - lar->area[3][1] = lar->co[1] + xsize * lar->mat[0][1] - ysize * lar->mat[1][1]; - lar->area[3][2] = lar->co[2] + xsize * lar->mat[0][2] - ysize * lar->mat[1][2]; - /* only for correction button size, matrix size works on energy */ - lar->areasize = lar->dist * lar->dist / (4.0f * xsize * ysize); -} -#endif - -static void ramp_blend( - GPUMaterial *mat, GPUNodeLink *fac, GPUNodeLink *col1, GPUNodeLink *col2, int type, - GPUNodeLink **r_col) -{ - static const char *names[] = {"mix_blend", "mix_add", "mix_mult", "mix_sub", - "mix_screen", "mix_div", "mix_diff", "mix_dark", "mix_light", - "mix_overlay", "mix_dodge", "mix_burn", "mix_hue", "mix_sat", - "mix_val", "mix_color", "mix_soft", "mix_linear"}; + const float v = radius * radius * (0.25f * 0.25f); + const float Rm = sqrtf(v * GAUSS_TRUNCATE); - GPU_link(mat, names[type], fac, col1, col2, r_col); + if (r >= Rm) { + return 0.0f; + } + return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v); } -static void BKE_colorband_eval_blend( - GPUMaterial *mat, ColorBand *coba, GPUNodeLink *fac, float rampfac, int type, - GPUNodeLink *incol, GPUNodeLink **r_col) +#define BURLEY_TRUNCATE 16.0f +#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) +static float burley_profile(float r, float d) { - GPUNodeLink *tmp, *alpha, *col; - float *array; - int size; - - /* do colorband */ - BKE_colorband_evaluate_table_rgba(coba, &array, &size); - GPU_link(mat, "valtorgb", fac, GPU_texture(size, array), &col, &tmp); - - /* use alpha in fac */ - GPU_link(mat, "mtex_alpha_from_col", col, &alpha); - GPU_link(mat, "math_multiply", alpha, GPU_uniform(&rampfac), &fac); - - /* blending method */ - ramp_blend(mat, fac, incol, col, type, r_col); + float exp_r_3_d = expf(-r / (3.0f * d)); + float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + return (exp_r_d + exp_r_3_d) / (4.0f * d); } -static void ramp_diffuse_result(GPUShadeInput *shi, GPUNodeLink **diff) +static float cubic_profile(float r, float radius, float sharpness) { - Material *ma = shi->mat; - GPUMaterial *mat = shi->gpumat; - - if (!(mat->scene->gm.flag & GAME_GLSL_NO_RAMPS)) { - if (ma->ramp_col) { - if (ma->rampin_col == MA_RAMP_IN_RESULT) { - GPUNodeLink *fac; - GPU_link(mat, "ramp_rgbtobw", *diff, &fac); + float Rm = radius * (1.0f + sharpness); - /* colorband + blend */ - BKE_colorband_eval_blend(mat, ma->ramp_col, fac, ma->rampfac_col, ma->rampblend_col, *diff, diff); - } - } + if (r >= Rm) { + return 0.0f; } -} - -static void add_to_diffuse( - GPUMaterial *mat, Material *ma, GPUShadeInput *shi, GPUNodeLink *is, GPUNodeLink *rgb, - GPUNodeLink **r_diff) -{ - GPUNodeLink *fac, *tmp, *addcol; + /* custom variation with extra sharpness, to match the previous code */ + const float y = 1.0f / (1.0f + sharpness); + float Rmy, ry, ryinv; - if (!(mat->scene->gm.flag & GAME_GLSL_NO_RAMPS) && - ma->ramp_col && (ma->mode & MA_RAMP_COL)) - { - /* MA_RAMP_IN_RESULT is exceptional */ - if (ma->rampin_col == MA_RAMP_IN_RESULT) { - addcol = shi->rgb; - } - else { - /* input */ - switch (ma->rampin_col) { - case MA_RAMP_IN_ENERGY: - GPU_link(mat, "ramp_rgbtobw", rgb, &fac); - break; - case MA_RAMP_IN_SHADER: - fac = is; - break; - case MA_RAMP_IN_NOR: - GPU_link(mat, "vec_math_dot", shi->view, shi->vn, &tmp, &fac); - break; - default: - GPU_link(mat, "set_value_zero", &fac); - break; - } + Rmy = powf(Rm, y); + ry = powf(r, y); + ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f; - /* colorband + blend */ - BKE_colorband_eval_blend(mat, ma->ramp_col, fac, ma->rampfac_col, ma->rampblend_col, shi->rgb, &addcol); - } - } - else - addcol = shi->rgb; + const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy; + const float f = Rmy - ry; + const float num = f * (f * f) * (y * ryinv); - /* output to */ - GPU_link(mat, "shade_madd", *r_diff, rgb, addcol, r_diff); + return (10.0f * num) / (Rmy5 * M_PI); } -static void ramp_spec_result(GPUShadeInput *shi, GPUNodeLink **spec) +static float eval_profile(float r, short falloff_type, float sharpness, float param) { - Material *ma = shi->mat; - GPUMaterial *mat = shi->gpumat; + r = fabsf(r); - if (!(mat->scene->gm.flag & GAME_GLSL_NO_RAMPS) && - ma->ramp_spec && ma->rampin_spec == MA_RAMP_IN_RESULT) + if (falloff_type == SHD_SUBSURFACE_BURLEY || + falloff_type == SHD_SUBSURFACE_RANDOM_WALK) { - GPUNodeLink *fac; - GPU_link(mat, "ramp_rgbtobw", *spec, &fac); - - /* colorband + blend */ - BKE_colorband_eval_blend(mat, ma->ramp_spec, fac, ma->rampfac_spec, ma->rampblend_spec, *spec, spec); + return burley_profile(r, param) / BURLEY_TRUNCATE_CDF; } -} - -static void do_specular_ramp(GPUShadeInput *shi, GPUNodeLink *is, GPUNodeLink *t, GPUNodeLink **spec) -{ - Material *ma = shi->mat; - GPUMaterial *mat = shi->gpumat; - GPUNodeLink *fac, *tmp; - - *spec = shi->specrgb; - - /* MA_RAMP_IN_RESULT is exception */ - if (ma->ramp_spec && (ma->rampin_spec != MA_RAMP_IN_RESULT)) { - - /* input */ - switch (ma->rampin_spec) { - case MA_RAMP_IN_ENERGY: - fac = t; - break; - case MA_RAMP_IN_SHADER: - fac = is; - break; - case MA_RAMP_IN_NOR: - GPU_link(mat, "vec_math_dot", shi->view, shi->vn, &tmp, &fac); - break; - default: - GPU_link(mat, "set_value_zero", &fac); - break; - } - - /* colorband + blend */ - BKE_colorband_eval_blend(mat, ma->ramp_spec, fac, ma->rampfac_spec, ma->rampblend_spec, *spec, spec); + else if (falloff_type == SHD_SUBSURFACE_CUBIC) { + return cubic_profile(r, param, sharpness); + } + else { + return gaussian_profile(r, param); } } -static void add_user_list(ListBase *list, void *data) -{ - LinkData *link = MEM_callocN(sizeof(LinkData), "GPULinkData"); - link->data = data; - BLI_addtail(list, link); -} - -static void shade_light_textures(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **rgb) +/* Resolution for each sample of the precomputed kernel profile */ +#define INTEGRAL_RESOLUTION 32 +static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param) { - for (int i = 0; i < MAX_MTEX; ++i) { - MTex *mtex = lamp->la->mtex[i]; - - if (mtex && mtex->tex && (mtex->tex->type & TEX_IMAGE) && mtex->tex->ima) { - mat->dynproperty |= DYN_LAMP_PERSMAT; + const float range = x1 - x0; + const float step = range / INTEGRAL_RESOLUTION; + float integral = 0.0f; - float one = 1.0f; - GPUNodeLink *tex_rgb; - - GPU_link(mat, "shade_light_texture", - GPU_builtin(GPU_VIEW_POSITION), - GPU_image(mtex->tex->ima, &mtex->tex->iuser, false), - GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob), - &tex_rgb); - texture_rgb_blend(mat, tex_rgb, *rgb, GPU_uniform(&one), GPU_uniform(&mtex->colfac), mtex->blendtype, rgb); - } + for (int i = 0; i < INTEGRAL_RESOLUTION; ++i) { + float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION; + float y = eval_profile(x, falloff_type, sharpness, param); + integral += y * step; } + + return integral; } +#undef INTEGRAL_RESOLUTION -static void shade_one_light(GPUShadeInput *shi, GPUShadeResult *shr, GPULamp *lamp) +static void compute_sss_kernel( + GPUSssKernelData *kd, float *radii, int sample_ct, int falloff_type, float sharpness) { - Material *ma = shi->mat; - GPUMaterial *mat = shi->gpumat; - GPUNodeLink *lv, *dist, *is, *inp, *i; - GPUNodeLink *outcol, *specfac, *t, *shadfac = NULL, *lcol; - float one = 1.0f; - - if ((lamp->mode & LA_ONLYSHADOW) && !(ma->mode & MA_SHADOW)) - return; + float rad[3]; + /* Minimum radius */ + rad[0] = MAX2(radii[0], 1e-15f); + rad[1] = MAX2(radii[1], 1e-15f); + rad[2] = MAX2(radii[2], 1e-15f); - GPUNodeLink *vn = shi->vn; - GPUNodeLink *view = shi->view; + /* Christensen-Burley fitting */ + float l[3], d[3]; - GPUNodeLink *visifac = lamp_get_visibility(mat, lamp, &lv, &dist); - -#if 0 - if (ma->mode & MA_TANGENT_V) - GPU_link(mat, "shade_tangent_v", lv, GPU_attribute(CD_TANGENT, ""), &vn); -#endif - - GPU_link(mat, "shade_inp", vn, lv, &inp); + if (falloff_type == SHD_SUBSURFACE_BURLEY || + falloff_type == SHD_SUBSURFACE_RANDOM_WALK) + { + mul_v3_v3fl(l, rad, 0.25f * M_1_PI); + const float A = 1.0f; + const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); + /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */ + mul_v3_v3fl(d, l, 0.6f / s); + mul_v3_v3fl(rad, d, BURLEY_TRUNCATE); + kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - if (lamp->mode & LA_NO_DIFF) { - GPU_link(mat, "shade_is_no_diffuse", &is); + copy_v3_v3(kd->param, d); } - else if (lamp->type == LA_HEMI) { - GPU_link(mat, "shade_is_hemi", inp, &is); + else if (falloff_type == SHD_SUBSURFACE_CUBIC) { + copy_v3_v3(kd->param, rad); + mul_v3_fl(rad, 1.0f + sharpness); + kd->max_radius = MAX3(rad[0], rad[1], rad[2]); } else { - if (lamp->type == LA_AREA) { - float area[4][4] = {{0.0f}}, areasize = 0.0f; - - mat->dynproperty |= DYN_LAMP_VEC | DYN_LAMP_CO; - GPU_link(mat, "shade_inp_area", - GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_uniform(lamp->dynco, GPU_DYNAMIC_LAMP_DYNCO, lamp->ob), - GPU_dynamic_uniform(lamp->dynvec, GPU_DYNAMIC_LAMP_DYNVEC, lamp->ob), vn, - GPU_uniform((float *)area), - GPU_uniform(&areasize), - GPU_uniform(&lamp->k), &inp); - } + kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - is = inp; /* Lambert */ - - if (!(mat->scene->gm.flag & GAME_GLSL_NO_SHADERS)) { - if (ma->diff_shader == MA_DIFF_ORENNAYAR) - GPU_link(mat, "shade_diffuse_oren_nayer", inp, vn, lv, view, - GPU_uniform(&ma->roughness), &is); - else if (ma->diff_shader == MA_DIFF_TOON) - GPU_link(mat, "shade_diffuse_toon", vn, lv, view, - GPU_uniform(&ma->param[0]), GPU_uniform(&ma->param[1]), &is); - else if (ma->diff_shader == MA_DIFF_MINNAERT) - GPU_link(mat, "shade_diffuse_minnaert", inp, vn, view, - GPU_uniform(&ma->darkness), &is); - else if (ma->diff_shader == MA_DIFF_FRESNEL) - GPU_link(mat, "shade_diffuse_fresnel", vn, lv, view, - GPU_uniform(&ma->param[0]), GPU_uniform(&ma->param[1]), &is); - } + copy_v3_v3(kd->param, rad); } - if (!(mat->scene->gm.flag & GAME_GLSL_NO_SHADERS)) - if (ma->shade_flag & MA_CUBIC) - GPU_link(mat, "shade_cubic", is, &is); - - i = is; - GPU_link(mat, "shade_visifac", i, visifac, shi->refl, &i); - - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(lamp->dyncol, GPU_DYNAMIC_LAMP_DYNCOL, lamp->ob), &lcol); - shade_light_textures(mat, lamp, &lcol); - GPU_link(mat, "shade_mul_value_v3", - GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob), lcol, &lcol); + /* Compute samples locations on the 1d kernel [-1..1] */ + sss_calculate_offsets(kd, sample_ct, SSS_EXPONENT); -#if 0 - if (ma->mode & MA_TANGENT_VN) - GPU_link(mat, "shade_tangent_v_spec", GPU_attribute(CD_TANGENT, ""), &vn); -#endif - - /* this replaces if (i > 0.0) conditional until that is supported */ - /* done in shade_visifac now, GPU_link(mat, "mtex_value_clamp_positive", i, &i); */ - - if ((ma->mode & MA_SHADOW) && GPU_lamp_has_shadow_buffer(lamp)) { - if (!(mat->scene->gm.flag & GAME_GLSL_NO_SHADOWS)) { - mat->dynproperty |= DYN_LAMP_PERSMAT; + /* Weights sum for normalization */ + float sum[3] = {0.0f, 0.0f, 0.0f}; - if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) { - GPU_link(mat, "test_shadowbuf_vsm", - GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob), - GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob), - GPU_uniform(&lamp->bias), GPU_uniform(&lamp->la->bleedbias), inp, &shadfac); - } - else { - GPU_link(mat, "test_shadowbuf", - GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob), - GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob), - GPU_uniform(&lamp->bias), inp, &shadfac); - } + /* Compute integral of each sample footprint */ + for (int i = 0; i < sample_ct; i++) { + float x0, x1; - if (lamp->mode & LA_ONLYSHADOW) { - GPUNodeLink *shadrgb; - GPU_link(mat, "shade_only_shadow", i, shadfac, - GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob), - GPU_uniform(lamp->shadow_color), &shadrgb); - - if (!(lamp->mode & LA_NO_DIFF)) { - GPU_link(mat, "shade_only_shadow_diffuse", shadrgb, shi->rgb, - shr->diff, &shr->diff); - } - - if (!(lamp->mode & LA_NO_SPEC)) { - GPU_link(mat, "shade_only_shadow_specular", shadrgb, shi->specrgb, - shr->spec, &shr->spec); - } - - add_user_list(&mat->lamps, lamp); - add_user_list(&lamp->materials, shi->gpumat->ma); - return; - } + if (i == 0) { + x0 = kd->kernel[0][3] - fabsf(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f; } - } - else if ((mat->scene->gm.flag & GAME_GLSL_NO_SHADOWS) && (lamp->mode & LA_ONLYSHADOW)) { - add_user_list(&mat->lamps, lamp); - add_user_list(&lamp->materials, shi->gpumat->ma); - return; - } - else - GPU_link(mat, "set_value", GPU_uniform(&one), &shadfac); - - if (GPU_link_changed(shi->refl) || ma->ref != 0.0f) { - if (!(lamp->mode & LA_NO_DIFF)) { - GPUNodeLink *rgb; - GPU_link(mat, "shade_mul_value", i, lcol, &rgb); - GPU_link(mat, "mtex_value_invert", shadfac, &shadfac); - GPU_link(mat, "mix_mult", shadfac, rgb, GPU_uniform(lamp->shadow_color), &rgb); - GPU_link(mat, "mtex_value_invert", shadfac, &shadfac); - add_to_diffuse(mat, ma, shi, is, rgb, &shr->diff); + else { + x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f; } - } - if (mat->scene->gm.flag & GAME_GLSL_NO_SHADERS) { - /* pass */ - } - else if (!(lamp->mode & LA_NO_SPEC) && !(lamp->mode & LA_ONLYSHADOW) && - (GPU_link_changed(shi->spec) || ma->spec != 0.0f)) - { - if (lamp->type == LA_HEMI) { - GPU_link(mat, "shade_hemi_spec", vn, lv, view, GPU_uniform(&ma->spec), shi->har, visifac, &t); - GPU_link(mat, "shade_add_spec", t, lcol, shi->specrgb, &outcol); - GPU_link(mat, "shade_add_clamped", shr->spec, outcol, &shr->spec); + if (i == sample_ct - 1) { + x1 = kd->kernel[sample_ct - 1][3] + fabsf(kd->kernel[sample_ct - 2][3] - kd->kernel[sample_ct - 1][3]) / 2.0f; } else { - if (ma->spec_shader == MA_SPEC_PHONG) { - GPU_link(mat, "shade_phong_spec", vn, lv, view, shi->har, &specfac); - } - else if (ma->spec_shader == MA_SPEC_COOKTORR) { - GPU_link(mat, "shade_cooktorr_spec", vn, lv, view, shi->har, &specfac); - } - else if (ma->spec_shader == MA_SPEC_BLINN) { - GPU_link(mat, "shade_blinn_spec", vn, lv, view, - GPU_uniform(&ma->refrac), shi->har, &specfac); - } - else if (ma->spec_shader == MA_SPEC_WARDISO) { - GPU_link(mat, "shade_wardiso_spec", vn, lv, view, - GPU_uniform(&ma->rms), &specfac); - } - else { - GPU_link(mat, "shade_toon_spec", vn, lv, view, - GPU_uniform(&ma->param[2]), GPU_uniform(&ma->param[3]), &specfac); - } - - if (lamp->type == LA_AREA) - GPU_link(mat, "shade_spec_area_inp", specfac, inp, &specfac); - - GPU_link(mat, "shade_spec_t", shadfac, shi->spec, visifac, specfac, &t); - - if (ma->mode & MA_RAMP_SPEC) { - GPUNodeLink *spec; - do_specular_ramp(shi, specfac, t, &spec); - GPU_link(mat, "shade_add_spec", t, lcol, spec, &outcol); - GPU_link(mat, "shade_add_clamped", shr->spec, outcol, &shr->spec); - } - else { - GPU_link(mat, "shade_add_spec", t, lcol, shi->specrgb, &outcol); - GPU_link(mat, "shade_add_clamped", shr->spec, outcol, &shr->spec); - } + x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f; } - } - add_user_list(&mat->lamps, lamp); - add_user_list(&lamp->materials, shi->gpumat->ma); -} - -static void material_lights(GPUShadeInput *shi, GPUShadeResult *shr) -{ - Base *base; - Scene *sce_iter; + x0 *= kd->max_radius; + x1 *= kd->max_radius; - for (SETLOOPER(shi->gpumat->scene, sce_iter, base)) { - Object *ob = base->object; + kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]); + kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]); + kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]); - if (ob->type == OB_LAMP) { - GPULamp *lamp = GPU_lamp_from_blender(shi->gpumat->scene, ob, NULL); - if (lamp) - shade_one_light(shi, shr, lamp); - } - - if (ob->transflag & OB_DUPLI) { - ListBase *lb = object_duplilist(G.main->eval_ctx, shi->gpumat->scene, ob); - - for (DupliObject *dob = lb->first; dob; dob = dob->next) { - Object *ob_iter = dob->ob; - - if (ob_iter->type == OB_LAMP) { - float omat[4][4]; - copy_m4_m4(omat, ob_iter->obmat); - copy_m4_m4(ob_iter->obmat, dob->mat); - - GPULamp *lamp = GPU_lamp_from_blender(shi->gpumat->scene, ob_iter, ob); - if (lamp) - shade_one_light(shi, shr, lamp); + sum[0] += kd->kernel[i][0]; + sum[1] += kd->kernel[i][1]; + sum[2] += kd->kernel[i][2]; + } - copy_m4_m4(ob_iter->obmat, omat); - } + for (int i = 0; i < 3; ++i) { + if (sum[i] > 0.0f) { + /* Normalize */ + for (int j = 0; j < sample_ct; j++) { + kd->kernel[j][i] /= sum[i]; } - - free_object_duplilist(lb); + } + else { + /* Avoid 0 kernel sum. */ + kd->kernel[sample_ct / 2][i] = 1.0f; } } - /* prevent only shadow lamps from producing negative colors.*/ - GPU_link(shi->gpumat, "shade_clamp_positive", shr->spec, &shr->spec); - GPU_link(shi->gpumat, "shade_clamp_positive", shr->diff, &shr->diff); -} - -static void texture_rgb_blend( - GPUMaterial *mat, GPUNodeLink *tex, GPUNodeLink *out, GPUNodeLink *fact, GPUNodeLink *facg, - int blendtype, GPUNodeLink **in) -{ - switch (blendtype) { - case MTEX_BLEND: - GPU_link(mat, "mtex_rgb_blend", out, tex, fact, facg, in); - break; - case MTEX_MUL: - GPU_link(mat, "mtex_rgb_mul", out, tex, fact, facg, in); - break; - case MTEX_SCREEN: - GPU_link(mat, "mtex_rgb_screen", out, tex, fact, facg, in); - break; - case MTEX_OVERLAY: - GPU_link(mat, "mtex_rgb_overlay", out, tex, fact, facg, in); - break; - case MTEX_SUB: - GPU_link(mat, "mtex_rgb_sub", out, tex, fact, facg, in); - break; - case MTEX_ADD: - GPU_link(mat, "mtex_rgb_add", out, tex, fact, facg, in); - break; - case MTEX_DIV: - GPU_link(mat, "mtex_rgb_div", out, tex, fact, facg, in); - break; - case MTEX_DIFF: - GPU_link(mat, "mtex_rgb_diff", out, tex, fact, facg, in); - break; - case MTEX_DARK: - GPU_link(mat, "mtex_rgb_dark", out, tex, fact, facg, in); - break; - case MTEX_LIGHT: - GPU_link(mat, "mtex_rgb_light", out, tex, fact, facg, in); - break; - case MTEX_BLEND_HUE: - GPU_link(mat, "mtex_rgb_hue", out, tex, fact, facg, in); - break; - case MTEX_BLEND_SAT: - GPU_link(mat, "mtex_rgb_sat", out, tex, fact, facg, in); - break; - case MTEX_BLEND_VAL: - GPU_link(mat, "mtex_rgb_val", out, tex, fact, facg, in); - break; - case MTEX_BLEND_COLOR: - GPU_link(mat, "mtex_rgb_color", out, tex, fact, facg, in); - break; - case MTEX_SOFT_LIGHT: - GPU_link(mat, "mtex_rgb_soft", out, tex, fact, facg, in); - break; - case MTEX_LIN_LIGHT: - GPU_link(mat, "mtex_rgb_linear", out, tex, fact, facg, in); - break; - default: - GPU_link(mat, "set_rgb_zero", &in); - break; + /* Put center sample at the start of the array (to sample first) */ + float tmpv[4]; + copy_v4_v4(tmpv, kd->kernel[sample_ct / 2]); + for (int i = sample_ct / 2; i > 0; i--) { + copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]); } -} + copy_v4_v4(kd->kernel[0], tmpv); -static void texture_value_blend( - GPUMaterial *mat, GPUNodeLink *tex, GPUNodeLink *out, GPUNodeLink *fact, GPUNodeLink *facg, - int blendtype, GPUNodeLink **in) -{ - switch (blendtype) { - case MTEX_BLEND: - GPU_link(mat, "mtex_value_blend", out, tex, fact, facg, in); - break; - case MTEX_MUL: - GPU_link(mat, "mtex_value_mul", out, tex, fact, facg, in); - break; - case MTEX_SCREEN: - GPU_link(mat, "mtex_value_screen", out, tex, fact, facg, in); - break; - case MTEX_SUB: - GPU_link(mat, "mtex_value_sub", out, tex, fact, facg, in); - break; - case MTEX_ADD: - GPU_link(mat, "mtex_value_add", out, tex, fact, facg, in); - break; - case MTEX_DIV: - GPU_link(mat, "mtex_value_div", out, tex, fact, facg, in); - break; - case MTEX_DIFF: - GPU_link(mat, "mtex_value_diff", out, tex, fact, facg, in); - break; - case MTEX_DARK: - GPU_link(mat, "mtex_value_dark", out, tex, fact, facg, in); - break; - case MTEX_LIGHT: - GPU_link(mat, "mtex_value_light", out, tex, fact, facg, in); - break; - default: - GPU_link(mat, "set_value_zero", &in); - break; - } + kd->samples = sample_ct; } -static void do_material_tex(GPUShadeInput *shi) +#define INTEGRAL_RESOLUTION 512 +static void compute_sss_translucence_kernel( + const GPUSssKernelData *kd, int resolution, short falloff_type, float sharpness, float **output) { - Material *ma = shi->mat; - GPUMaterial *mat = shi->gpumat; - MTex *mtex; - Tex *tex; - GPUNodeLink *texco, *tin, *trgb, *tnor, *tcol, *stencil, *tnorfac; - GPUNodeLink *texco_norm, *texco_orco, *texco_object; - GPUNodeLink *texco_global, *texco_uv = NULL; - GPUNodeLink *newnor, *orn; - float one = 1.0f; - int rgbnor, talpha; - bool init_done = false; - int iBumpSpacePrev = 0; /* Not necessary, quieting gcc warning. */ - GPUNodeLink *vNorg, *vNacc, *fPrevMagnitude; - int iFirstTimeNMap = 1; - bool found_deriv_map = false; - - GPU_link(mat, "set_value", GPU_uniform(&one), &stencil); - - GPU_link(mat, "texco_norm", GPU_builtin(GPU_VIEW_NORMAL), &texco_norm); - GPU_link(mat, "texco_orco", GPU_attribute(CD_ORCO, ""), &texco_orco); - GPU_link(mat, "texco_object", GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), - GPU_builtin(GPU_VIEW_POSITION), &texco_object); -#if 0 - GPU_link(mat, "texco_tangent", GPU_attribute(CD_TANGENT, ""), &texco_tangent); -#endif - GPU_link(mat, "texco_global", GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - GPU_builtin(GPU_VIEW_POSITION), &texco_global); - - orn = texco_norm; - - /* go over texture slots */ - for (int tex_nr = 0; tex_nr < MAX_MTEX; tex_nr++) { - /* separate tex switching */ - if (ma->septex & (1 << tex_nr)) continue; - - if (ma->mtex[tex_nr]) { - mtex = ma->mtex[tex_nr]; - - tex = mtex->tex; - if (tex == NULL) continue; - - /* which coords */ - if (mtex->texco == TEXCO_ORCO) - texco = texco_orco; - else if (mtex->texco == TEXCO_OBJECT) - texco = texco_object; - else if (mtex->texco == TEXCO_NORM) - texco = orn; - else if (mtex->texco == TEXCO_TANGENT) - texco = texco_object; - else if (mtex->texco == TEXCO_GLOB) - texco = texco_global; - else if (mtex->texco == TEXCO_REFL) { - GPU_link(mat, "texco_refl", shi->vn, shi->view, &shi->ref); - texco = shi->ref; - } - else if (mtex->texco == TEXCO_UV) { - if (1) { //!(texco_uv && strcmp(mtex->uvname, lastuvname) == 0)) { - GPU_link(mat, "texco_uv", GPU_attribute(CD_MTFACE, mtex->uvname), &texco_uv); - /*lastuvname = mtex->uvname;*/ /*UNUSED*/ - } - texco = texco_uv; - } - else - continue; - - /* in case of uv, this would just undo a multiplication in texco_uv */ - if (mtex->texco != TEXCO_UV) - GPU_link(mat, "mtex_2d_mapping", texco, &texco); - - if (mtex->size[0] != 1.0f || mtex->size[1] != 1.0f || mtex->size[2] != 1.0f) - GPU_link(mat, "mtex_mapping_size", texco, GPU_uniform(mtex->size), &texco); - - float ofs[3] = { - mtex->ofs[0] + 0.5f - 0.5f * mtex->size[0], - mtex->ofs[1] + 0.5f - 0.5f * mtex->size[1], - 0.0f - }; - - if (ofs[0] != 0.0f || ofs[1] != 0.0f || ofs[2] != 0.0f) - GPU_link(mat, "mtex_mapping_ofs", texco, GPU_uniform(ofs), &texco); - - talpha = 0; - - if (tex && tex->ima && - ((tex->type == TEX_IMAGE) || - ((tex->type == TEX_ENVMAP) && (mtex->texco == TEXCO_REFL)))) - { - if (tex->type == TEX_IMAGE) { - GPU_link(mat, "mtex_image", texco, GPU_image(tex->ima, &tex->iuser, false), &tin, &trgb); - } - else { - GPU_link(mat, "mtex_cube_map_refl", - GPU_cube_map(tex->ima, &tex->iuser, false), shi->view, shi->vn, - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - GPU_builtin(GPU_VIEW_MATRIX), &tin, &trgb); - } - rgbnor = TEX_RGB; - - talpha = ((tex->imaflag & TEX_USEALPHA) && tex->ima && (tex->ima->flag & IMA_IGNORE_ALPHA) == 0); - } - else { - continue; - } + float (*texels)[4]; + texels = MEM_callocN(sizeof(float) * 4 * resolution, "compute_sss_translucence_kernel"); + *output = (float *)texels; - /* texture output */ - if ((rgbnor & TEX_RGB) && (mtex->texflag & MTEX_RGBTOINT)) { - GPU_link(mat, "mtex_rgbtoint", trgb, &tin); - rgbnor -= TEX_RGB; - } + /* Last texel should be black, hence the - 1. */ + for (int i = 0; i < resolution - 1; ++i) { + /* Distance from surface. */ + float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution); - if (mtex->texflag & MTEX_NEGATIVE) { - if (rgbnor & TEX_RGB) - GPU_link(mat, "mtex_rgb_invert", trgb, &trgb); - else - GPU_link(mat, "mtex_value_invert", tin, &tin); - } + /* For each distance d we compute the radiance incomming from an hypothetic parallel plane. */ + /* Compute radius of the footprint on the hypothetic plane */ + float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d); + float r_step = r_fp / INTEGRAL_RESOLUTION; + float area_accum = 0.0f; + for (float r = 0.0f; r < r_fp; r += r_step) { + /* Compute distance to the "shading" point through the medium. */ + /* r_step * 0.5f to put sample between the area borders */ + float dist = hypotf(r + r_step * 0.5f, d); - if (mtex->texflag & MTEX_STENCIL) { - if (rgbnor & TEX_RGB) - GPU_link(mat, "mtex_rgb_stencil", stencil, trgb, &stencil, &trgb); - else - GPU_link(mat, "mtex_value_stencil", stencil, tin, &stencil, &tin); - } + float profile[3]; + profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]); + profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]); + profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]); - /* mapping */ - if (mtex->mapto & (MAP_COL | MAP_COLSPEC | MAP_COLMIR)) { - /* stencil maps on the texture control slider, not texture intensity value */ - if ((rgbnor & TEX_RGB) == 0) { - GPU_link(mat, "set_rgb", GPU_uniform(&mtex->r), &tcol); - } - else { - GPU_link(mat, "set_rgba", trgb, &tcol); - - if (mtex->mapto & MAP_ALPHA) - GPU_link(mat, "set_value", stencil, &tin); - else if (talpha) - GPU_link(mat, "mtex_alpha_from_col", trgb, &tin); - else - GPU_link(mat, "set_value_one", &tin); - } - - if ((tex->type == TEX_IMAGE) || - ((tex->type == TEX_ENVMAP) && (mtex->texco == TEXCO_REFL))) - { - if (GPU_material_do_color_management(mat)) { - GPU_link(mat, "srgb_to_linearrgb", tcol, &tcol); - } - } - - if (mtex->mapto & MAP_COL) { - GPUNodeLink *colfac; - - if (mtex->colfac == 1.0f) colfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->colfac), stencil, &colfac); - - texture_rgb_blend(mat, tcol, shi->rgb, tin, colfac, mtex->blendtype, &shi->rgb); - } - - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && (mtex->mapto & MAP_COLSPEC)) { - GPUNodeLink *colspecfac; - - if (mtex->colspecfac == 1.0f) colspecfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->colspecfac), stencil, &colspecfac); - - texture_rgb_blend(mat, tcol, shi->specrgb, tin, colspecfac, mtex->blendtype, &shi->specrgb); - } - - if (mtex->mapto & MAP_COLMIR) { - GPUNodeLink *colmirfac; - - if (mtex->mirrfac == 1.0f) colmirfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->mirrfac), stencil, &colmirfac); - - /* exception for envmap only */ - if (tex->type == TEX_ENVMAP && mtex->blendtype == MTEX_BLEND) { - GPU_link(mat, "mtex_mirror", tcol, shi->refcol, tin, colmirfac, &shi->refcol); - } - else - texture_rgb_blend(mat, tcol, shi->mir, tin, colmirfac, mtex->blendtype, &shi->mir); - } - } - - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && (mtex->mapto & MAP_NORM)) { - if (tex->type == TEX_IMAGE) { - found_deriv_map = tex->imaflag & TEX_DERIVATIVEMAP; - - if (tex->imaflag & TEX_NORMALMAP) { - /* normalmap image */ - GPU_link(mat, "mtex_normal", texco, GPU_image(tex->ima, &tex->iuser, true), &tnor); - - if (mtex->norfac < 0.0f) - GPU_link(mat, "mtex_negate_texnormal", tnor, &tnor); - - if (mtex->normapspace == MTEX_NSPACE_TANGENT) { - if (iFirstTimeNMap != 0) { - // use unnormalized normal (this is how we bake it - closer to gamedev) - GPUNodeLink *vNegNorm; - GPU_link(mat, "vec_math_negate", - GPU_builtin(GPU_VIEW_NORMAL), &vNegNorm); - GPU_link(mat, "mtex_nspace_tangent", - GPU_attribute(CD_TANGENT, ""), vNegNorm, tnor, &newnor); - iFirstTimeNMap = 0; - } - else { /* otherwise use accumulated perturbations */ - GPU_link(mat, "mtex_nspace_tangent", - GPU_attribute(CD_TANGENT, ""), shi->vn, tnor, &newnor); - } - } - else if (mtex->normapspace == MTEX_NSPACE_OBJECT) { - /* transform normal by object then view matrix */ - GPU_link(mat, "mtex_nspace_object", tnor, &newnor); - } - else if (mtex->normapspace == MTEX_NSPACE_WORLD) { - /* transform normal by view matrix */ - GPU_link(mat, "mtex_nspace_world", GPU_builtin(GPU_VIEW_MATRIX), tnor, &newnor); - } - else { - /* no transform, normal in camera space */ - newnor = tnor; - } - - float norfac = min_ff(fabsf(mtex->norfac), 1.0f); - - if (norfac == 1.0f && !GPU_link_changed(stencil)) { - shi->vn = newnor; - } - else { - tnorfac = GPU_uniform(&norfac); - - if (GPU_link_changed(stencil)) - GPU_link(mat, "math_multiply", tnorfac, stencil, &tnorfac); - - GPU_link(mat, "mtex_blend_normal", tnorfac, shi->vn, newnor, &shi->vn); - } - - } - else if (found_deriv_map || - (mtex->texflag & (MTEX_3TAP_BUMP | MTEX_5TAP_BUMP | MTEX_BICUBIC_BUMP))) - { - /* ntap bumpmap image */ - int iBumpSpace; - float ima_x, ima_y; - - float imag_tspace_dimension_x = 1024.0f; /* only used for texture space variant */ - float aspect = 1.0f; - - GPUNodeLink *vR1, *vR2; - GPUNodeLink *dBs, *dBt, *fDet; - - float hScale = 0.1f; /* compatibility adjustment factor for all bumpspace types */ - if (mtex->texflag & MTEX_BUMP_TEXTURESPACE) - hScale = 13.0f; /* factor for scaling texspace bumps */ - else if (found_deriv_map) - hScale = 1.0f; - - /* resolve texture resolution */ - if ((mtex->texflag & MTEX_BUMP_TEXTURESPACE) || found_deriv_map) { - ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, &tex->iuser, NULL); - ima_x = 512.0f; ima_y = 512.0f; /* prevent calling textureSize, glsl 1.3 only */ - if (ibuf) { - ima_x = ibuf->x; - ima_y = ibuf->y; - aspect = (float)ima_y / ima_x; - } - BKE_image_release_ibuf(tex->ima, ibuf, NULL); - } - - /* The negate on norfac is done because the - * normal in the renderer points inward which corresponds - * to inverting the bump map. Should this ever change - * this negate must be removed. */ - float norfac = -hScale * mtex->norfac; - if (found_deriv_map) { - float fVirtDim = sqrtf(fabsf(ima_x * mtex->size[0] * ima_y * mtex->size[1])); - norfac /= MAX2(fVirtDim, FLT_EPSILON); - } - - tnorfac = GPU_uniform(&norfac); - - if (found_deriv_map) - GPU_link(mat, "math_multiply", tnorfac, GPU_builtin(GPU_AUTO_BUMPSCALE), &tnorfac); - - if (GPU_link_changed(stencil)) - GPU_link(mat, "math_multiply", tnorfac, stencil, &tnorfac); - - if (!init_done) { - /* copy shi->vn to vNorg and vNacc, set magnitude to 1 */ - GPU_link(mat, "mtex_bump_normals_init", shi->vn, &vNorg, &vNacc, &fPrevMagnitude); - iBumpSpacePrev = 0; - init_done = true; - } - - // find current bump space - if (mtex->texflag & MTEX_BUMP_OBJECTSPACE) - iBumpSpace = 1; - else if (mtex->texflag & MTEX_BUMP_TEXTURESPACE) - iBumpSpace = 2; - else - iBumpSpace = 4; /* ViewSpace */ - - /* re-initialize if bump space changed */ - if (iBumpSpacePrev != iBumpSpace) { - GPUNodeLink *surf_pos = GPU_builtin(GPU_VIEW_POSITION); - - if (mtex->texflag & MTEX_BUMP_OBJECTSPACE) - GPU_link(mat, "mtex_bump_init_objspace", - surf_pos, vNorg, - GPU_builtin(GPU_VIEW_MATRIX), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - GPU_builtin(GPU_OBJECT_MATRIX), - GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), - fPrevMagnitude, vNacc, - &fPrevMagnitude, &vNacc, - &vR1, &vR2, &fDet); - - else if (mtex->texflag & MTEX_BUMP_TEXTURESPACE) - GPU_link(mat, "mtex_bump_init_texturespace", - surf_pos, vNorg, - fPrevMagnitude, vNacc, - &fPrevMagnitude, &vNacc, - &vR1, &vR2, &fDet); - - else - GPU_link(mat, "mtex_bump_init_viewspace", - surf_pos, vNorg, - fPrevMagnitude, vNacc, - &fPrevMagnitude, &vNacc, - &vR1, &vR2, &fDet); - - iBumpSpacePrev = iBumpSpace; - } - - - if (found_deriv_map) { - GPU_link(mat, "mtex_bump_deriv", - texco, GPU_image(tex->ima, &tex->iuser, true), - GPU_uniform(&ima_x), GPU_uniform(&ima_y), tnorfac, - &dBs, &dBt); - } - else if (mtex->texflag & MTEX_3TAP_BUMP) - GPU_link(mat, "mtex_bump_tap3", - texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac, - &dBs, &dBt); - else if (mtex->texflag & MTEX_5TAP_BUMP) - GPU_link(mat, "mtex_bump_tap5", - texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac, - &dBs, &dBt); - else if (mtex->texflag & MTEX_BICUBIC_BUMP) { - if (GPU_bicubic_bump_support()) { - GPU_link(mat, "mtex_bump_bicubic", - texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac, - &dBs, &dBt); - } - else { - GPU_link(mat, "mtex_bump_tap5", - texco, GPU_image(tex->ima, &tex->iuser, true), tnorfac, - &dBs, &dBt); - } - } - - - if (mtex->texflag & MTEX_BUMP_TEXTURESPACE) { - float imag_tspace_dimension_y = aspect * imag_tspace_dimension_x; - GPU_link(mat, "mtex_bump_apply_texspace", - fDet, dBs, dBt, vR1, vR2, - GPU_image(tex->ima, &tex->iuser, true), texco, - GPU_uniform(&imag_tspace_dimension_x), - GPU_uniform(&imag_tspace_dimension_y), vNacc, - &vNacc, &shi->vn); - } - else - GPU_link(mat, "mtex_bump_apply", - fDet, dBs, dBt, vR1, vR2, vNacc, - &vNacc, &shi->vn); - - } - } - - GPU_link(mat, "vec_math_negate", shi->vn, &orn); - } + /* Since the profile and configuration are radially symetrical we + * can just evaluate it once and weight it accordingly */ + float r_next = r + r_step; + float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r); - if ((mtex->mapto & MAP_VARS)) { - if (rgbnor & TEX_RGB) { - if (talpha) - GPU_link(mat, "mtex_alpha_from_col", trgb, &tin); - else - GPU_link(mat, "mtex_rgbtoint", trgb, &tin); - } - - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_REF) { - GPUNodeLink *difffac; - - if (mtex->difffac == 1.0f) difffac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->difffac), stencil, &difffac); - - texture_value_blend( - mat, GPU_uniform(&mtex->def_var), shi->refl, tin, difffac, - mtex->blendtype, &shi->refl); - GPU_link(mat, "mtex_value_clamp_positive", shi->refl, &shi->refl); - } - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_SPEC) { - GPUNodeLink *specfac; - - if (mtex->specfac == 1.0f) specfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->specfac), stencil, &specfac); - - texture_value_blend( - mat, GPU_uniform(&mtex->def_var), shi->spec, tin, specfac, - mtex->blendtype, &shi->spec); - GPU_link(mat, "mtex_value_clamp_positive", shi->spec, &shi->spec); - } - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_EMIT) { - GPUNodeLink *emitfac; - - if (mtex->emitfac == 1.0f) emitfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->emitfac), stencil, &emitfac); - - texture_value_blend( - mat, GPU_uniform(&mtex->def_var), shi->emit, tin, emitfac, - mtex->blendtype, &shi->emit); - GPU_link(mat, "mtex_value_clamp_positive", shi->emit, &shi->emit); - } - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_HAR) { - GPUNodeLink *hardfac; - - if (mtex->hardfac == 1.0f) hardfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->hardfac), stencil, &hardfac); - - GPU_link(mat, "mtex_har_divide", shi->har, &shi->har); - texture_value_blend( - mat, GPU_uniform(&mtex->def_var), shi->har, tin, hardfac, - mtex->blendtype, &shi->har); - GPU_link(mat, "mtex_har_multiply_clamp", shi->har, &shi->har); - } - if (mtex->mapto & MAP_ALPHA) { - GPUNodeLink *alphafac; - - if (mtex->alphafac == 1.0f) alphafac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->alphafac), stencil, &alphafac); - - texture_value_blend( - mat, GPU_uniform(&mtex->def_var), shi->alpha, tin, alphafac, - mtex->blendtype, &shi->alpha); - GPU_link(mat, "mtex_value_clamp", shi->alpha, &shi->alpha); - } - if (!(mat->scene->gm.flag & GAME_GLSL_NO_EXTRA_TEX) && mtex->mapto & MAP_AMB) { - GPUNodeLink *ambfac; - - if (mtex->ambfac == 1.0f) ambfac = stencil; - else GPU_link(mat, "math_multiply", GPU_uniform(&mtex->ambfac), stencil, &ambfac); - - texture_value_blend( - mat, GPU_uniform(&mtex->def_var), shi->amb, tin, ambfac, - mtex->blendtype, &shi->amb); - GPU_link(mat, "mtex_value_clamp", shi->amb, &shi->amb); - } - } + mul_v3_fl(profile, disk_area); + add_v3_v3(texels[i], profile); + area_accum += disk_area; } + /* Normalize over the disk. */ + mul_v3_fl(texels[i], 1.0f / (area_accum)); } -} -void GPU_shadeinput_set(GPUMaterial *mat, Material *ma, GPUShadeInput *shi) -{ - float one = 1.0f; - - memset(shi, 0, sizeof(*shi)); - - shi->gpumat = mat; - shi->mat = ma; - - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->r, GPU_DYNAMIC_MAT_DIFFRGB, ma), &shi->rgb); - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->specr, GPU_DYNAMIC_MAT_SPECRGB, ma), &shi->specrgb); - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->mirr, GPU_DYNAMIC_MAT_MIR, ma), &shi->mir); - GPU_link(mat, "set_rgba_zero", &shi->refcol); - GPU_link(mat, "shade_norm", GPU_builtin(GPU_VIEW_NORMAL), &shi->vn); - - if (mat->alpha) - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->alpha, GPU_DYNAMIC_MAT_ALPHA, ma), &shi->alpha); - else - GPU_link(mat, "set_value", GPU_uniform(&one), &shi->alpha); - - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->ref, GPU_DYNAMIC_MAT_REF, ma), &shi->refl); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->spec, GPU_DYNAMIC_MAT_SPEC, ma), &shi->spec); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->emit, GPU_DYNAMIC_MAT_EMIT, ma), &shi->emit); - GPU_link(mat, "set_value", GPU_dynamic_uniform((float *)&ma->har, GPU_DYNAMIC_MAT_HARD, ma), &shi->har); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->amb, GPU_DYNAMIC_MAT_AMB, ma), &shi->amb); - GPU_link(mat, "set_value", GPU_uniform(&ma->spectra), &shi->spectra); - GPU_link(mat, "shade_view", GPU_builtin(GPU_VIEW_POSITION), &shi->view); - GPU_link(mat, "vcol_attribute", GPU_attribute(CD_MCOL, ""), &shi->vcol); - if (GPU_material_do_color_management(mat)) - GPU_link(mat, "srgb_to_linearrgb", shi->vcol, &shi->vcol); - GPU_link(mat, "texco_refl", shi->vn, shi->view, &shi->ref); -} + /* Normalize */ + for (int j = resolution - 2; j > 0; j--) { + texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f; + texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f; + texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f; + } -void GPU_mist_update_enable(short enable) -{ - GPUWorld.mistenabled = (float)enable; -} + /* First texel should be white */ + texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f; + texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f; + texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f; -void GPU_mist_update_values(int type, float start, float dist, float inten, float color[3]) -{ - GPUWorld.mistype = (float)type; - GPUWorld.miststart = start; - GPUWorld.mistdistance = dist; - GPUWorld.mistintensity = inten; - copy_v3_v3(GPUWorld.mistcol, color); - GPUWorld.mistcol[3] = 1.0f; + /* dim the last few texels for smoother transition */ + mul_v3_fl(texels[resolution - 2], 0.25f); + mul_v3_fl(texels[resolution - 3], 0.5f); + mul_v3_fl(texels[resolution - 4], 0.75f); } +#undef INTEGRAL_RESOLUTION -void GPU_horizon_update_color(float color[3]) +void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short *falloff_type, float *sharpness) { - copy_v3_v3(GPUWorld.horicol, color); -} + material->sss_radii = radii; + material->sss_falloff = falloff_type; + material->sss_sharpness = sharpness; + material->sss_dirty = true; -void GPU_ambient_update_color(float color[3]) -{ - copy_v3_v3(GPUWorld.ambcol, color); - GPUWorld.ambcol[3] = 1.0f; -} - -void GPU_zenith_update_color(float color[3]) -{ - copy_v3_v3(GPUWorld.zencol, color); + /* Update / Create UBO */ + if (material->sss_profile == NULL) { + material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + } } -void GPU_shaderesult_set(GPUShadeInput *shi, GPUShadeResult *shr) +struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct, GPUTexture **tex_profile) { - GPUMaterial *mat = shi->gpumat; - GPUNodeLink *emit, *ulinfac, *ulogfac, *mistfac; - Material *ma = shi->mat; - World *world = mat->scene->world; - float linfac, logfac; - - memset(shr, 0, sizeof(*shr)); - - if (ma->mode & MA_VERTEXCOLP) - shi->rgb = shi->vcol; - - do_material_tex(shi); - - if ((mat->scene->gm.flag & GAME_GLSL_NO_LIGHTS) || (ma->mode & MA_SHLESS)) { - GPU_link(mat, "set_rgb", shi->rgb, &shr->diff); - GPU_link(mat, "set_rgb_zero", &shr->spec); - GPU_link(mat, "set_value", shi->alpha, &shr->alpha); - shr->combined = shr->diff; - } - else { - if (GPU_link_changed(shi->emit) || ma->emit != 0.0f) { - if ((ma->mode & (MA_VERTEXCOL | MA_VERTEXCOLP)) == MA_VERTEXCOL) { - GPU_link(mat, "shade_add", shi->emit, shi->vcol, &emit); - GPU_link(mat, "shade_mul", emit, shi->rgb, &shr->diff); - } - else - GPU_link(mat, "shade_mul_value", shi->emit, shi->rgb, &shr->diff); - } - else - GPU_link(mat, "set_rgb_zero", &shr->diff); - - GPU_link(mat, "set_rgb_zero", &shr->spec); + if (material->sss_radii == NULL) + return NULL; - material_lights(shi, shr); + if (material->sss_dirty || (material->sss_samples != sample_ct)) { + GPUSssKernelData kd; - shr->combined = shr->diff; + float sharpness = (material->sss_sharpness != NULL) ? *material->sss_sharpness : 0.0f; - GPU_link(mat, "set_value", shi->alpha, &shr->alpha); + /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */ + sharpness *= 0.5f; - if (world) { - /* exposure correction */ - if (world->exp != 0.0f || world->range != 1.0f) { - linfac = 1.0f + powf((2.0f * world->exp + 0.5f), -10); - logfac = logf((linfac - 1.0f) / linfac) / world->range; + compute_sss_kernel(&kd, material->sss_radii, sample_ct, *material->sss_falloff, sharpness); - GPU_link(mat, "set_value", GPU_uniform(&linfac), &ulinfac); - GPU_link(mat, "set_value", GPU_uniform(&logfac), &ulogfac); - - GPU_link(mat, "shade_exposure_correct", shr->combined, - ulinfac, ulogfac, &shr->combined); - GPU_link(mat, "shade_exposure_correct", shr->spec, - ulinfac, ulogfac, &shr->spec); - } - - /* environment lighting */ - if (!(mat->scene->gm.flag & GAME_GLSL_NO_ENV_LIGHTING) && - (world->mode & WO_ENV_LIGHT) && - (mat->scene->r.mode & R_SHADOW) && - !BKE_scene_use_new_shading_nodes(mat->scene)) - { - if ((world->ao_env_energy != 0.0f) && (GPU_link_changed(shi->amb) || ma->amb != 0.0f) && - (GPU_link_changed(shi->refl) || ma->ref != 0.0f)) - { - if (world->aocolor != WO_AOPLAIN) { - if (!(is_zero_v3(&world->horr) & is_zero_v3(&world->zenr))) { - GPUNodeLink *fcol, *f; - GPU_link(mat, "math_multiply", shi->amb, shi->refl, &f); - GPU_link(mat, "math_multiply", f, GPU_uniform(&world->ao_env_energy), &f); - GPU_link(mat, "shade_mul_value", f, shi->rgb, &fcol); - GPU_link(mat, "env_apply", shr->combined, - GPU_dynamic_uniform(GPUWorld.horicol, GPU_DYNAMIC_HORIZON_COLOR, NULL), - GPU_dynamic_uniform(GPUWorld.zencol, GPU_DYNAMIC_ZENITH_COLOR, NULL), fcol, - GPU_builtin(GPU_VIEW_MATRIX), shi->vn, &shr->combined); - } - } - else { - GPUNodeLink *f; - GPU_link(mat, "math_multiply", shi->amb, shi->refl, &f); - GPU_link(mat, "math_multiply", f, GPU_uniform(&world->ao_env_energy), &f); - GPU_link(mat, "shade_maddf", shr->combined, f, shi->rgb, &shr->combined); - } - } - } + /* Update / Create UBO */ + GPU_uniformbuffer_update(material->sss_profile, &kd); - /* ambient color */ - if (GPU_link_changed(shi->amb) || ma->amb != 0.0f) { - GPU_link(mat, "shade_maddf", shr->combined, GPU_uniform(&ma->amb), - GPU_dynamic_uniform(GPUWorld.ambcol, GPU_DYNAMIC_AMBIENT_COLOR, NULL), - &shr->combined); - } - } + /* Update / Create Tex */ + float *translucence_profile; + compute_sss_translucence_kernel(&kd, 64, *material->sss_falloff, sharpness, &translucence_profile); - if (ma->mode & MA_TRANSP && (ma->mode & (MA_ZTRANSP | MA_RAYTRANSP))) { - if (GPU_link_changed(shi->spectra) || ma->spectra != 0.0f) { - GPU_link(mat, "alpha_spec_correction", shr->spec, shi->spectra, - shi->alpha, &shr->alpha); - } + if (material->sss_tex_profile != NULL) { + GPU_texture_free(material->sss_tex_profile); } - if (ma->mode & MA_RAMP_COL) ramp_diffuse_result(shi, &shr->combined); - if (ma->mode & MA_RAMP_SPEC) ramp_spec_result(shi, &shr->spec); + material->sss_tex_profile = GPU_texture_create_1D(64, GPU_RGBA16F, translucence_profile, NULL); - if (GPU_link_changed(shi->refcol)) - GPU_link(mat, "shade_add_mirror", shi->mir, shi->refcol, shr->combined, &shr->combined); + MEM_freeN(translucence_profile); - if (GPU_link_changed(shi->spec) || ma->spec != 0.0f) - GPU_link(mat, "shade_add", shr->combined, shr->spec, &shr->combined); + material->sss_samples = sample_ct; + material->sss_dirty = false; } - GPU_link(mat, "mtex_alpha_to_col", shr->combined, shr->alpha, &shr->combined); - - if (ma->shade_flag & MA_OBCOLOR) - GPU_link(mat, "shade_obcolor", shr->combined, GPU_builtin(GPU_OBCOLOR), &shr->combined); - - if (!(ma->mode & MA_NOMIST)) { - GPU_link(mat, "shade_mist_factor", GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_uniform(&GPUWorld.mistenabled, GPU_DYNAMIC_MIST_ENABLE, NULL), - GPU_dynamic_uniform(&GPUWorld.miststart, GPU_DYNAMIC_MIST_START, NULL), - GPU_dynamic_uniform(&GPUWorld.mistdistance, GPU_DYNAMIC_MIST_DISTANCE, NULL), - GPU_dynamic_uniform(&GPUWorld.mistype, GPU_DYNAMIC_MIST_TYPE, NULL), - GPU_dynamic_uniform(&GPUWorld.mistintensity, GPU_DYNAMIC_MIST_INTENSITY, NULL), &mistfac); - - GPU_link(mat, "mix_blend", mistfac, shr->combined, - GPU_dynamic_uniform(GPUWorld.mistcol, GPU_DYNAMIC_MIST_COLOR, NULL), &shr->combined); - } - - if (!mat->alpha) { - if (world && (GPU_link_changed(shr->alpha) || ma->alpha != 1.0f)) - GPU_link(mat, "shade_world_mix", GPU_dynamic_uniform(GPUWorld.horicol, GPU_DYNAMIC_HORIZON_COLOR, NULL), - shr->combined, &shr->combined); - - GPU_link(mat, "shade_alpha_opaque", shr->combined, &shr->combined); - } - - if (ma->shade_flag & MA_OBCOLOR) { - mat->obcolalpha = 1; - GPU_link(mat, "shade_alpha_obcolor", shr->combined, GPU_builtin(GPU_OBCOLOR), &shr->combined); + if (tex_profile != NULL) { + *tex_profile = material->sss_tex_profile; } + return material->sss_profile; } -static GPUNodeLink *GPU_blender_material(GPUMaterial *mat, Material *ma) -{ - GPUShadeInput shi; - GPUShadeResult shr; +#undef SSS_EXPONENT +#undef SSS_SAMPLES - GPU_shadeinput_set(mat, ma, &shi); - GPU_shaderesult_set(&shi, &shr); - - return shr.combined; +void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs) +{ + *attribs = material->attribs; } -static GPUNodeLink *gpu_material_diffuse_bsdf(GPUMaterial *mat, Material *ma) +void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) { - static float roughness = 0.0f; - GPUNodeLink *outlink; - - GPU_link(mat, "node_bsdf_diffuse", - GPU_uniform(&ma->r), GPU_uniform(&roughness), GPU_builtin(GPU_VIEW_NORMAL), &outlink); - - return outlink; + if (!material->outlink) + material->outlink = link; } -static GPUNodeLink *gpu_material_preview_matcap(GPUMaterial *mat, Material *ma) +void gpu_material_add_node(GPUMaterial *material, GPUNode *node) { - GPUNodeLink *outlink; - - /* some explanations here: - * matcap normal holds the normal remapped to the 0.0 - 1.0 range. To take advantage of flat shading, we abuse - * the built in secondary color of opengl. Color is just the regular color, which should include mask value too. - * This also needs flat shading so we use the primary opengl color built-in */ - GPU_link(mat, "material_preview_matcap", GPU_uniform(&ma->r), GPU_image_preview(ma->preview), - GPU_opengl_builtin(GPU_MATCAP_NORMAL), GPU_opengl_builtin(GPU_COLOR), &outlink); - - return outlink; + BLI_addtail(&material->nodes, node); } -/* new solid draw mode with glsl matcaps */ -GPUMaterial *GPU_material_matcap(Scene *scene, Material *ma, bool use_opensubdiv) +/* Return true if the material compilation has not yet begin or begin. */ +GPUMaterialStatus GPU_material_status(GPUMaterial *mat) { - GPUMaterial *mat; - GPUNodeLink *outlink; - LinkData *link; - - for (link = ma->gpumaterial.first; link; link = link->next) { - GPUMaterial *current_material = (GPUMaterial *)link->data; - if (current_material->scene == scene && - current_material->is_opensubdiv == use_opensubdiv) - { - return current_material; - } - } - - /* allocate material */ - mat = GPU_material_construct_begin(ma); - mat->scene = scene; - mat->type = GPU_MATERIAL_TYPE_MESH; - mat->is_opensubdiv = use_opensubdiv; - - if (ma->preview && ma->preview->rect[0]) { - outlink = gpu_material_preview_matcap(mat, ma); - } - else { - outlink = gpu_material_diffuse_bsdf(mat, ma); - } - - GPU_material_output_link(mat, outlink); - - gpu_material_construct_end(mat, "matcap_pass"); + return mat->status; +} - /* note that even if building the shader fails in some way, we still keep - * it to avoid trying to compile again and again, and simple do not use - * the actual shader on drawing */ +/* Code generation */ - link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); - link->data = mat; - BLI_addtail(&ma->gpumaterial, link); +bool GPU_material_do_color_management(GPUMaterial *mat) +{ + if (!BKE_scene_check_color_management_enabled(mat->scene)) + return false; - return mat; + return true; } -static void do_world_tex(GPUShadeInput *shi, struct World *wo, GPUNodeLink **hor, GPUNodeLink **zen, GPUNodeLink **blend) +bool GPU_material_use_domain_surface(GPUMaterial *mat) { - GPUMaterial *mat = shi->gpumat; - GPUNodeLink *texco, *tin, *trgb, *stencil, *tcol, *zenfac; - MTex *mtex; - Tex *tex; - float ofs[3], zero = 0.0f; - int tex_nr, rgbnor; - - GPU_link(mat, "set_value_one", &stencil); - /* go over texture slots */ - for (tex_nr = 0; tex_nr < MAX_MTEX; tex_nr++) { - if (wo->mtex[tex_nr]) { - mtex = wo->mtex[tex_nr]; - tex = mtex->tex; - if (tex == NULL || !tex->ima || (tex->type != TEX_IMAGE && tex->type != TEX_ENVMAP)) - continue; - /* which coords */ - if (mtex->texco == TEXCO_VIEW || mtex->texco == TEXCO_GLOB) { - if (tex->type == TEX_IMAGE) - texco = GPU_builtin(GPU_VIEW_POSITION); - else if (tex->type == TEX_ENVMAP) - GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &texco); - } - else if (mtex->texco == TEXCO_EQUIRECTMAP || mtex->texco == TEXCO_ANGMAP) { - if ((tex->type == TEX_IMAGE && wo->skytype & WO_SKYREAL) || tex->type == TEX_ENVMAP) - GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &texco); - else - texco = GPU_builtin(GPU_VIEW_POSITION); - } - else - continue; - GPU_link(mat, "texco_norm", texco, &texco); - if (tex->type == TEX_IMAGE && !(wo->skytype & WO_SKYREAL)) { - GPU_link(mat, "mtex_2d_mapping", texco, &texco); - } - if (mtex->size[0] != 1.0f || mtex->size[1] != 1.0f || mtex->size[2] != 1.0f) { - float size[3] = { mtex->size[0], mtex->size[1], mtex->size[2] }; - if (tex->type == TEX_ENVMAP) { - size[1] = mtex->size[2]; - size[2] = mtex->size[1]; - } - GPU_link(mat, "mtex_mapping_size", texco, GPU_uniform(size), &texco); - } - ofs[0] = mtex->ofs[0] + 0.5f - 0.5f * mtex->size[0]; - if (tex->type == TEX_ENVMAP) { - ofs[1] = -mtex->ofs[2] + 0.5f - 0.5f * mtex->size[2]; - ofs[2] = mtex->ofs[1] + 0.5f - 0.5f * mtex->size[1]; - } - else { - ofs[1] = mtex->ofs[1] + 0.5f - 0.5f * mtex->size[1]; - ofs[2] = 0.0; - } - if (ofs[0] != 0.0f || ofs[1] != 0.0f || ofs[2] != 0.0f) - GPU_link(mat, "mtex_mapping_ofs", texco, GPU_uniform(ofs), &texco); - if (mtex->texco == TEXCO_EQUIRECTMAP) { - GPU_link(mat, "node_tex_environment_equirectangular", texco, GPU_image(tex->ima, &tex->iuser, false), &trgb); - } - else if (mtex->texco == TEXCO_ANGMAP) { - GPU_link(mat, "node_tex_environment_mirror_ball", texco, GPU_image(tex->ima, &tex->iuser, false), &trgb); - } - else { - if (tex->type == TEX_ENVMAP) - GPU_link(mat, "mtex_cube_map", texco, GPU_cube_map(tex->ima, &tex->iuser, false), &tin, &trgb); - else if (tex->type == TEX_IMAGE) - GPU_link(mat, "mtex_image", texco, GPU_image(tex->ima, &tex->iuser, false), &tin, &trgb); - } - rgbnor = TEX_RGB; - if (tex->type == TEX_IMAGE || tex->type == TEX_ENVMAP) - if (GPU_material_do_color_management(mat)) - GPU_link(mat, "srgb_to_linearrgb", trgb, &trgb); - /* texture output */ - if ((rgbnor & TEX_RGB) && (mtex->texflag & MTEX_RGBTOINT)) { - GPU_link(mat, "mtex_rgbtoint", trgb, &tin); - rgbnor -= TEX_RGB; - } - if (mtex->texflag & MTEX_NEGATIVE) { - if (rgbnor & TEX_RGB) - GPU_link(mat, "mtex_rgb_invert", trgb, &trgb); - else - GPU_link(mat, "mtex_value_invert", tin, &tin); - } - if (mtex->texflag & MTEX_STENCIL) { - if (rgbnor & TEX_RGB) - GPU_link(mat, "mtex_rgb_stencil", stencil, trgb, &stencil, &trgb); - else - GPU_link(mat, "mtex_value_stencil", stencil, tin, &stencil, &tin); - } - else { - if (rgbnor & TEX_RGB) - GPU_link(mat, "mtex_alpha_multiply_value", trgb, stencil, &trgb); - else - GPU_link(mat, "math_multiply", stencil, tin, &tin); - } - /* color mapping */ - if (mtex->mapto & (WOMAP_HORIZ + WOMAP_ZENUP + WOMAP_ZENDOWN)) { - if ((rgbnor & TEX_RGB) == 0) - GPU_link(mat, "set_rgb", GPU_uniform(&mtex->r), &trgb); - else - GPU_link(mat, "mtex_alpha_from_col", trgb, &tin); - GPU_link(mat, "set_rgb", trgb, &tcol); - if (mtex->mapto & WOMAP_HORIZ) { - texture_rgb_blend(mat, tcol, *hor, tin, GPU_uniform(&mtex->colfac), mtex->blendtype, hor); - } - if (mtex->mapto & (WOMAP_ZENUP + WOMAP_ZENDOWN)) { - GPU_link(mat, "set_value_zero", &zenfac); - if (wo->skytype & WO_SKYREAL) { - if (mtex->mapto & WOMAP_ZENUP) { - if (mtex->mapto & WOMAP_ZENDOWN) { - GPU_link(mat, "world_zen_mapping", shi->view, GPU_uniform(&mtex->zenupfac), - GPU_uniform(&mtex->zendownfac), &zenfac); - } - else { - GPU_link(mat, "world_zen_mapping", shi->view, GPU_uniform(&mtex->zenupfac), - GPU_uniform(&zero), &zenfac); - } - } - else if (mtex->mapto & WOMAP_ZENDOWN) { - GPU_link(mat, "world_zen_mapping", shi->view, GPU_uniform(&zero), - GPU_uniform(&mtex->zendownfac), &zenfac); - } - } - else { - if (mtex->mapto & WOMAP_ZENUP) - GPU_link(mat, "set_value", GPU_uniform(&mtex->zenupfac), &zenfac); - else if (mtex->mapto & WOMAP_ZENDOWN) - GPU_link(mat, "set_value", GPU_uniform(&mtex->zendownfac), &zenfac); - } - texture_rgb_blend(mat, tcol, *zen, tin, zenfac, mtex->blendtype, zen); - } - } - if (mtex->mapto & WOMAP_BLEND && wo->skytype & WO_SKYBLEND) { - if (rgbnor & TEX_RGB) - GPU_link(mat, "mtex_rgbtoint", trgb, &tin); - texture_value_blend(mat, GPU_uniform(&mtex->def_var), *blend, tin, GPU_uniform(&mtex->blendfac), mtex->blendtype, blend); - } - } - } + return (mat->domain & GPU_DOMAIN_SURFACE); } -static void gpu_material_old_world(struct GPUMaterial *mat, struct World *wo) +bool GPU_material_use_domain_volume(GPUMaterial *mat) { - GPUShadeInput shi; - GPUShadeResult shr; - GPUNodeLink *hor, *zen, *ray, *blend; - - shi.gpumat = mat; + return (mat->domain & GPU_DOMAIN_VOLUME); +} - for (int i = 0; i < MAX_MTEX; i++) { - if (wo->mtex[i] && wo->mtex[i]->tex) { - wo->skytype |= WO_SKYTEX; - break; - } - } - if ((wo->skytype & (WO_SKYBLEND + WO_SKYTEX)) == 0) { - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&wo->horr, GPU_DYNAMIC_HORIZON_COLOR, NULL), &shr.combined); - } - else { - GPU_link(mat, "set_rgb_zero", &shi.rgb); - GPU_link(mat, "background_transform_to_world", GPU_builtin(GPU_VIEW_POSITION), &ray); - if (wo->skytype & WO_SKYPAPER) - GPU_link(mat, "world_paper_view", GPU_builtin(GPU_VIEW_POSITION), &shi.view); - else - GPU_link(mat, "shade_view", ray, &shi.view); - if (wo->skytype & WO_SKYBLEND) { - if (wo->skytype & WO_SKYPAPER) { - if (wo->skytype & WO_SKYREAL) - GPU_link(mat, "world_blend_paper_real", GPU_builtin(GPU_VIEW_POSITION), &blend); - else - GPU_link(mat, "world_blend_paper", GPU_builtin(GPU_VIEW_POSITION), &blend); - } - else { - if (wo->skytype & WO_SKYREAL) - GPU_link(mat, "world_blend_real", ray, &blend); - else - GPU_link(mat, "world_blend", ray, &blend); - } - } - else { - GPU_link(mat, "set_value_zero", &blend); +GPUMaterial *GPU_material_from_nodetree_find( + ListBase *gpumaterials, const void *engine_type, int options) +{ + for (LinkData *link = gpumaterials->first; link; link = link->next) { + GPUMaterial *current_material = (GPUMaterial *)link->data; + if (current_material->engine_type == engine_type && + current_material->options == options) + { + return current_material; } - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&wo->horr, GPU_DYNAMIC_HORIZON_COLOR, NULL), &hor); - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&wo->zenr, GPU_DYNAMIC_ZENITH_COLOR, NULL), &zen); - do_world_tex(&shi, wo, &hor, &zen, &blend); - if (wo->skytype & WO_SKYBLEND) - GPU_link(mat, "node_mix_shader", blend, hor, zen, &shi.rgb); - else - GPU_link(mat, "set_rgb", hor, &shi.rgb); - GPU_link(mat, "set_rgb", shi.rgb, &shr.combined); } - GPU_material_output_link(mat, shr.combined); + + return NULL; } -GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo) +/** + * \note Caller must use #GPU_material_from_nodetree_find to re-use existing materials, + * This is enforced since constructing other arguments to this function may be expensive + * so only do this when they are needed. + */ +GPUMaterial *GPU_material_from_nodetree( + Scene *scene, struct bNodeTree *ntree, ListBase *gpumaterials, const void *engine_type, int options) { LinkData *link; - GPUMaterial *mat; + bool has_volume_output, has_surface_output; - for (link = wo->gpumaterial.first; link; link = link->next) - if (((GPUMaterial *)link->data)->scene == scene) - return link->data; + /* Caller must re-use materials. */ + BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL); /* allocate material */ - mat = GPU_material_construct_begin(NULL); + GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");; mat->scene = scene; - mat->type = GPU_MATERIAL_TYPE_WORLD; + mat->engine_type = engine_type; + mat->options = options; - /* create nodes */ - if (BKE_scene_use_new_shading_nodes(scene) && wo->nodetree && wo->use_nodes) { - ntreeGPUMaterialNodes(wo->nodetree, mat, NODE_NEW_SHADING); + ntreeGPUMaterialNodes(ntree, mat, NODE_NEW_SHADING | NODE_NEWER_SHADING); + ntreeGPUMaterialDomain(ntree, &has_surface_output, &has_volume_output); + + if (has_surface_output) { + mat->domain |= GPU_DOMAIN_SURFACE; } - else { - gpu_material_old_world(mat, wo); + if (has_volume_output) { + mat->domain |= GPU_DOMAIN_VOLUME; } - if (GPU_material_do_color_management(mat)) - if (mat->outlink) - GPU_link(mat, "linearrgb_to_srgb", mat->outlink, &mat->outlink); - - gpu_material_construct_end(mat, wo->id.name); + if (mat->outlink) { + /* Prune the unused nodes and extract attribs before compiling so the + * generated VBOs are ready to accept the future shader. */ + GPU_nodes_prune(&mat->nodes, mat->outlink); + GPU_nodes_get_vertex_attributes(&mat->nodes, &mat->attribs); + mat->status = GPU_MAT_QUEUED; + } /* note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simple do not use @@ -2145,80 +640,27 @@ GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo) link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); link->data = mat; - BLI_addtail(&wo->gpumaterial, link); + BLI_addtail(gpumaterials, link); return mat; } - -GPUMaterial *GPU_material_from_blender(Scene *scene, Material *ma, bool use_opensubdiv) +void GPU_material_generate_pass( + GPUMaterial *mat, const char *vert_code, const char *geom_code, const char *frag_lib, const char *defines) { - GPUMaterial *mat; - GPUNodeLink *outlink; - LinkData *link; - - for (link = ma->gpumaterial.first; link; link = link->next) { - GPUMaterial *current_material = (GPUMaterial *)link->data; - if (current_material->scene == scene && - current_material->is_opensubdiv == use_opensubdiv) - { - return current_material; - } - } - - /* allocate material */ - mat = GPU_material_construct_begin(ma); - mat->scene = scene; - mat->type = GPU_MATERIAL_TYPE_MESH; - mat->is_opensubdiv = use_opensubdiv; - - /* render pipeline option */ - bool new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); - if (!new_shading_nodes && (ma->mode & MA_TRANSP)) - GPU_material_enable_alpha(mat); - else if (new_shading_nodes && ma->alpha < 1.0f) - GPU_material_enable_alpha(mat); - - if (!(scene->gm.flag & GAME_GLSL_NO_NODES) && ma->nodetree && ma->use_nodes) { - /* create nodes */ - if (new_shading_nodes) - ntreeGPUMaterialNodes(ma->nodetree, mat, NODE_NEW_SHADING); - else - ntreeGPUMaterialNodes(ma->nodetree, mat, NODE_OLD_SHADING); + BLI_assert(mat->pass == NULL); /* Only run once! */ + if (mat->outlink) { + mat->pass = GPU_generate_pass_new( + mat, mat->outlink, &mat->attribs, &mat->nodes, &mat->inputs, vert_code, geom_code, frag_lib, defines); + mat->status = (mat->pass) ? GPU_MAT_SUCCESS : GPU_MAT_FAILED; } else { - if (new_shading_nodes) { - /* create simple diffuse material instead of nodes */ - outlink = gpu_material_diffuse_bsdf(mat, ma); - } - else { - /* create blender material */ - outlink = GPU_blender_material(mat, ma); - } - - GPU_material_output_link(mat, outlink); + mat->status = GPU_MAT_FAILED; } - - if (GPU_material_do_color_management(mat)) - if (mat->outlink) - GPU_link(mat, "linearrgb_to_srgb", mat->outlink, &mat->outlink); - - gpu_material_construct_end(mat, ma->id.name); - - /* note that even if building the shader fails in some way, we still keep - * it to avoid trying to compile again and again, and simple do not use - * the actual shader on drawing */ - - link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); - link->data = mat; - BLI_addtail(&ma->gpumaterial, link); - - return mat; } void GPU_materials_free(void) { - Object *ob; Material *ma; World *wo; extern Material defmaterial; @@ -2230,684 +672,4 @@ void GPU_materials_free(void) GPU_material_free(&wo->gpumaterial); GPU_material_free(&defmaterial.gpumaterial); - - for (ob = G.main->object.first; ob; ob = ob->id.next) - GPU_lamp_free(ob); -} - -/* Lamps and shadow buffers */ - -static void gpu_lamp_calc_winmat(GPULamp *lamp) -{ - float temp, angle, pixsize, wsize; - - if (lamp->type == LA_SUN) { - wsize = lamp->la->shadow_frustum_size; - orthographic_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend); - } - else if (lamp->type == LA_SPOT) { - angle = saacos(lamp->spotsi); - temp = 0.5f * lamp->size * cosf(angle) / sinf(angle); - pixsize = lamp->d / temp; - wsize = pixsize * 0.5f * lamp->size; - /* compute shadows according to X and Y scaling factors */ - perspective_m4( - lamp->winmat, - -wsize * lamp->spotvec[0], wsize * lamp->spotvec[0], - -wsize * lamp->spotvec[1], wsize * lamp->spotvec[1], - lamp->d, lamp->clipend); - } -} - -void GPU_lamp_update(GPULamp *lamp, int lay, int hide, float obmat[4][4]) -{ - float mat[4][4]; - float obmat_scale[3]; - - lamp->lay = lay; - lamp->hide = hide; - - normalize_m4_m4_ex(mat, obmat, obmat_scale); - - copy_v3_v3(lamp->vec, mat[2]); - copy_v3_v3(lamp->co, mat[3]); - copy_m4_m4(lamp->obmat, mat); - invert_m4_m4(lamp->imat, mat); - - if (lamp->type == LA_SPOT) { - /* update spotlamp scale on X and Y axis */ - lamp->spotvec[0] = obmat_scale[0] / obmat_scale[2]; - lamp->spotvec[1] = obmat_scale[1] / obmat_scale[2]; - } - - if (GPU_lamp_has_shadow_buffer(lamp)) { - /* makeshadowbuf */ - gpu_lamp_calc_winmat(lamp); - } -} - -void GPU_lamp_update_colors(GPULamp *lamp, float r, float g, float b, float energy) -{ - lamp->energy = energy; - if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy; - - lamp->col[0] = r; - lamp->col[1] = g; - lamp->col[2] = b; -} - -void GPU_lamp_update_distance(GPULamp *lamp, float distance, float att1, float att2, - float coeff_const, float coeff_lin, float coeff_quad) -{ - lamp->dist = distance; - lamp->att1 = att1; - lamp->att2 = att2; - lamp->coeff_const = coeff_const; - lamp->coeff_lin = coeff_lin; - lamp->coeff_quad = coeff_quad; -} - -void GPU_lamp_update_spot(GPULamp *lamp, float spotsize, float spotblend) -{ - lamp->spotsi = cosf(spotsize * 0.5f); - lamp->spotbl = (1.0f - lamp->spotsi) * spotblend; } - -static void gpu_lamp_from_blender(Scene *scene, Object *ob, Object *par, Lamp *la, GPULamp *lamp) -{ - lamp->scene = scene; - lamp->ob = ob; - lamp->par = par; - lamp->la = la; - - /* add_render_lamp */ - lamp->mode = la->mode; - lamp->type = la->type; - - lamp->energy = la->energy; - if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy; - - lamp->col[0] = la->r; - lamp->col[1] = la->g; - lamp->col[2] = la->b; - - GPU_lamp_update(lamp, ob->lay, (ob->restrictflag & OB_RESTRICT_RENDER), ob->obmat); - - lamp->spotsi = la->spotsize; - if (lamp->mode & LA_HALO) - if (lamp->spotsi > DEG2RADF(170.0f)) - lamp->spotsi = DEG2RADF(170.0f); - lamp->spotsi = cosf(lamp->spotsi * 0.5f); - lamp->spotbl = (1.0f - lamp->spotsi) * la->spotblend; - lamp->k = la->k; - - lamp->dist = la->dist; - lamp->falloff_type = la->falloff_type; - lamp->att1 = la->att1; - lamp->att2 = la->att2; - lamp->coeff_const = la->coeff_const; - lamp->coeff_lin = la->coeff_lin; - lamp->coeff_quad = la->coeff_quad; - lamp->curfalloff = la->curfalloff; - - /* initshadowbuf */ - lamp->bias = 0.02f * la->bias; - lamp->size = la->bufsize; - lamp->d = la->clipsta; - lamp->clipend = la->clipend; - - /* arbitrary correction for the fact we do no soft transition */ - lamp->bias *= 0.25f; -} - -static void gpu_lamp_shadow_free(GPULamp *lamp) -{ - if (lamp->tex) { - GPU_texture_free(lamp->tex); - lamp->tex = NULL; - } - if (lamp->depthtex) { - GPU_texture_free(lamp->depthtex); - lamp->depthtex = NULL; - } - if (lamp->fb) { - GPU_framebuffer_free(lamp->fb); - lamp->fb = NULL; - } - if (lamp->blurtex) { - GPU_texture_free(lamp->blurtex); - lamp->blurtex = NULL; - } - if (lamp->blurfb) { - GPU_framebuffer_free(lamp->blurfb); - lamp->blurfb = NULL; - } -} - -GPULamp *GPU_lamp_from_blender(Scene *scene, Object *ob, Object *par) -{ - Lamp *la; - GPULamp *lamp; - LinkData *link; - - for (link = ob->gpulamp.first; link; link = link->next) { - lamp = (GPULamp *)link->data; - - if (lamp->par == par && lamp->scene == scene) - return link->data; - } - - lamp = MEM_callocN(sizeof(GPULamp), "GPULamp"); - - link = MEM_callocN(sizeof(LinkData), "GPULampLink"); - link->data = lamp; - BLI_addtail(&ob->gpulamp, link); - - la = ob->data; - gpu_lamp_from_blender(scene, ob, par, la, lamp); - - if ((la->type == LA_SPOT && (la->mode & (LA_SHAD_BUF | LA_SHAD_RAY))) || - (la->type == LA_SUN && (la->mode & LA_SHAD_RAY))) - { - /* opengl */ - lamp->fb = GPU_framebuffer_create(); - if (!lamp->fb) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) { - /* Shadow depth map */ - lamp->depthtex = GPU_texture_create_depth(lamp->size, lamp->size, NULL); - if (!lamp->depthtex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - /* Shadow color map */ - lamp->tex = GPU_texture_create_vsm_shadow_map(lamp->size, NULL); - if (!lamp->tex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - /* FBO and texture for blurring */ - lamp->blurfb = GPU_framebuffer_create(); - if (!lamp->blurfb) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - lamp->blurtex = GPU_texture_create_vsm_shadow_map(lamp->size * 0.5, NULL); - if (!lamp->blurtex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - /* we need to properly bind to test for completeness */ - GPU_texture_bind_as_framebuffer(lamp->blurtex); - - if (!GPU_framebuffer_check_valid(lamp->blurfb, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - GPU_framebuffer_texture_unbind(lamp->blurfb, lamp->blurtex); - } - else { - lamp->tex = GPU_texture_create_depth(lamp->size, lamp->size, NULL); - if (!lamp->tex) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - - if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) { - gpu_lamp_shadow_free(lamp); - return lamp; - } - } - - GPU_framebuffer_restore(); - - lamp->shadow_color[0] = la->shdwr; - lamp->shadow_color[1] = la->shdwg; - lamp->shadow_color[2] = la->shdwb; - } - else { - lamp->shadow_color[0] = 1.0; - lamp->shadow_color[1] = 1.0; - lamp->shadow_color[2] = 1.0; - } - - return lamp; -} - -void GPU_lamp_free(Object *ob) -{ - GPULamp *lamp; - LinkData *link; - LinkData *nlink; - Material *ma; - - for (link = ob->gpulamp.first; link; link = link->next) { - lamp = link->data; - - while (lamp->materials.first) { - nlink = lamp->materials.first; - ma = nlink->data; - BLI_freelinkN(&lamp->materials, nlink); - - if (ma->gpumaterial.first) - GPU_material_free(&ma->gpumaterial); - } - - gpu_lamp_shadow_free(lamp); - - MEM_freeN(lamp); - } - - BLI_freelistN(&ob->gpulamp); -} - -bool GPU_lamp_has_shadow_buffer(GPULamp *lamp) -{ - return (!(lamp->scene->gm.flag & GAME_GLSL_NO_SHADOWS) && - !(lamp->scene->gm.flag & GAME_GLSL_NO_LIGHTS) && - lamp->tex && lamp->fb); -} - -void GPU_lamp_update_buffer_mats(GPULamp *lamp) -{ - float rangemat[4][4], persmat[4][4]; - - /* initshadowbuf */ - invert_m4_m4(lamp->viewmat, lamp->obmat); - normalize_v3(lamp->viewmat[0]); - normalize_v3(lamp->viewmat[1]); - normalize_v3(lamp->viewmat[2]); - - /* makeshadowbuf */ - mul_m4_m4m4(persmat, lamp->winmat, lamp->viewmat); - - /* opengl depth buffer is range 0.0..1.0 instead of -1.0..1.0 in blender */ - unit_m4(rangemat); - rangemat[0][0] = 0.5f; - rangemat[1][1] = 0.5f; - rangemat[2][2] = 0.5f; - rangemat[3][0] = 0.5f; - rangemat[3][1] = 0.5f; - rangemat[3][2] = 0.5f; - - mul_m4_m4m4(lamp->persmat, rangemat, persmat); -} - -void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsize, float winmat[4][4]) -{ - GPU_lamp_update_buffer_mats(lamp); - - /* opengl */ - glDisable(GL_SCISSOR_TEST); - GPU_texture_bind_as_framebuffer(lamp->tex); - if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) - GPU_shader_bind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE)); - - /* set matrices */ - copy_m4_m4(viewmat, lamp->viewmat); - copy_m4_m4(winmat, lamp->winmat); - *winsize = lamp->size; -} - -void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp) -{ - if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) { - GPU_shader_unbind(); - GPU_framebuffer_blur(lamp->fb, lamp->tex, lamp->blurfb, lamp->blurtex); - } - - GPU_framebuffer_texture_unbind(lamp->fb, lamp->tex); - GPU_framebuffer_restore(); - glEnable(GL_SCISSOR_TEST); -} - -int GPU_lamp_shadow_buffer_type(GPULamp *lamp) -{ - return lamp->la->shadowmap_type; -} - -int GPU_lamp_shadow_bind_code(GPULamp *lamp) -{ - return lamp->tex ? GPU_texture_opengl_bindcode(lamp->tex) : -1; -} - -float *GPU_lamp_dynpersmat(GPULamp *lamp) -{ - return &lamp->dynpersmat[0][0]; -} - -int GPU_lamp_shadow_layer(GPULamp *lamp) -{ - if (lamp->fb && lamp->tex && (lamp->mode & (LA_LAYER | LA_LAYER_SHADOW))) - return lamp->lay; - else - return -1; -} - -GPUNodeLink *GPU_lamp_get_data( - GPUMaterial *mat, GPULamp *lamp, - GPUNodeLink **r_col, GPUNodeLink **r_lv, GPUNodeLink **r_dist, GPUNodeLink **r_shadow, GPUNodeLink **r_energy) -{ - GPUNodeLink *visifac; - - *r_col = GPU_dynamic_uniform(lamp->dyncol, GPU_DYNAMIC_LAMP_DYNCOL, lamp->ob); - *r_energy = GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob); - visifac = lamp_get_visibility(mat, lamp, r_lv, r_dist); - - shade_light_textures(mat, lamp, r_col); - - if (GPU_lamp_has_shadow_buffer(lamp)) { - GPUNodeLink *vn, *inp; - - GPU_link(mat, "shade_norm", GPU_builtin(GPU_VIEW_NORMAL), &vn); - GPU_link(mat, "shade_inp", vn, *r_lv, &inp); - mat->dynproperty |= DYN_LAMP_PERSMAT; - - if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) { - GPU_link(mat, "shadows_only_vsm", - GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob), - GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob), - GPU_uniform(&lamp->bias), GPU_uniform(&lamp->la->bleedbias), - GPU_uniform(lamp->shadow_color), inp, r_shadow); - } - else { - GPU_link(mat, "shadows_only", - GPU_builtin(GPU_VIEW_POSITION), - GPU_dynamic_texture(lamp->tex, GPU_DYNAMIC_SAMPLER_2DSHADOW, lamp->ob), - GPU_dynamic_uniform((float *)lamp->dynpersmat, GPU_DYNAMIC_LAMP_DYNPERSMAT, lamp->ob), - GPU_uniform(&lamp->bias), GPU_uniform(lamp->shadow_color), inp, r_shadow); - } - } - else { - GPU_link(mat, "set_rgb_one", r_shadow); - } - - /* ensure shadow buffer and lamp textures will be updated */ - add_user_list(&mat->lamps, lamp); - add_user_list(&lamp->materials, mat->ma); - - return visifac; -} - -/* export the GLSL shader */ - -GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma) -{ - static struct { - GPUBuiltin gputype; - GPUDynamicType dynamictype; - GPUDataType datatype; - } builtins[] = { - { GPU_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_VIEWMAT, GPU_DATA_16F }, - { GPU_INVERSE_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_VIEWIMAT, GPU_DATA_16F }, - { GPU_OBJECT_MATRIX, GPU_DYNAMIC_OBJECT_MAT, GPU_DATA_16F }, - { GPU_INVERSE_OBJECT_MATRIX, GPU_DYNAMIC_OBJECT_IMAT, GPU_DATA_16F }, - { GPU_LOC_TO_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_LOCTOVIEWMAT, GPU_DATA_16F }, - { GPU_INVERSE_LOC_TO_VIEW_MATRIX, GPU_DYNAMIC_OBJECT_LOCTOVIEWIMAT, GPU_DATA_16F }, - { GPU_OBCOLOR, GPU_DYNAMIC_OBJECT_COLOR, GPU_DATA_4F }, - { GPU_AUTO_BUMPSCALE, GPU_DYNAMIC_OBJECT_AUTOBUMPSCALE, GPU_DATA_1F }, - { 0 } - }; - - GPUShaderExport *shader = NULL; - GPUInput *input; - int liblen, fraglen; - - /* TODO(sergey): How to determine whether we need OSD or not here? */ - GPUMaterial *mat = GPU_material_from_blender(scene, ma, false); - GPUPass *pass = (mat) ? mat->pass : NULL; - - if (pass && pass->fragmentcode && pass->vertexcode) { - shader = MEM_callocN(sizeof(GPUShaderExport), "GPUShaderExport"); - - for (input = pass->inputs.first; input; input = input->next) { - GPUInputUniform *uniform = MEM_callocN(sizeof(GPUInputUniform), "GPUInputUniform"); - - if (input->ima) { - /* image sampler uniform */ - uniform->type = GPU_DYNAMIC_SAMPLER_2DIMAGE; - uniform->datatype = GPU_DATA_1I; - uniform->image = input->ima; - uniform->texnumber = input->texid; - BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname)); - } - else if (input->tex) { - /* generated buffer */ - uniform->texnumber = input->texid; - uniform->datatype = GPU_DATA_1I; - BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname)); - - switch (input->textype) { - case GPU_SHADOW2D: - uniform->type = GPU_DYNAMIC_SAMPLER_2DSHADOW; - uniform->lamp = input->dynamicdata; - break; - case GPU_TEX2D: - if (GPU_texture_opengl_bindcode(input->tex)) { - uniform->type = GPU_DYNAMIC_SAMPLER_2DBUFFER; - glBindTexture(GL_TEXTURE_2D, GPU_texture_opengl_bindcode(input->tex)); - uniform->texsize = GPU_texture_width(input->tex) * GPU_texture_height(input->tex); - uniform->texpixels = MEM_mallocN(uniform->texsize * 4, "RGBApixels"); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, uniform->texpixels); - glBindTexture(GL_TEXTURE_2D, 0); - } - break; - - case GPU_NONE: - case GPU_TEXCUBE: - case GPU_FLOAT: - case GPU_VEC2: - case GPU_VEC3: - case GPU_VEC4: - case GPU_MAT3: - case GPU_MAT4: - case GPU_ATTRIB: - break; - } - } - else { - uniform->type = input->dynamictype; - BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname)); - switch (input->type) { - case GPU_FLOAT: - uniform->datatype = GPU_DATA_1F; - break; - case GPU_VEC2: - uniform->datatype = GPU_DATA_2F; - break; - case GPU_VEC3: - uniform->datatype = GPU_DATA_3F; - break; - case GPU_VEC4: - uniform->datatype = GPU_DATA_4F; - break; - case GPU_MAT3: - uniform->datatype = GPU_DATA_9F; - break; - case GPU_MAT4: - uniform->datatype = GPU_DATA_16F; - break; - - case GPU_NONE: - case GPU_TEX2D: - case GPU_TEXCUBE: - case GPU_SHADOW2D: - case GPU_ATTRIB: - break; - } - - if (GPU_DYNAMIC_GROUP_FROM_TYPE(uniform->type) == GPU_DYNAMIC_GROUP_LAMP) - uniform->lamp = input->dynamicdata; - - if (GPU_DYNAMIC_GROUP_FROM_TYPE(uniform->type) == GPU_DYNAMIC_GROUP_MAT) - uniform->material = input->dynamicdata; - } - - if (uniform->type != GPU_DYNAMIC_NONE) - BLI_addtail(&shader->uniforms, uniform); - else - MEM_freeN(uniform); - } - - /* process builtin uniform */ - for (int i = 0; builtins[i].gputype; i++) { - if (mat->builtins & builtins[i].gputype) { - GPUInputUniform *uniform = MEM_callocN(sizeof(GPUInputUniform), "GPUInputUniform"); - uniform->type = builtins[i].dynamictype; - uniform->datatype = builtins[i].datatype; - BLI_strncpy(uniform->varname, GPU_builtin_name(builtins[i].gputype), sizeof(uniform->varname)); - BLI_addtail(&shader->uniforms, uniform); - } - } - - /* now link fragment shader with library shader */ - /* TBD: remove the function that are not used in the main function */ - liblen = (pass->libcode) ? strlen(pass->libcode) : 0; - fraglen = strlen(pass->fragmentcode); - shader->fragment = (char *)MEM_mallocN(liblen + fraglen + 1, "GPUFragShader"); - if (pass->libcode) - memcpy(shader->fragment, pass->libcode, liblen); - memcpy(&shader->fragment[liblen], pass->fragmentcode, fraglen); - shader->fragment[liblen + fraglen] = 0; - - // export the attribute - for (int i = 0; i < mat->attribs.totlayer; i++) { - GPUInputAttribute *attribute = MEM_callocN(sizeof(GPUInputAttribute), "GPUInputAttribute"); - attribute->type = mat->attribs.layer[i].type; - attribute->number = mat->attribs.layer[i].glindex; - BLI_snprintf(attribute->varname, sizeof(attribute->varname), "att%d", mat->attribs.layer[i].attribid); - - switch (attribute->type) { - case CD_TANGENT: - attribute->datatype = GPU_DATA_4F; - break; - case CD_MTFACE: - attribute->datatype = GPU_DATA_2F; - attribute->name = mat->attribs.layer[i].name; - break; - case CD_MCOL: - attribute->datatype = GPU_DATA_4UB; - attribute->name = mat->attribs.layer[i].name; - break; - case CD_ORCO: - attribute->datatype = GPU_DATA_3F; - break; - } - - if (attribute->datatype != GPU_DATA_NONE) - BLI_addtail(&shader->attributes, attribute); - else - MEM_freeN(attribute); - } - - /* export the vertex shader */ - shader->vertex = BLI_strdup(pass->vertexcode); - } - - return shader; -} - -void GPU_free_shader_export(GPUShaderExport *shader) -{ - if (shader == NULL) - return; - - for (GPUInputUniform *uniform = shader->uniforms.first; uniform; uniform = uniform->next) - if (uniform->texpixels) - MEM_freeN(uniform->texpixels); - - BLI_freelistN(&shader->uniforms); - BLI_freelistN(&shader->attributes); - - if (shader->vertex) - MEM_freeN(shader->vertex); - if (shader->fragment) - MEM_freeN(shader->fragment); - - MEM_freeN(shader); -} - -#ifdef WITH_OPENSUBDIV -void GPU_material_update_fvar_offset(GPUMaterial *gpu_material, - DerivedMesh *dm) -{ - GPUPass *pass = gpu_material->pass; - GPUShader *shader = (pass != NULL ? pass->shader : NULL); - ListBase *inputs = (pass != NULL ? &pass->inputs : NULL); - GPUInput *input; - - if (shader == NULL) { - return; - } - - GPU_shader_bind(shader); - - for (input = inputs->first; - input != NULL; - input = input->next) - { - if (input->source == GPU_SOURCE_ATTRIB && - input->attribtype == CD_MTFACE) - { - char name[64]; - /* TODO(sergey): This will work for until names are - * consistent, we'll need to solve this somehow in the future. - */ - int layer_index; - int location; - - if (input->attribname[0] != '\0') { - layer_index = CustomData_get_named_layer(&dm->loopData, - CD_MLOOPUV, - input->attribname); - } - else { - layer_index = CustomData_get_active_layer(&dm->loopData, - CD_MLOOPUV); - } - - BLI_snprintf(name, sizeof(name), - "fvar%d_offset", - input->attribid); - location = GPU_shader_get_uniform(shader, name); - GPU_shader_uniform_int(shader, location, layer_index); - } - } - - GPU_shader_unbind(); -} -#endif diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c new file mode 100644 index 00000000000..b6214f2778b --- /dev/null +++ b/source/blender/gpu/intern/gpu_matrix.c @@ -0,0 +1,649 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the ipmlied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Alexandr Kuznetsov, Jason Wilkins, Mike Erwin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file source/blender/gpu/intern/gpu_matrix.c + * \ingroup gpu + */ + +#include "../../../intern/gawain/gawain/gwn_shader_interface.h" + +#define SUPPRESS_GENERIC_MATRIX_API +#define USE_GPU_PY_MATRIX_API /* only so values are declared */ +#include "GPU_matrix.h" +#undef USE_GPU_PY_MATRIX_API + +#include "BLI_math_matrix.h" +#include "BLI_math_rotation.h" +#include "BLI_math_vector.h" + + +#define DEBUG_MATRIX_BIND 0 + +#define MATRIX_STACK_DEPTH 32 + +typedef float Mat4[4][4]; +typedef float Mat3[3][3]; + +typedef struct MatrixStack { + Mat4 stack[MATRIX_STACK_DEPTH]; + unsigned int top; +} MatrixStack; + +typedef struct { + MatrixStack model_view_stack; + MatrixStack projection_stack; + + bool dirty; + + /* TODO: cache of derived matrices (Normal, MVP, inverse MVP, etc) + * generate as needed for shaders, invalidate when original matrices change + * + * TODO: separate Model from View transform? Batches/objects have model, + * camera/eye has view & projection + */ +} MatrixState; + +#define MATRIX_4X4_IDENTITY {{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}} + +static MatrixState state = { + .model_view_stack = {{MATRIX_4X4_IDENTITY}, 0}, + .projection_stack = {{MATRIX_4X4_IDENTITY}, 0}, + .dirty = true +}; + +#undef MATRIX_4X4_IDENTITY + +#define ModelViewStack state.model_view_stack +#define ModelView ModelViewStack.stack[ModelViewStack.top] + +#define ProjectionStack state.projection_stack +#define Projection ProjectionStack.stack[ProjectionStack.top] + +void gpuMatrixReset(void) +{ + state.model_view_stack.top = 0; + state.projection_stack.top = 0; + unit_m4(ModelView); + unit_m4(Projection); + state.dirty = true; +} + +#ifdef WITH_GPU_SAFETY + +/* Check if matrix is numerically good */ +static void checkmat(cosnt float *m) +{ + const int n = 16; + for (int i = 0; i < n; i++) { +#if _MSC_VER + BLI_assert(_finite(m[i])); +#else + BLI_assert(!isinf(m[i])); +#endif + } +} + +#define CHECKMAT(m) checkmat((const float*)m) + +#else + +#define CHECKMAT(m) + +#endif + + +void gpuPushMatrix(void) +{ + BLI_assert(ModelViewStack.top + 1 < MATRIX_STACK_DEPTH); + ModelViewStack.top++; + copy_m4_m4(ModelView, ModelViewStack.stack[ModelViewStack.top - 1]); +} + +void gpuPopMatrix(void) +{ + BLI_assert(ModelViewStack.top > 0); + ModelViewStack.top--; + state.dirty = true; +} + +void gpuPushProjectionMatrix(void) +{ + BLI_assert(ProjectionStack.top + 1 < MATRIX_STACK_DEPTH); + ProjectionStack.top++; + copy_m4_m4(Projection, ProjectionStack.stack[ProjectionStack.top - 1]); +} + +void gpuPopProjectionMatrix(void) +{ + BLI_assert(ProjectionStack.top > 0); + ProjectionStack.top--; + state.dirty = true; +} + +void gpuLoadMatrix(const float m[4][4]) +{ + copy_m4_m4(ModelView, m); + CHECKMAT(ModelView3D); + state.dirty = true; +} + +void gpuLoadIdentityProjectionMatrix(void) +{ + unit_m4(Projection); + CHECKMAT(Projection3D); + state.dirty = true; +} + +void gpuLoadProjectionMatrix(const float m[4][4]) +{ + copy_m4_m4(Projection, m); + CHECKMAT(Projection3D); + state.dirty = true; +} + +void gpuLoadIdentity(void) +{ + unit_m4(ModelView); + state.dirty = true; +} + +void gpuTranslate2f(float x, float y) +{ + Mat4 m; + unit_m4(m); + m[3][0] = x; + m[3][1] = y; + gpuMultMatrix(m); +} + +void gpuTranslate2fv(const float vec[2]) +{ + gpuTranslate2f(vec[0], vec[1]); +} + +void gpuTranslate3f(float x, float y, float z) +{ +#if 1 + translate_m4(ModelView, x, y, z); + CHECKMAT(ModelView); +#else /* above works well in early testing, below is generic version */ + Mat4 m; + unit_m4(m); + m[3][0] = x; + m[3][1] = y; + m[3][2] = z; + gpuMultMatrix(m); +#endif + state.dirty = true; +} + +void gpuTranslate3fv(const float vec[3]) +{ + gpuTranslate3f(vec[0], vec[1], vec[2]); +} + +void gpuScaleUniform(float factor) +{ + Mat4 m; + scale_m4_fl(m, factor); + gpuMultMatrix(m); +} + +void gpuScale2f(float x, float y) +{ + Mat4 m = {{0.0f}}; + m[0][0] = x; + m[1][1] = y; + m[2][2] = 1.0f; + m[3][3] = 1.0f; + gpuMultMatrix(m); +} + +void gpuScale2fv(const float vec[2]) +{ + gpuScale2f(vec[0], vec[1]); +} + +void gpuScale3f(float x, float y, float z) +{ + Mat4 m = {{0.0f}}; + m[0][0] = x; + m[1][1] = y; + m[2][2] = z; + m[3][3] = 1.0f; + gpuMultMatrix(m); +} + +void gpuScale3fv(const float vec[3]) +{ + gpuScale3f(vec[0], vec[1], vec[2]); +} + +void gpuMultMatrix(const float m[4][4]) +{ + mul_m4_m4_post(ModelView, m); + CHECKMAT(ModelView); + state.dirty = true; +} + +void gpuRotate2D(float deg) +{ + /* essentially RotateAxis('Z') + * TODO: simpler math for 2D case + */ + rotate_m4(ModelView, 'Z', DEG2RADF(deg)); +} + +void gpuRotate3f(float deg, float x, float y, float z) +{ + const float axis[3] = {x, y, z}; + gpuRotate3fv(deg, axis); +} + +void gpuRotate3fv(float deg, const float axis[3]) +{ + Mat4 m; + axis_angle_to_mat4(m, axis, DEG2RADF(deg)); + gpuMultMatrix(m); +} + +void gpuRotateAxis(float deg, char axis) +{ + /* rotate_m4 works in place */ + rotate_m4(ModelView, axis, DEG2RADF(deg)); + CHECKMAT(ModelView); + state.dirty = true; +} + +static void mat4_ortho_set(float m[4][4], float left, float right, float bottom, float top, float near, float far) +{ + m[0][0] = 2.0f / (right - left); + m[1][0] = 0.0f; + m[2][0] = 0.0f; + m[3][0] = -(right + left) / (right - left); + + m[0][1] = 0.0f; + m[1][1] = 2.0f / (top - bottom); + m[2][1] = 0.0f; + m[3][1] = -(top + bottom) / (top - bottom); + + m[0][2] = 0.0f; + m[1][2] = 0.0f; + m[2][2] = -2.0f / (far - near); + m[3][2] = -(far + near) / (far - near); + + m[0][3] = 0.0f; + m[1][3] = 0.0f; + m[2][3] = 0.0f; + m[3][3] = 1.0f; + + state.dirty = true; +} + +static void mat4_frustum_set(float m[4][4], float left, float right, float bottom, float top, float near, float far) +{ + m[0][0] = 2.0f * near / (right - left); + m[1][0] = 0.0f; + m[2][0] = (right + left) / (right - left); + m[3][0] = 0.0f; + + m[0][1] = 0.0f; + m[1][1] = 2.0f * near / (top - bottom); + m[2][1] = (top + bottom) / (top - bottom); + m[3][1] = 0.0f; + + m[0][2] = 0.0f; + m[1][2] = 0.0f; + m[2][2] = -(far + near) / (far - near); + m[3][2] = -2.0f * far * near / (far - near); + + m[0][3] = 0.0f; + m[1][3] = 0.0f; + m[2][3] = -1.0f; + m[3][3] = 0.0f; + + state.dirty = true; +} + +static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3]) +{ +/* This function is loosely based on Mesa implementation. + * + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + + float side[3]; + + normalize_v3(lookdir); + + cross_v3_v3v3(side, lookdir, camup); + + normalize_v3(side); + + cross_v3_v3v3(camup, side, lookdir); + + m[0][0] = side[0]; + m[1][0] = side[1]; + m[2][0] = side[2]; + m[3][0] = 0.0f; + + m[0][1] = camup[0]; + m[1][1] = camup[1]; + m[2][1] = camup[2]; + m[3][1] = 0.0f; + + m[0][2] = -lookdir[0]; + m[1][2] = -lookdir[1]; + m[2][2] = -lookdir[2]; + m[3][2] = 0.0f; + + m[0][3] = 0.0f; + m[1][3] = 0.0f; + m[2][3] = 0.0f; + m[3][3] = 1.0f; + + state.dirty = true; +} + +void gpuOrtho(float left, float right, float bottom, float top, float near, float far) +{ + mat4_ortho_set(Projection, left, right, bottom, top, near, far); + CHECKMAT(Projection); + state.dirty = true; +} + +void gpuOrtho2D(float left, float right, float bottom, float top) +{ + Mat4 m; + mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f); + CHECKMAT(Projection2D); + state.dirty = true; +} + +void gpuFrustum(float left, float right, float bottom, float top, float near, float far) +{ + mat4_frustum_set(Projection, left, right, bottom, top, near, far); + CHECKMAT(Projection); + state.dirty = true; +} + +void gpuPerspective(float fovy, float aspect, float near, float far) +{ + float half_height = tanf(fovy * (float)(M_PI / 360.0)) * near; + float half_width = half_height * aspect; + gpuFrustum(-half_width, +half_width, -half_height, +half_height, near, far); +} + +void gpuLookAt(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ) +{ + Mat4 cm; + float lookdir[3]; + float camup[3] = {upX, upY, upZ}; + + lookdir[0] = centerX - eyeX; + lookdir[1] = centerY - eyeY; + lookdir[2] = centerZ - eyeZ; + + mat4_look_from_origin(cm, lookdir, camup); + + gpuMultMatrix(cm); + gpuTranslate3f(-eyeX, -eyeY, -eyeZ); +} + +void gpuProject(const float world[3], const float model[4][4], const float proj[4][4], const int view[4], float win[3]) +{ + float v[4]; + + mul_v4_m4v3(v, model, world); + mul_m4_v4(proj, v); + + if (v[3] != 0.0f) { + mul_v3_fl(v, 1.0f / v[3]); + } + + win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f; + win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; + win[2] = (v[2] + 1) * 0.5f; +} + +bool gpuUnProject(const float win[3], const float model[4][4], const float proj[4][4], const int view[4], float world[3]) +{ + float pm[4][4]; + float in[4]; + float out[4]; + + mul_m4_m4m4(pm, proj, model); + + if (!invert_m4(pm)) { + zero_v3(world); + return false; + } + + in[0] = win[0]; + in[1] = win[1]; + in[2] = win[2]; + in[3] = 1; + + /* Map x and y from window coordinates */ + in[0] = (in[0] - view[0]) / view[2]; + in[1] = (in[1] - view[1]) / view[3]; + + /* Map to range -1 to +1 */ + in[0] = 2 * in[0] - 1; + in[1] = 2 * in[1] - 1; + in[2] = 2 * in[2] - 1; + + mul_v4_m4v3(out, pm, in); + + if (out[3] == 0.0f) { + copy_v3_v3(world, out); + return false; + } + + mul_v3_v3fl(world, out, 1.0f / out[3]); + return true; +} + +const float (*gpuGetModelViewMatrix(float m[4][4]))[4] +{ + if (m) { + copy_m4_m4(m, ModelView); + return m; + } + else { + return ModelView; + } +} + +const float (*gpuGetProjectionMatrix(float m[4][4]))[4] +{ + if (m) { + copy_m4_m4(m, Projection); + return m; + } + else { + return Projection; + } +} + +const float (*gpuGetModelViewProjectionMatrix(float m[4][4]))[4] +{ + if (m == NULL) { + static Mat4 temp; + m = temp; + } + + mul_m4_m4m4(m, Projection, ModelView); + return m; +} + +const float (*gpuGetNormalMatrix(float m[3][3]))[3] +{ + if (m == NULL) { + static Mat3 temp3; + m = temp3; + } + + copy_m3_m4(m, (const float (*)[4])gpuGetModelViewMatrix(NULL)); + + invert_m3(m); + transpose_m3(m); + + return m; +} + +const float (*gpuGetNormalMatrixInverse(float m[3][3]))[3] +{ + if (m == NULL) { + static Mat3 temp3; + m = temp3; + } + + gpuGetNormalMatrix(m); + invert_m3(m); + + return m; +} + +void gpuBindMatrices(const Gwn_ShaderInterface *shaderface) +{ + /* set uniform values to matrix stack values + * call this before a draw call if desired matrices are dirty + * call glUseProgram before this, as glUniform expects program to be bound + */ + + const Gwn_ShaderInput *MV = GWN_shaderinterface_uniform_builtin(shaderface, GWN_UNIFORM_MODELVIEW); + const Gwn_ShaderInput *P = GWN_shaderinterface_uniform_builtin(shaderface, GWN_UNIFORM_PROJECTION); + const Gwn_ShaderInput *MVP = GWN_shaderinterface_uniform_builtin(shaderface, GWN_UNIFORM_MVP); + + const Gwn_ShaderInput *N = GWN_shaderinterface_uniform_builtin(shaderface, GWN_UNIFORM_NORMAL); + const Gwn_ShaderInput *MV_inv = GWN_shaderinterface_uniform_builtin(shaderface, GWN_UNIFORM_MODELVIEW_INV); + const Gwn_ShaderInput *P_inv = GWN_shaderinterface_uniform_builtin(shaderface, GWN_UNIFORM_PROJECTION_INV); + + if (MV) { +#if DEBUG_MATRIX_BIND + puts("setting MV matrix"); +#endif + + glUniformMatrix4fv(MV->location, 1, GL_FALSE, (const float *)gpuGetModelViewMatrix(NULL)); + } + + if (P) { +#if DEBUG_MATRIX_BIND + puts("setting P matrix"); +#endif + + glUniformMatrix4fv(P->location, 1, GL_FALSE, (const float *)gpuGetProjectionMatrix(NULL)); + } + + if (MVP) { +#if DEBUG_MATRIX_BIND + puts("setting MVP matrix"); +#endif + + glUniformMatrix4fv(MVP->location, 1, GL_FALSE, (const float *)gpuGetModelViewProjectionMatrix(NULL)); + } + + if (N) { +#if DEBUG_MATRIX_BIND + puts("setting normal matrix"); +#endif + + glUniformMatrix3fv(N->location, 1, GL_FALSE, (const float *)gpuGetNormalMatrix(NULL)); + } + + if (MV_inv) { + Mat4 m; + gpuGetModelViewMatrix(m); + invert_m4(m); + glUniformMatrix4fv(MV_inv->location, 1, GL_FALSE, (const float *)m); + } + + if (P_inv) { + Mat4 m; + gpuGetProjectionMatrix(m); + invert_m4(m); + glUniformMatrix4fv(P_inv->location, 1, GL_FALSE, (const float *)m); + } + + state.dirty = false; +} + +bool gpuMatricesDirty(void) +{ + return state.dirty; +} + + +/* -------------------------------------------------------------------- */ + +/** \name Python API Helpers + * \{ */ +BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mismatch"); + +/* Return int since caller is may subtract. */ + +int GPU_matrix_stack_level_get_model_view(void) +{ + return (int)state.model_view_stack.top; +} + +int GPU_matrix_stack_level_get_projection(void) +{ + return (int)state.projection_stack.top; +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 291a552a041..7023e44d289 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -187,12 +187,7 @@ uint GPU_select_end(void) */ bool GPU_select_query_check_active(void) { - return ((U.gpu_select_method == USER_SELECT_USE_OCCLUSION_QUERY) || - ((U.gpu_select_method == USER_SELECT_AUTO) && - (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY) || - /* unsupported by nouveau, gallium 0.4, see: T47940 */ - GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)))); - + return ELEM(U.gpu_select_method, USER_SELECT_USE_OCCLUSION_QUERY, USER_SELECT_AUTO); } /* ---------------------------------------------------------------------------- @@ -232,3 +227,29 @@ bool GPU_select_is_cached(void) { return g_select_state.use_cache && gpu_select_pick_is_cached(); } + + +/* ---------------------------------------------------------------------------- + * Utilities + */ + +/** + * Helper function, nothing special but avoids doing inline since hit's aren't sorted by depth + * and purpose of 4x buffer indices isn't so clear. + * + * Note that comparing depth as uint is fine. + */ +const uint *GPU_select_buffer_near(const uint *buffer, int hits) +{ + const uint *buffer_near = NULL; + uint depth_min = (uint)-1; + for (int i = 0; i < hits; i++) { + if (buffer[1] < depth_min) { + BLI_assert(buffer[3] != -1); + depth_min = buffer[1]; + buffer_near = buffer; + } + buffer += 4; + } + return buffer_near; +} diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index b31a527a2ad..fecac55087f 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -31,6 +31,8 @@ #include <stdlib.h> #include <float.h> +#include "GPU_immediate.h" +#include "GPU_draw.h" #include "GPU_select.h" #include "GPU_extensions.h" #include "GPU_glew.h" @@ -316,8 +318,8 @@ void gpu_select_pick_begin( /* Restrict OpenGL operations for when we don't have cache */ if (ps->is_cached == false) { + gpuPushAttrib(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT); - glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT); /* disable writing to the framebuffer */ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); @@ -334,12 +336,8 @@ void gpu_select_pick_begin( glDepthFunc(GL_LEQUAL); } - /* set just in case */ - glPixelTransferf(GL_DEPTH_BIAS, 0.0); - glPixelTransferf(GL_DEPTH_SCALE, 1.0); - float viewport[4]; - glGetFloatv(GL_SCISSOR_BOX, viewport); + glGetFloatv(GL_VIEWPORT, viewport); ps->src.clip_rect = *input; ps->src.rect_len = rect_len; @@ -542,7 +540,7 @@ uint gpu_select_pick_end(void) /* force finishing last pass */ gpu_select_pick_load_id(ps->gl.prev_id); } - glPopAttrib(); + gpuPopAttrib(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c index b934dbf7aa8..4520025ea7f 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.c +++ b/source/blender/gpu/intern/gpu_select_sample_query.c @@ -32,6 +32,8 @@ #include <stdlib.h> +#include "GPU_immediate.h" +#include "GPU_draw.h" #include "GPU_select.h" #include "GPU_extensions.h" #include "GPU_glew.h" @@ -42,6 +44,8 @@ #include "BLI_utildefines.h" +#include "PIL_time.h" + #include "gpu_select_private.h" @@ -94,15 +98,15 @@ void gpu_select_query_begin( g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id), "gpu selection ids"); glGenQueries(g_query_state.num_of_queries, g_query_state.queries); - glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT); + gpuPushAttrib(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT); /* disable writing to the framebuffer */ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); /* In order to save some fill rate we minimize the viewport using rect. - * We need to get the region of the scissor so that our geometry doesn't + * We need to get the region of the viewport so that our geometry doesn't * get rejected before the depth test. Should probably cull rect against - * scissor for viewport but this is a rare case I think */ - glGetFloatv(GL_SCISSOR_BOX, viewport); + * the viewport but this is a rare case I think */ + glGetFloatv(GL_VIEWPORT, viewport); glViewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input)); /* occlusion queries operates on fragments that pass tests and since we are interested on all @@ -171,7 +175,16 @@ uint gpu_select_query_end(void) } for (i = 0; i < g_query_state.active_query; i++) { - uint result; + uint result = 0; + /* Wait until the result is available. */ + while (result == 0) { + glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT_AVAILABLE, &result); + if (result == 0) { + /* (fclem) Not sure if this is better than calling + * glGetQueryObjectuiv() indefinitely. */ + PIL_sleep_ms(1); + } + } glGetQueryObjectuiv(g_query_state.queries[i], GL_QUERY_RESULT, &result); if (result > 0) { if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) { @@ -206,7 +219,7 @@ uint gpu_select_query_end(void) glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries); MEM_freeN(g_query_state.queries); MEM_freeN(g_query_state.id); - glPopAttrib(); + gpuPopAttrib(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); return hits; diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 331af47d560..8c978be81c1 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -35,19 +35,124 @@ #include "BKE_appdir.h" #include "BKE_global.h" -#include "GPU_compositing.h" -#include "GPU_debug.h" +#include "DNA_space_types.h" + #include "GPU_extensions.h" -#include "GPU_glew.h" +#include "GPU_matrix.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_material.h" +#include "GPU_uniformbuffer.h" + +#include "gpu_shader_private.h" -/* TODO(sergey): Find better default values for this constants. */ -#define MAX_DEFINE_LENGTH 1024 -#define MAX_EXT_DEFINE_LENGTH 1024 +/* Adjust these constants as needed. */ +#define MAX_DEFINE_LENGTH 256 +#define MAX_EXT_DEFINE_LENGTH 256 /* Non-generated shaders */ +extern char datatoc_gpu_shader_depth_only_frag_glsl[]; +extern char datatoc_gpu_shader_uniform_color_frag_glsl[]; +extern char datatoc_gpu_shader_checker_frag_glsl[]; +extern char datatoc_gpu_shader_diag_stripes_frag_glsl[]; +extern char datatoc_gpu_shader_simple_lighting_frag_glsl[]; +extern char datatoc_gpu_shader_simple_lighting_flat_color_frag_glsl[]; +extern char datatoc_gpu_shader_simple_lighting_smooth_color_frag_glsl[]; +extern char datatoc_gpu_shader_simple_lighting_smooth_color_alpha_frag_glsl[]; +extern char datatoc_gpu_shader_flat_color_frag_glsl[]; +extern char datatoc_gpu_shader_flat_color_alpha_test_0_frag_glsl[]; +extern char datatoc_gpu_shader_flat_id_frag_glsl[]; +extern char datatoc_gpu_shader_2D_vert_glsl[]; +extern char datatoc_gpu_shader_2D_flat_color_vert_glsl[]; +extern char datatoc_gpu_shader_2D_smooth_color_uniform_alpha_vert_glsl[]; +extern char datatoc_gpu_shader_2D_smooth_color_vert_glsl[]; +extern char datatoc_gpu_shader_2D_smooth_color_frag_glsl[]; +extern char datatoc_gpu_shader_2D_smooth_color_dithered_frag_glsl[]; +extern char datatoc_gpu_shader_2D_image_vert_glsl[]; +extern char datatoc_gpu_shader_2D_image_rect_vert_glsl[]; +extern char datatoc_gpu_shader_2D_image_multi_rect_vert_glsl[]; +extern char datatoc_gpu_shader_2D_widget_base_vert_glsl[]; +extern char datatoc_gpu_shader_2D_widget_base_frag_glsl[]; +extern char datatoc_gpu_shader_2D_widget_shadow_vert_glsl[]; +extern char datatoc_gpu_shader_2D_widget_shadow_frag_glsl[]; +extern char datatoc_gpu_shader_2D_nodelink_frag_glsl[]; +extern char datatoc_gpu_shader_2D_nodelink_vert_glsl[]; + +extern char datatoc_gpu_shader_3D_image_vert_glsl[]; +extern char datatoc_gpu_shader_image_frag_glsl[]; +extern char datatoc_gpu_shader_image_linear_frag_glsl[]; +extern char datatoc_gpu_shader_image_color_frag_glsl[]; +extern char datatoc_gpu_shader_image_desaturate_frag_glsl[]; +extern char datatoc_gpu_shader_image_varying_color_frag_glsl[]; +extern char datatoc_gpu_shader_image_alpha_color_frag_glsl[]; +extern char datatoc_gpu_shader_image_shuffle_color_frag_glsl[]; +extern char datatoc_gpu_shader_image_interlace_frag_glsl[]; +extern char datatoc_gpu_shader_image_mask_uniform_color_frag_glsl[]; +extern char datatoc_gpu_shader_image_modulate_alpha_frag_glsl[]; +extern char datatoc_gpu_shader_image_depth_linear_frag_glsl[]; +extern char datatoc_gpu_shader_image_depth_copy_frag_glsl[]; +extern char datatoc_gpu_shader_image_multisample_resolve_frag_glsl[]; +extern char datatoc_gpu_shader_3D_vert_glsl[]; +extern char datatoc_gpu_shader_3D_normal_vert_glsl[]; +extern char datatoc_gpu_shader_3D_flat_color_vert_glsl[]; +extern char datatoc_gpu_shader_3D_smooth_color_vert_glsl[]; +extern char datatoc_gpu_shader_3D_normal_flat_color_vert_glsl[]; +extern char datatoc_gpu_shader_3D_normal_smooth_color_vert_glsl[]; +extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; +extern char datatoc_gpu_shader_3D_passthrough_vert_glsl[]; +extern char datatoc_gpu_shader_3D_clipped_uniform_color_vert_glsl[]; + +extern char datatoc_gpu_shader_instance_vert_glsl[]; +extern char datatoc_gpu_shader_instance_variying_size_variying_color_vert_glsl[]; +extern char datatoc_gpu_shader_instance_variying_size_variying_id_vert_glsl[]; +extern char datatoc_gpu_shader_instance_objectspace_variying_color_vert_glsl[]; +extern char datatoc_gpu_shader_instance_screenspace_variying_color_vert_glsl[]; +extern char datatoc_gpu_shader_instance_screen_aligned_vert_glsl[]; +extern char datatoc_gpu_shader_instance_camera_vert_glsl[]; +extern char datatoc_gpu_shader_instance_distance_line_vert_glsl[]; +extern char datatoc_gpu_shader_instance_edges_variying_color_geom_glsl[]; +extern char datatoc_gpu_shader_instance_edges_variying_color_vert_glsl[]; +extern char datatoc_gpu_shader_instance_mball_handles_vert_glsl[]; + +extern char datatoc_gpu_shader_3D_groundpoint_vert_glsl[]; +extern char datatoc_gpu_shader_3D_groundline_geom_glsl[]; + +extern char datatoc_gpu_shader_point_uniform_color_frag_glsl[]; +extern char datatoc_gpu_shader_point_uniform_color_aa_frag_glsl[]; +extern char datatoc_gpu_shader_point_uniform_color_outline_aa_frag_glsl[]; +extern char datatoc_gpu_shader_point_varying_color_outline_aa_frag_glsl[]; +extern char datatoc_gpu_shader_point_varying_color_frag_glsl[]; +extern char datatoc_gpu_shader_3D_point_fixed_size_varying_color_vert_glsl[]; +extern char datatoc_gpu_shader_3D_point_varying_size_vert_glsl[]; +extern char datatoc_gpu_shader_3D_point_varying_size_varying_color_vert_glsl[]; +extern char datatoc_gpu_shader_3D_point_uniform_size_aa_vert_glsl[]; +extern char datatoc_gpu_shader_3D_point_uniform_size_outline_aa_vert_glsl[]; +extern char datatoc_gpu_shader_2D_point_varying_size_varying_color_vert_glsl[]; +extern char datatoc_gpu_shader_2D_point_uniform_size_aa_vert_glsl[]; +extern char datatoc_gpu_shader_2D_point_uniform_size_outline_aa_vert_glsl[]; +extern char datatoc_gpu_shader_2D_point_uniform_size_varying_color_outline_aa_vert_glsl[]; + +extern char datatoc_gpu_shader_2D_line_dashed_uniform_color_vert_glsl[]; +extern char datatoc_gpu_shader_2D_line_dashed_frag_glsl[]; +extern char datatoc_gpu_shader_2D_line_dashed_geom_glsl[]; +extern char datatoc_gpu_shader_3D_line_dashed_uniform_color_legacy_vert_glsl[]; +extern char datatoc_gpu_shader_3D_line_dashed_uniform_color_vert_glsl[]; + +extern char datatoc_gpu_shader_edges_front_back_persp_vert_glsl[]; +extern char datatoc_gpu_shader_edges_front_back_persp_geom_glsl[]; +extern char datatoc_gpu_shader_edges_front_back_persp_legacy_vert_glsl[]; +extern char datatoc_gpu_shader_edges_front_back_ortho_vert_glsl[]; +extern char datatoc_gpu_shader_edges_overlay_vert_glsl[]; +extern char datatoc_gpu_shader_edges_overlay_geom_glsl[]; +extern char datatoc_gpu_shader_edges_overlay_simple_geom_glsl[]; +extern char datatoc_gpu_shader_edges_overlay_frag_glsl[]; +extern char datatoc_gpu_shader_text_vert_glsl[]; +extern char datatoc_gpu_shader_text_geom_glsl[]; +extern char datatoc_gpu_shader_text_frag_glsl[]; +extern char datatoc_gpu_shader_text_simple_vert_glsl[]; +extern char datatoc_gpu_shader_text_simple_geom_glsl[]; +extern char datatoc_gpu_shader_keyframe_diamond_vert_glsl[]; +extern char datatoc_gpu_shader_keyframe_diamond_frag_glsl[]; + extern char datatoc_gpu_shader_fire_frag_glsl[]; extern char datatoc_gpu_shader_smoke_vert_glsl[]; extern char datatoc_gpu_shader_smoke_frag_glsl[]; @@ -55,51 +160,23 @@ extern char datatoc_gpu_shader_vsm_store_vert_glsl[]; extern char datatoc_gpu_shader_vsm_store_frag_glsl[]; extern char datatoc_gpu_shader_sep_gaussian_blur_vert_glsl[]; extern char datatoc_gpu_shader_sep_gaussian_blur_frag_glsl[]; -extern char datatoc_gpu_shader_fx_vert_glsl[]; -extern char datatoc_gpu_shader_fx_ssao_frag_glsl[]; -extern char datatoc_gpu_shader_fx_dof_frag_glsl[]; -extern char datatoc_gpu_shader_fx_dof_vert_glsl[]; -extern char datatoc_gpu_shader_fx_dof_hq_frag_glsl[]; -extern char datatoc_gpu_shader_fx_dof_hq_vert_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_lib_glsl[]; - -static struct GPUShadersGlobal { - struct { - GPUShader *vsm_store; - GPUShader *sep_gaussian_blur; - GPUShader *smoke; - GPUShader *smoke_fire; - GPUShader *smoke_coba; - /* cache for shader fx. Those can exist in combinations so store them here */ - GPUShader *fx_shaders[MAX_FX_SHADERS * 2]; - } shaders; -} GG = {{NULL}}; - -/* GPUShader */ - -struct GPUShader { - GLuint program; /* handle for full program (links shader stages below) */ - - GLuint vertex; /* handle for vertex shader */ - GLuint geometry; /* handle for geometry shader */ - GLuint fragment; /* handle for fragment shader */ - - int totattrib; /* total number of attributes */ - int uniforms; /* required uniforms */ - - void *uniform_interface; /* cached uniform interface for shader. Data depends on shader */ -}; + +/* cache of built-in shaders (each is created on first use) */ +static GPUShader *builtin_shaders[GPU_NUM_BUILTIN_SHADERS] = { NULL }; + +typedef struct { + const char *vert; + const char *frag; + const char *geom; /* geometry stage runs between vert & frag, but is less common, so it goes last */ +} GPUShaderStages; static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) { - int i; int line = 1; fprintf(stderr, "GPUShader: %s error:\n", task); - for (i = 0; i < totcode; i++) { + for (int i = 0; i < totcode; i++) { const char *c, *pos, *end = code[i] + strlen(code[i]); if (G.debug & G_DEBUG) { @@ -122,76 +199,23 @@ static void shader_print_errors(const char *task, const char *log, const char ** static const char *gpu_shader_version(void) { - if (GLEW_VERSION_3_2) { - if (GLEW_ARB_compatibility) { - return "#version 150 compatibility\n"; - /* highest version that is widely supported - * gives us native geometry shaders! - * use compatibility profile so we can continue using builtin shader input/output names - */ - } - else { - return "#version 130\n"; - /* latest version that is compatible with existing shaders */ - } - } - else if (GLEW_VERSION_3_1) { - if (GLEW_ARB_compatibility) { - return "#version 140\n"; - /* also need the ARB_compatibility extension, handled below */ - } - else { - return "#version 130\n"; - /* latest version that is compatible with existing shaders */ - } - } - else if (GLEW_VERSION_3_0) { - return "#version 130\n"; - /* GLSL 1.3 has modern syntax/keywords/datatypes so use if available - * older features are deprecated but still available without compatibility extension or profile - */ - } - else { - return "#version 120\n"; - /* minimum supported */ - } + return "#version 330\n"; } - -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH], bool use_geometry_shader) +static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) { /* enable extensions for features that are not part of our base GLSL version * don't use an extension for something already available! */ if (GLEW_ARB_texture_query_lod) { - /* a #version 400 feature, but we use #version 150 maximum so use extension */ + /* a #version 400 feature, but we use #version 330 maximum so use extension */ strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); } - - if (use_geometry_shader && GPU_geometry_shader_support_via_extension()) { - strcat(defines, "#extension GL_EXT_geometry_shader4: enable\n"); - } - - if (GLEW_VERSION_3_1 && !GLEW_VERSION_3_2 && GLEW_ARB_compatibility) { - strcat(defines, "#extension GL_ARB_compatibility: enable\n"); - } - - if (!GLEW_VERSION_3_1) { - if (GLEW_ARB_draw_instanced) { - strcat(defines, "#extension GL_ARB_draw_instanced: enable\n"); - } - - if (!GLEW_VERSION_3_0 && GLEW_EXT_gpu_shader4) { - strcat(defines, "#extension GL_EXT_gpu_shader4: enable\n"); - /* TODO: maybe require this? shaders become so much nicer */ - } - } } static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH], - bool use_opensubdiv, - bool use_new_shading) + bool use_opensubdiv) { /* some useful defines to detect GPU type */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { @@ -209,10 +233,6 @@ static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH], if (GPU_bicubic_bump_support()) strcat(defines, "#define BUMP_BICUBIC\n"); - if (GLEW_VERSION_3_0) { - strcat(defines, "#define BIT_OPERATIONS\n"); - } - #ifdef WITH_OPENSUBDIV /* TODO(sergey): Check whether we actually compiling shader for * the OpenSubdiv mesh. @@ -234,10 +254,6 @@ static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH], UNUSED_VARS(use_opensubdiv); #endif - if (use_new_shading) { - strcat(defines, "#define USE_NEW_SHADING\n"); - } - return; } @@ -245,20 +261,17 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *geocode, const char *libcode, - const char *defines, - int input, - int output, - int number) + const char *defines) { return GPU_shader_create_ex(vertexcode, fragcode, geocode, libcode, defines, - input, - output, - number, - GPU_SHADER_FLAGS_NONE); + GPU_SHADER_FLAGS_NONE, + GPU_SHADER_TFB_NONE, + NULL, + 0); } #define DEBUG_SHADER_NONE "" @@ -313,16 +326,12 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, const char *geocode, const char *libcode, const char *defines, - int input, - int output, - int number, - const int flags) + const int flags, + const GPUShaderTFBType tf_type, + const char **tf_names, + const int tf_count) { #ifdef WITH_OPENSUBDIV - /* TODO(sergey): used to add #version 150 to the geometry shader. - * Could safely be renamed to "use_geometry_code" since it's very - * likely any of geometry code will want to use GLSL 1.5. - */ bool use_opensubdiv = (flags & GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV) != 0; #else UNUSED_VARS(flags); @@ -335,9 +344,6 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, char standard_defines[MAX_DEFINE_LENGTH] = ""; char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; - if (geocode && !GPU_geometry_shader_support()) - return NULL; - shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); @@ -361,9 +367,8 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, } gpu_shader_standard_defines(standard_defines, - use_opensubdiv, - (flags & GPU_SHADER_FLAGS_NEW_SHADING) != 0); - gpu_shader_standard_extensions(standard_extensions, geocode != NULL); + use_opensubdiv); + gpu_shader_standard_extensions(standard_extensions); if (vertexcode) { const char *source[5]; @@ -461,23 +466,22 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, GPU_shader_free(shader); return NULL; } - - if (!use_opensubdiv) { - GPU_shader_geometry_stage_primitive_io(shader, input, output, number); - } } #ifdef WITH_OPENSUBDIV if (use_opensubdiv) { glBindAttribLocation(shader->program, 0, "position"); glBindAttribLocation(shader->program, 1, "normal"); - GPU_shader_geometry_stage_primitive_io(shader, - GL_LINES_ADJACENCY_EXT, - GL_TRIANGLE_STRIP, - 4); } #endif + if (tf_names != NULL) { + glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); + /* Primitive type must be setup */ + BLI_assert(tf_type != GPU_SHADER_TFB_NONE); + shader->feedback_transform_type = tf_type; + } + glLinkProgram(shader->program); glGetProgramiv(shader->program, GL_LINK_STATUS, &status); if (!status) { @@ -492,16 +496,26 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, return NULL; } + shader->interface = GWN_shaderinterface_create(shader->program); + #ifdef WITH_OPENSUBDIV /* TODO(sergey): Find a better place for this. */ - if (use_opensubdiv && GLEW_VERSION_4_1) { - glProgramUniform1i(shader->program, - glGetUniformLocation(shader->program, "FVarDataOffsetBuffer"), - 30); /* GL_TEXTURE30 */ - - glProgramUniform1i(shader->program, - glGetUniformLocation(shader->program, "FVarDataBuffer"), - 31); /* GL_TEXTURE31 */ + if (use_opensubdiv) { + if (GLEW_VERSION_4_1) { + glProgramUniform1i(shader->program, + GWN_shaderinterface_uniform(shader->interface, "FVarDataOffsetBuffer")->location, + 30); /* GL_TEXTURE30 */ + + glProgramUniform1i(shader->program, + GWN_shaderinterface_uniform(shader->interface, "FVarDataBuffer")->location, + 31); /* GL_TEXTURE31 */ + } + else { + glUseProgram(shader->program); + glUniform1i(GWN_shaderinterface_uniform(shader->interface, "FVarDataOffsetBuffer")->location, 30); + glUniform1i(GWN_shaderinterface_uniform(shader->interface, "FVarDataBuffer")->location, 31); + glUseProgram(0); + } } #endif @@ -515,20 +529,42 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, void GPU_shader_bind(GPUShader *shader) { - GPU_ASSERT_NO_GL_ERRORS("Pre Shader Bind"); + BLI_assert(shader && shader->program); + glUseProgram(shader->program); - GPU_ASSERT_NO_GL_ERRORS("Post Shader Bind"); + gpuBindMatrices(shader->interface); } void GPU_shader_unbind(void) { - GPU_ASSERT_NO_GL_ERRORS("Pre Shader Unbind"); glUseProgram(0); - GPU_ASSERT_NO_GL_ERRORS("Post Shader Unbind"); +} + +bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id) +{ + if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { + return false; + } + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); + + switch (shader->feedback_transform_type) { + case GPU_SHADER_TFB_POINTS: glBeginTransformFeedback(GL_POINTS); return true; + case GPU_SHADER_TFB_LINES: glBeginTransformFeedback(GL_LINES); return true; + case GPU_SHADER_TFB_TRIANGLES: glBeginTransformFeedback(GL_TRIANGLES); return true; + default: return false; + } +} + +void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) +{ + glEndTransformFeedback(); } void GPU_shader_free(GPUShader *shader) { + BLI_assert(shader); + if (shader->vertex) glDeleteShader(shader->vertex); if (shader->geometry) @@ -538,25 +574,43 @@ void GPU_shader_free(GPUShader *shader) if (shader->program) glDeleteProgram(shader->program); - if (shader->uniform_interface) - MEM_freeN(shader->uniform_interface); + if (shader->interface) + GWN_shaderinterface_discard(shader->interface); MEM_freeN(shader); } int GPU_shader_get_uniform(GPUShader *shader, const char *name) { - return glGetUniformLocation(shader->program, name); + BLI_assert(shader && shader->program); + const Gwn_ShaderInput *uniform = GWN_shaderinterface_uniform(shader->interface, name); + return uniform ? uniform->location : -1; +} + +int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) +{ + BLI_assert(shader && shader->program); + const Gwn_ShaderInput *uniform = GWN_shaderinterface_uniform_builtin(shader->interface, builtin); + return uniform ? uniform->location : -1; +} + +int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + + const Gwn_ShaderInput *ubo = GWN_shaderinterface_ubo(shader->interface, name); + return ubo ? ubo->location : -1; } void *GPU_shader_get_interface(GPUShader *shader) { - return shader->uniform_interface; + return shader->interface; } -void GPU_shader_set_interface(GPUShader *shader, void *interface) +/* Clement : Temp */ +int GPU_shader_get_program(GPUShader *shader) { - shader->uniform_interface = interface; + return (int)shader->program; } void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) @@ -564,16 +618,12 @@ void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int leng if (location == -1 || value == NULL) return; - GPU_ASSERT_NO_GL_ERRORS("Pre Uniform Vector"); - if (length == 1) glUniform1fv(location, arraysize, value); else if (length == 2) glUniform2fv(location, arraysize, value); else if (length == 3) glUniform3fv(location, arraysize, value); else if (length == 4) glUniform4fv(location, arraysize, value); else if (length == 9) glUniformMatrix3fv(location, arraysize, 0, value); else if (length == 16) glUniformMatrix4fv(location, arraysize, 0, value); - - GPU_ASSERT_NO_GL_ERRORS("Post Uniform Vector"); } void GPU_shader_uniform_vector_int(GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) @@ -581,14 +631,10 @@ void GPU_shader_uniform_vector_int(GPUShader *UNUSED(shader), int location, int if (location == -1) return; - GPU_ASSERT_NO_GL_ERRORS("Pre Uniform Vector"); - if (length == 1) glUniform1iv(location, arraysize, value); else if (length == 2) glUniform2iv(location, arraysize, value); else if (length == 3) glUniform3iv(location, arraysize, value); else if (length == 4) glUniform4iv(location, arraysize, value); - - GPU_ASSERT_NO_GL_ERRORS("Post Uniform Vector"); } void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) @@ -596,225 +642,296 @@ void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) if (location == -1) return; - GPU_CHECK_ERRORS_AROUND(glUniform1i(location, value)); + glUniform1i(location, value); } -void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) +void GPU_shader_uniform_buffer(GPUShader *shader, int location, GPUUniformBuffer *ubo) { - if (GPU_geometry_shader_support_via_extension()) { - /* geometry shaders must provide this info themselves for #version 150 and up */ - glProgramParameteriEXT(shader->program, GL_GEOMETRY_INPUT_TYPE_EXT, input); - glProgramParameteriEXT(shader->program, GL_GEOMETRY_OUTPUT_TYPE_EXT, output); - glProgramParameteriEXT(shader->program, GL_GEOMETRY_VERTICES_OUT_EXT, number); + int bindpoint = GPU_uniformbuffer_bindpoint(ubo); + + if (location == -1) { + return; } + + glUniformBlockBinding(shader->program, location, bindpoint); } void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) { - GLenum arbnumber; int number = GPU_texture_bound_number(tex); - int bindcode = GPU_texture_opengl_bindcode(tex); - int target = GPU_texture_target(tex); - if (number >= GPU_max_textures()) { - fprintf(stderr, "Not enough texture slots.\n"); + if (number == -1) { + fprintf(stderr, "Texture is not bound.\n"); + BLI_assert(0); return; } - if (number == -1) - return; - if (location == -1) return; - GPU_ASSERT_NO_GL_ERRORS("Pre Uniform Texture"); - - arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number); - - if (number != 0) glActiveTexture(arbnumber); - if (bindcode != 0) - glBindTexture(target, bindcode); - else - GPU_invalid_tex_bind(target); glUniform1i(location, number); - glEnable(target); - if (number != 0) glActiveTexture(GL_TEXTURE0); - - GPU_ASSERT_NO_GL_ERRORS("Post Uniform Texture"); } int GPU_shader_get_attribute(GPUShader *shader, const char *name) { - int index; - - GPU_CHECK_ERRORS_AROUND(index = glGetAttribLocation(shader->program, name)); - - return index; + BLI_assert(shader && shader->program); + const Gwn_ShaderInput *attrib = GWN_shaderinterface_attr(shader->interface, name); + return attrib ? attrib->location : -1; } GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) { - GPUShader *retval = NULL; - - switch (shader) { - case GPU_SHADER_VSM_STORE: - if (!GG.shaders.vsm_store) - GG.shaders.vsm_store = GPU_shader_create( - datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl, - NULL, NULL, NULL, 0, 0, 0); - retval = GG.shaders.vsm_store; - break; - case GPU_SHADER_SEP_GAUSSIAN_BLUR: - if (!GG.shaders.sep_gaussian_blur) - GG.shaders.sep_gaussian_blur = GPU_shader_create( - datatoc_gpu_shader_sep_gaussian_blur_vert_glsl, - datatoc_gpu_shader_sep_gaussian_blur_frag_glsl, - NULL, NULL, NULL, 0, 0, 0); - retval = GG.shaders.sep_gaussian_blur; - break; - case GPU_SHADER_SMOKE: - if (!GG.shaders.smoke) - GG.shaders.smoke = GPU_shader_create( - datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl, - NULL, NULL, NULL, 0, 0, 0); - retval = GG.shaders.smoke; - break; - case GPU_SHADER_SMOKE_FIRE: - if (!GG.shaders.smoke_fire) - GG.shaders.smoke_fire = GPU_shader_create( - datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_fire_frag_glsl, - NULL, NULL, NULL, 0, 0, 0); - retval = GG.shaders.smoke_fire; - break; - case GPU_SHADER_SMOKE_COBA: - if (!GG.shaders.smoke_coba) - GG.shaders.smoke_coba = GPU_shader_create( - datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl, - NULL, NULL, "#define USE_COBA;\n", 0, 0, 0); - retval = GG.shaders.smoke_coba; - break; - } - - if (retval == NULL) - printf("Unable to create a GPUShader for builtin shader: %u\n", shader); - - return retval; -} - -#define MAX_DEFINES 100 - -GPUShader *GPU_shader_get_builtin_fx_shader(int effect, bool persp) -{ - int offset; - char defines[MAX_DEFINES] = ""; - /* avoid shaders out of range */ - if (effect >= MAX_FX_SHADERS) - return NULL; - - offset = 2 * effect; - - if (persp) { - offset += 1; - strcat(defines, "#define PERSP_MATRIX\n"); - } - - if (!GG.shaders.fx_shaders[offset]) { - GPUShader *shader = NULL; - - switch (effect) { - case GPU_SHADER_FX_SSAO: - shader = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_ssao_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + BLI_assert(shader != GPU_NUM_BUILTIN_SHADERS); /* don't be a troll */ + + static const GPUShaderStages builtin_shader_stages[GPU_NUM_BUILTIN_SHADERS] = { + [GPU_SHADER_VSM_STORE] = { datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl }, + [GPU_SHADER_SEP_GAUSSIAN_BLUR] = { datatoc_gpu_shader_sep_gaussian_blur_vert_glsl, + datatoc_gpu_shader_sep_gaussian_blur_frag_glsl }, + [GPU_SHADER_SMOKE] = { datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl }, + [GPU_SHADER_SMOKE_FIRE] = { datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl }, + [GPU_SHADER_SMOKE_COBA] = { datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl }, + + [GPU_SHADER_TEXT] = { datatoc_gpu_shader_text_vert_glsl, + datatoc_gpu_shader_text_frag_glsl, + datatoc_gpu_shader_text_geom_glsl }, + [GPU_SHADER_TEXT_SIMPLE] = { datatoc_gpu_shader_text_simple_vert_glsl, + datatoc_gpu_shader_text_frag_glsl, + datatoc_gpu_shader_text_simple_geom_glsl }, + [GPU_SHADER_KEYFRAME_DIAMOND] = { datatoc_gpu_shader_keyframe_diamond_vert_glsl, + datatoc_gpu_shader_keyframe_diamond_frag_glsl }, + [GPU_SHADER_EDGES_FRONT_BACK_PERSP] = { datatoc_gpu_shader_edges_front_back_persp_vert_glsl, + /* this version is */ datatoc_gpu_shader_flat_color_frag_glsl, + /* magical but slooow */ datatoc_gpu_shader_edges_front_back_persp_geom_glsl }, + [GPU_SHADER_EDGES_FRONT_BACK_ORTHO] = { datatoc_gpu_shader_edges_front_back_ortho_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl }, + [GPU_SHADER_EDGES_OVERLAY_SIMPLE] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_edges_overlay_frag_glsl, + datatoc_gpu_shader_edges_overlay_simple_geom_glsl }, + [GPU_SHADER_EDGES_OVERLAY] = { datatoc_gpu_shader_edges_overlay_vert_glsl, + datatoc_gpu_shader_edges_overlay_frag_glsl, + datatoc_gpu_shader_edges_overlay_geom_glsl }, + [GPU_SHADER_SIMPLE_LIGHTING] = { datatoc_gpu_shader_3D_normal_vert_glsl, datatoc_gpu_shader_simple_lighting_frag_glsl }, + /* Use 'USE_FLAT_NORMAL' to make flat shader from smooth */ + [GPU_SHADER_SIMPLE_LIGHTING_FLAT_COLOR] = { datatoc_gpu_shader_3D_normal_smooth_color_vert_glsl, datatoc_gpu_shader_simple_lighting_smooth_color_frag_glsl }, + [GPU_SHADER_SIMPLE_LIGHTING_SMOOTH_COLOR] = { datatoc_gpu_shader_3D_normal_smooth_color_vert_glsl, datatoc_gpu_shader_simple_lighting_smooth_color_frag_glsl }, + [GPU_SHADER_SIMPLE_LIGHTING_SMOOTH_COLOR_ALPHA] = { datatoc_gpu_shader_3D_normal_smooth_color_vert_glsl, datatoc_gpu_shader_simple_lighting_smooth_color_alpha_frag_glsl }, + + [GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_image_vert_glsl, + datatoc_gpu_shader_image_mask_uniform_color_frag_glsl }, + [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] = { datatoc_gpu_shader_3D_image_vert_glsl, + datatoc_gpu_shader_image_modulate_alpha_frag_glsl }, + [GPU_SHADER_3D_IMAGE_DEPTH] = { datatoc_gpu_shader_3D_image_vert_glsl, + datatoc_gpu_shader_image_depth_linear_frag_glsl }, + [GPU_SHADER_3D_IMAGE_DEPTH_COPY] = { datatoc_gpu_shader_3D_image_vert_glsl, + datatoc_gpu_shader_image_depth_copy_frag_glsl }, + [GPU_SHADER_2D_IMAGE_MULTISAMPLE_2] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_image_multisample_resolve_frag_glsl }, + [GPU_SHADER_2D_IMAGE_MULTISAMPLE_4] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_image_multisample_resolve_frag_glsl }, + [GPU_SHADER_2D_IMAGE_MULTISAMPLE_8] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_image_multisample_resolve_frag_glsl }, + [GPU_SHADER_2D_IMAGE_MULTISAMPLE_16] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_image_multisample_resolve_frag_glsl }, + + [GPU_SHADER_2D_IMAGE_INTERLACE] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_interlace_frag_glsl }, + [GPU_SHADER_2D_CHECKER] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_checker_frag_glsl }, + + [GPU_SHADER_2D_DIAG_STRIPES] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_diag_stripes_frag_glsl }, + + [GPU_SHADER_2D_UNIFORM_COLOR] = { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_uniform_color_frag_glsl }, + [GPU_SHADER_2D_FLAT_COLOR] = { datatoc_gpu_shader_2D_flat_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl }, + [GPU_SHADER_2D_SMOOTH_COLOR] = { datatoc_gpu_shader_2D_smooth_color_vert_glsl, + datatoc_gpu_shader_2D_smooth_color_frag_glsl }, + [GPU_SHADER_2D_SMOOTH_COLOR_DITHER] = { datatoc_gpu_shader_2D_smooth_color_vert_glsl, + datatoc_gpu_shader_2D_smooth_color_dithered_frag_glsl }, + [GPU_SHADER_2D_IMAGE_LINEAR_TO_SRGB] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_linear_frag_glsl }, + [GPU_SHADER_2D_IMAGE] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_frag_glsl }, + [GPU_SHADER_2D_IMAGE_COLOR] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_color_frag_glsl }, + [GPU_SHADER_2D_IMAGE_DESATURATE_COLOR] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_desaturate_frag_glsl }, + [GPU_SHADER_2D_IMAGE_ALPHA_COLOR] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_alpha_color_frag_glsl }, + [GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR] = { datatoc_gpu_shader_2D_image_vert_glsl, + datatoc_gpu_shader_image_shuffle_color_frag_glsl }, + [GPU_SHADER_2D_IMAGE_RECT_COLOR] = { datatoc_gpu_shader_2D_image_rect_vert_glsl, + datatoc_gpu_shader_image_color_frag_glsl }, + [GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR] = { datatoc_gpu_shader_2D_image_multi_rect_vert_glsl, + datatoc_gpu_shader_image_varying_color_frag_glsl }, + + [GPU_SHADER_3D_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_uniform_color_frag_glsl }, + [GPU_SHADER_3D_UNIFORM_COLOR_U32] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_uniform_color_frag_glsl }, + [GPU_SHADER_3D_FLAT_COLOR] = { datatoc_gpu_shader_3D_flat_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl }, + [GPU_SHADER_3D_FLAT_COLOR_U32] = { datatoc_gpu_shader_3D_flat_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl }, + [GPU_SHADER_3D_SMOOTH_COLOR] = { datatoc_gpu_shader_3D_smooth_color_vert_glsl, + datatoc_gpu_shader_3D_smooth_color_frag_glsl }, + [GPU_SHADER_3D_DEPTH_ONLY] = { datatoc_gpu_shader_3D_vert_glsl, datatoc_gpu_shader_depth_only_frag_glsl }, + [GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_clipped_uniform_color_vert_glsl, + datatoc_gpu_shader_uniform_color_frag_glsl }, + + [GPU_SHADER_3D_GROUNDPOINT] = { datatoc_gpu_shader_3D_groundpoint_vert_glsl, datatoc_gpu_shader_point_uniform_color_frag_glsl }, + [GPU_SHADER_3D_GROUNDLINE] = { datatoc_gpu_shader_3D_passthrough_vert_glsl, + datatoc_gpu_shader_uniform_color_frag_glsl, + datatoc_gpu_shader_3D_groundline_geom_glsl }, + + [GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR] = { datatoc_gpu_shader_2D_line_dashed_uniform_color_vert_glsl, + datatoc_gpu_shader_2D_line_dashed_frag_glsl, + datatoc_gpu_shader_2D_line_dashed_geom_glsl }, + [GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_line_dashed_uniform_color_vert_glsl, + datatoc_gpu_shader_2D_line_dashed_frag_glsl, + datatoc_gpu_shader_2D_line_dashed_geom_glsl }, + + [GPU_SHADER_3D_OBJECTSPACE_SIMPLE_LIGHTING_VARIYING_COLOR] = + { datatoc_gpu_shader_instance_objectspace_variying_color_vert_glsl, + datatoc_gpu_shader_simple_lighting_frag_glsl}, + [GPU_SHADER_3D_OBJECTSPACE_VARIYING_COLOR] = { datatoc_gpu_shader_instance_objectspace_variying_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl}, + [GPU_SHADER_3D_SCREENSPACE_VARIYING_COLOR] = { datatoc_gpu_shader_instance_screenspace_variying_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl}, + [GPU_SHADER_3D_INSTANCE_SCREEN_ALIGNED_AXIS] = { datatoc_gpu_shader_instance_screen_aligned_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl}, + [GPU_SHADER_3D_INSTANCE_SCREEN_ALIGNED] = { datatoc_gpu_shader_instance_screen_aligned_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl}, + + [GPU_SHADER_CAMERA] = { datatoc_gpu_shader_instance_camera_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl}, + [GPU_SHADER_DISTANCE_LINES] = { datatoc_gpu_shader_instance_distance_line_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl}, + + [GPU_SHADER_2D_POINT_FIXED_SIZE_UNIFORM_COLOR] = + { datatoc_gpu_shader_2D_vert_glsl, datatoc_gpu_shader_point_uniform_color_frag_glsl }, + [GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR] = + { datatoc_gpu_shader_2D_point_varying_size_varying_color_vert_glsl, + datatoc_gpu_shader_point_varying_color_frag_glsl }, + [GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA] = + { datatoc_gpu_shader_2D_point_uniform_size_aa_vert_glsl, + datatoc_gpu_shader_point_uniform_color_aa_frag_glsl }, + [GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA] = + { datatoc_gpu_shader_2D_point_uniform_size_outline_aa_vert_glsl, + datatoc_gpu_shader_point_uniform_color_outline_aa_frag_glsl }, + [GPU_SHADER_2D_POINT_UNIFORM_SIZE_VARYING_COLOR_OUTLINE_AA] = + { datatoc_gpu_shader_2D_point_uniform_size_varying_color_outline_aa_vert_glsl, + datatoc_gpu_shader_point_varying_color_outline_aa_frag_glsl }, + [GPU_SHADER_3D_POINT_FIXED_SIZE_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_vert_glsl, + datatoc_gpu_shader_point_uniform_color_frag_glsl }, + [GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR] = { datatoc_gpu_shader_3D_point_fixed_size_varying_color_vert_glsl, + datatoc_gpu_shader_point_varying_color_frag_glsl }, + [GPU_SHADER_3D_POINT_VARYING_SIZE_UNIFORM_COLOR] = { datatoc_gpu_shader_3D_point_varying_size_vert_glsl, + datatoc_gpu_shader_point_uniform_color_frag_glsl }, + [GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR] = + { datatoc_gpu_shader_3D_point_varying_size_varying_color_vert_glsl, + datatoc_gpu_shader_point_varying_color_frag_glsl }, + [GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA] = + { datatoc_gpu_shader_3D_point_uniform_size_aa_vert_glsl, + datatoc_gpu_shader_point_uniform_color_aa_frag_glsl }, + [GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA] = + { datatoc_gpu_shader_3D_point_uniform_size_outline_aa_vert_glsl, + datatoc_gpu_shader_point_uniform_color_outline_aa_frag_glsl }, + + [GPU_SHADER_INSTANCE_UNIFORM_COLOR] = { datatoc_gpu_shader_instance_vert_glsl, datatoc_gpu_shader_uniform_color_frag_glsl }, + [GPU_SHADER_INSTANCE_VARIYING_ID_VARIYING_SIZE] = + { datatoc_gpu_shader_instance_variying_size_variying_id_vert_glsl, + datatoc_gpu_shader_flat_id_frag_glsl }, + [GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE] = + { datatoc_gpu_shader_instance_variying_size_variying_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl }, + [GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SCALE] = + { datatoc_gpu_shader_instance_variying_size_variying_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl }, + [GPU_SHADER_INSTANCE_EDGES_VARIYING_COLOR] = { datatoc_gpu_shader_instance_edges_variying_color_vert_glsl, + datatoc_gpu_shader_flat_color_frag_glsl, + datatoc_gpu_shader_instance_edges_variying_color_geom_glsl}, + + [GPU_SHADER_2D_WIDGET_BASE] = { datatoc_gpu_shader_2D_widget_base_vert_glsl, + datatoc_gpu_shader_2D_widget_base_frag_glsl}, + [GPU_SHADER_2D_WIDGET_BASE_INST] = { datatoc_gpu_shader_2D_widget_base_vert_glsl, + datatoc_gpu_shader_2D_widget_base_frag_glsl}, + [GPU_SHADER_2D_WIDGET_SHADOW] = { datatoc_gpu_shader_2D_widget_shadow_vert_glsl, + datatoc_gpu_shader_2D_widget_shadow_frag_glsl }, + [GPU_SHADER_2D_NODELINK] = { datatoc_gpu_shader_2D_nodelink_vert_glsl, + datatoc_gpu_shader_2D_nodelink_frag_glsl }, + [GPU_SHADER_2D_NODELINK_INST] = { datatoc_gpu_shader_2D_nodelink_vert_glsl, + datatoc_gpu_shader_2D_nodelink_frag_glsl }, + }; + + if (builtin_shaders[shader] == NULL) { + /* just a few special cases */ + const char *defines = NULL; + switch (shader) { + case GPU_SHADER_2D_IMAGE_MULTISAMPLE_2: + defines = "#define SAMPLES 2\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE: - strcat(defines, "#define FIRST_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_2D_IMAGE_MULTISAMPLE_4: + defines = "#define SAMPLES 4\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO: - strcat(defines, "#define SECOND_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_2D_IMAGE_MULTISAMPLE_8: + defines = "#define SAMPLES 8\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE: - strcat(defines, "#define THIRD_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_2D_IMAGE_MULTISAMPLE_16: + defines = "#define SAMPLES 16\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR: - strcat(defines, "#define FOURTH_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_2D_WIDGET_BASE_INST: + case GPU_SHADER_2D_NODELINK_INST: + defines = "#define USE_INSTANCE\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE: - strcat(defines, "#define FIFTH_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_vert_glsl, datatoc_gpu_shader_fx_dof_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_SMOKE_COBA: + defines = "#define USE_COBA\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE: - strcat(defines, "#define FIRST_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_INSTANCE_VARIYING_ID_VARIYING_SIZE: + case GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE: + defines = "#define UNIFORM_SCALE\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_TWO: - strcat(defines, "#define SECOND_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, datatoc_gpu_shader_fx_dof_hq_geo_glsl, datatoc_gpu_shader_fx_lib_glsl, - defines, GL_POINTS, GL_TRIANGLE_STRIP, 4); + case GPU_SHADER_3D_INSTANCE_SCREEN_ALIGNED_AXIS: + defines = "#define AXIS_NAME\n"; break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE: - strcat(defines, "#define THIRD_PASS\n"); - shader = GPU_shader_create(datatoc_gpu_shader_fx_dof_hq_vert_glsl, datatoc_gpu_shader_fx_dof_hq_frag_glsl, NULL, datatoc_gpu_shader_fx_lib_glsl, defines, 0, 0, 0); + case GPU_SHADER_3D_OBJECTSPACE_SIMPLE_LIGHTING_VARIYING_COLOR: + defines = "#define USE_INSTANCE_COLOR\n"; break; - - case GPU_SHADER_FX_DEPTH_RESOLVE: - shader = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_depth_resolve_glsl, NULL, NULL, defines, 0, 0, 0); + case GPU_SHADER_3D_FLAT_COLOR_U32: + case GPU_SHADER_3D_UNIFORM_COLOR_U32: + defines = "#define USE_COLOR_U32\n"; + break; + case GPU_SHADER_SIMPLE_LIGHTING_FLAT_COLOR: + defines = "#define USE_FLAT_NORMAL\n"; + break; + default: break; } - GG.shaders.fx_shaders[offset] = shader; - GPU_fx_shader_init_interface(shader, effect); - } + const GPUShaderStages *stages = builtin_shader_stages + shader; - return GG.shaders.fx_shaders[offset]; -} - - -void GPU_shader_free_builtin_shaders(void) -{ - int i; - - if (GG.shaders.vsm_store) { - GPU_shader_free(GG.shaders.vsm_store); - GG.shaders.vsm_store = NULL; - } + if (shader == GPU_SHADER_EDGES_FRONT_BACK_PERSP && !GLEW_VERSION_3_2) { + /* TODO: remove after switch to core profile (maybe) */ + static const GPUShaderStages legacy_fancy_edges = + { datatoc_gpu_shader_edges_front_back_persp_legacy_vert_glsl, + datatoc_gpu_shader_flat_color_alpha_test_0_frag_glsl }; + stages = &legacy_fancy_edges; + } - if (GG.shaders.sep_gaussian_blur) { - GPU_shader_free(GG.shaders.sep_gaussian_blur); - GG.shaders.sep_gaussian_blur = NULL; - } + if (shader == GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR && !GLEW_VERSION_3_2) { + /* Dashed need geometry shader, which are not supported by legacy OpenGL, fallback to solid lines. */ + /* TODO: remove after switch to core profile (maybe) */ + static const GPUShaderStages legacy_dashed_lines = { datatoc_gpu_shader_3D_line_dashed_uniform_color_legacy_vert_glsl, + datatoc_gpu_shader_2D_line_dashed_frag_glsl }; + stages = &legacy_dashed_lines; + } - if (GG.shaders.smoke) { - GPU_shader_free(GG.shaders.smoke); - GG.shaders.smoke = NULL; + /* common case */ + builtin_shaders[shader] = GPU_shader_create(stages->vert, stages->frag, stages->geom, NULL, defines); } - if (GG.shaders.smoke_fire) { - GPU_shader_free(GG.shaders.smoke_fire); - GG.shaders.smoke_fire = NULL; - } + return builtin_shaders[shader]; +} - if (GG.shaders.smoke_coba) { - GPU_shader_free(GG.shaders.smoke_coba); - GG.shaders.smoke_coba = NULL; - } +#define MAX_DEFINES 100 - for (i = 0; i < 2 * MAX_FX_SHADERS; ++i) { - if (GG.shaders.fx_shaders[i]) { - GPU_shader_free(GG.shaders.fx_shaders[i]); - GG.shaders.fx_shaders[i] = NULL; +void GPU_shader_free_builtin_shaders(void) +{ + for (int i = 0; i < GPU_NUM_BUILTIN_SHADERS; ++i) { + if (builtin_shaders[i]) { + GPU_shader_free(builtin_shaders[i]); + builtin_shaders[i] = NULL; } } } - - diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h new file mode 100644 index 00000000000..d8ec6b5d6d1 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_private.h @@ -0,0 +1,44 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file gpu_shader_private.h + * \ingroup gpu + */ + +#ifndef __GPU_SHADER_PRIVATE_H__ +#define __GPU_SHADER_PRIVATE_H__ + +#include "GPU_glew.h" +#include "gawain/gwn_shader_interface.h" + +struct GPUShader { + GLuint program; /* handle for full program (links shader stages below) */ + + GLuint vertex; /* handle for vertex shader */ + GLuint geometry; /* handle for geometry shader */ + GLuint fragment; /* handle for fragment shader */ + + Gwn_ShaderInterface *interface; /* cached uniform & attrib interface for shader */ + + int feedback_transform_type; +}; + +#endif /* __GPU_SHADER_PRIVATE_H__ */ + diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 6c4ab395b6b..dadd8c441f4 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -32,9 +32,12 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math_base.h" +#include "BLI_listbase.h" +#include "BLI_threads.h" #include "BKE_global.h" +#include "GPU_batch.h" #include "GPU_debug.h" #include "GPU_draw.h" #include "GPU_extensions.h" @@ -48,10 +51,28 @@ static struct GPUTextureGlobal { GPUTexture *invalid_tex_3D; } GG = {NULL, NULL, NULL}; -/* GPUTexture */ +static ListBase g_orphaned_tex = {NULL, NULL}; +static ThreadMutex g_orphan_lock; + +/* Maximum number of FBOs a texture can be attached to. */ +#define GPU_TEX_MAX_FBO_ATTACHED 8 + +typedef enum GPUTextureFormatFlag { + GPU_FORMAT_DEPTH = (1 << 0), + GPU_FORMAT_STENCIL = (1 << 1), + GPU_FORMAT_INTEGER = (1 << 2), + GPU_FORMAT_FLOAT = (1 << 3), + GPU_FORMAT_1D = (1 << 10), + GPU_FORMAT_2D = (1 << 11), + GPU_FORMAT_3D = (1 << 12), + GPU_FORMAT_CUBE = (1 << 13), + GPU_FORMAT_ARRAY = (1 << 14), +} GPUTextureFormatFlag; + +/* GPUTexture */ struct GPUTexture { - int w, h; /* width/height */ + int w, h, d; /* width/height/depth */ int number; /* number for multitexture binding */ int refcount; /* reference count */ GLenum target; /* GL_TEXTURE_* */ @@ -60,45 +81,320 @@ struct GPUTexture { GLuint bindcode; /* opengl identifier for texture */ int fromblender; /* we got the texture from Blender */ - GPUFrameBuffer *fb; /* GPUFramebuffer this texture is attached to */ - int fb_attachment; /* slot the texture is attached to */ - int depth; /* is a depth texture? if 3D how deep? */ + GPUTextureFormat format; + GPUTextureFormatFlag format_flag; + + unsigned int bytesize; /* number of byte for one pixel */ + int components; /* number of color/alpha channels */ + int samples; /* number of samples for multisamples textures. 0 if not multisample target */ + + int fb_attachment[GPU_TEX_MAX_FBO_ATTACHED]; + GPUFrameBuffer *fb[GPU_TEX_MAX_FBO_ATTACHED]; }; -static unsigned char *GPU_texture_convert_pixels(int length, const float *fpixels) +/* ------ Memory Management ------- */ +/* Records every texture allocation / free + * to estimate the Texture Pool Memory consumption */ +static unsigned int memory_usage; + +static unsigned int gpu_texture_memory_footprint_compute(GPUTexture *tex) { - unsigned char *pixels, *p; - const float *fp = fpixels; - const int len = 4 * length; + int samp = max_ii(tex->samples, 1); + switch (tex->target) { + case GL_TEXTURE_1D: + return tex->bytesize * tex->w * samp; + case GL_TEXTURE_1D_ARRAY: + case GL_TEXTURE_2D: + return tex->bytesize * tex->w * tex->h * samp; + case GL_TEXTURE_2D_ARRAY: + case GL_TEXTURE_3D: + return tex->bytesize * tex->w * tex->h * tex->d * samp; + case GL_TEXTURE_CUBE_MAP: + return tex->bytesize * 6 * tex->w * tex->h * samp; + case GL_TEXTURE_CUBE_MAP_ARRAY: + return tex->bytesize * 6 * tex->w * tex->h * tex->d * samp; + default: + return 0; + } +} - p = pixels = MEM_callocN(sizeof(unsigned char) * len, "GPUTexturePixels"); +static void gpu_texture_memory_footprint_add(GPUTexture *tex) +{ + memory_usage += gpu_texture_memory_footprint_compute(tex); +} - for (int a = 0; a < len; a++, p++, fp++) - *p = unit_float_to_uchar_clamp((*fp)); +static void gpu_texture_memory_footprint_remove(GPUTexture *tex) +{ + memory_usage -= gpu_texture_memory_footprint_compute(tex); +} - return pixels; +unsigned int GPU_texture_memory_usage_get(void) +{ + return memory_usage; } -static void gpu_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h) +/* -------------------------------- */ + +static GLenum gpu_texture_get_format( + int components, GPUTextureFormat data_type, + GLenum *format, GLenum *data_format, GPUTextureFormatFlag *format_flag, unsigned int *bytesize) { - void *pixels = MEM_callocN(sizeof(char) * 4 * w * h, "GPUTextureEmptyPixels"); + if (ELEM(data_type, GPU_DEPTH_COMPONENT24, + GPU_DEPTH_COMPONENT16, + GPU_DEPTH_COMPONENT32F)) + { + *format_flag |= GPU_FORMAT_DEPTH; + *data_format = GL_FLOAT; + *format = GL_DEPTH_COMPONENT; + } + else if (data_type == GPU_DEPTH24_STENCIL8) { + *format_flag |= GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL; + *data_format = GL_UNSIGNED_INT_24_8; + *format = GL_DEPTH_STENCIL; + } + else { + /* Integer formats */ + if (ELEM(data_type, GPU_RG16I, GPU_R16I, GPU_RG16UI, GPU_R16UI, GPU_R32UI)) { + if (ELEM(data_type, GPU_R16UI, GPU_RG16UI, GPU_R32UI)) { + *data_format = GL_UNSIGNED_INT; + } + else { + *data_format = GL_INT; + } - if (target == GL_TEXTURE_1D) - glTexSubImage1D(target, 0, x, w, format, GL_UNSIGNED_BYTE, pixels); - else - glTexSubImage2D(target, 0, x, y, w, h, format, GL_UNSIGNED_BYTE, pixels); + *format_flag |= GPU_FORMAT_INTEGER; + + switch (components) { + case 1: *format = GL_RED_INTEGER; break; + case 2: *format = GL_RG_INTEGER; break; + case 3: *format = GL_RGB_INTEGER; break; + case 4: *format = GL_RGBA_INTEGER; break; + default: break; + } + } + else { + *data_format = GL_FLOAT; + *format_flag |= GPU_FORMAT_FLOAT; + + switch (components) { + case 1: *format = GL_RED; break; + case 2: *format = GL_RG; break; + case 3: *format = GL_RGB; break; + case 4: *format = GL_RGBA; break; + default: break; + } + } + } - MEM_freeN(pixels); + switch (data_type) { + case GPU_RGBA32F: + *bytesize = 32; + break; + case GPU_RG32F: + case GPU_RGBA16F: + case GPU_RGBA16: + *bytesize = 16; + break; + case GPU_RGB16F: + *bytesize = 12; + break; + case GPU_RG16F: + case GPU_RG16I: + case GPU_RG16UI: + case GPU_RG16: + case GPU_DEPTH24_STENCIL8: + case GPU_DEPTH_COMPONENT32F: + case GPU_RGBA8: + case GPU_R11F_G11F_B10F: + case GPU_R32F: + case GPU_R32UI: + case GPU_R32I: + *bytesize = 4; + break; + case GPU_DEPTH_COMPONENT24: + *bytesize = 3; + break; + case GPU_DEPTH_COMPONENT16: + case GPU_R16F: + case GPU_R16I: + case GPU_RG8: + *bytesize = 2; + break; + case GPU_R8: + *bytesize = 1; + break; + default: + *bytesize = 0; + break; + } + + /* You can add any of the available type to this list + * For available types see GPU_texture.h */ + switch (data_type) { + /* Formats texture & renderbuffer */ + case GPU_RGBA32F: return GL_RGBA32F; + case GPU_RGBA16F: return GL_RGBA16F; + case GPU_RGBA16: return GL_RGBA16; + case GPU_RG32F: return GL_RG32F; + case GPU_RGB16F: return GL_RGB16F; + case GPU_RG16F: return GL_RG16F; + case GPU_RG16I: return GL_RG16I; + case GPU_RG16: return GL_RG16; + case GPU_RGBA8: return GL_RGBA8; + case GPU_R32F: return GL_R32F; + case GPU_R32UI: return GL_R32UI; + case GPU_R32I: return GL_R32I; + case GPU_R16F: return GL_R16F; + case GPU_R16I: return GL_R16I; + case GPU_R16UI: return GL_R16UI; + case GPU_RG8: return GL_RG8; + case GPU_RG16UI: return GL_RG16UI; + case GPU_R8: return GL_R8; + /* Special formats texture & renderbuffer */ + case GPU_R11F_G11F_B10F: return GL_R11F_G11F_B10F; + case GPU_DEPTH24_STENCIL8: return GL_DEPTH24_STENCIL8; + /* Texture only format */ + /* ** Add Format here **/ + /* Special formats texture only */ + /* ** Add Format here **/ + /* Depth Formats */ + case GPU_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F; + case GPU_DEPTH_COMPONENT24: return GL_DEPTH_COMPONENT24; + case GPU_DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT16; + default: + fprintf(stderr, "Texture format incorrect or unsupported\n"); + return 0; + } } -static GPUTexture *GPU_texture_create_nD( - int w, int h, int n, const float *fpixels, int depth, - GPUHDRType hdr_type, int components, int samples, - char err_out[256]) +static int gpu_texture_get_component_count(GPUTextureFormat format) { - GLenum type, format, internalformat; - void *pixels = NULL; + switch (format) { + case GPU_RGBA8: + case GPU_RGBA16F: + case GPU_RGBA16: + case GPU_RGBA32F: + return 4; + case GPU_RGB16F: + case GPU_R11F_G11F_B10F: + return 3; + case GPU_RG8: + case GPU_RG16: + case GPU_RG16F: + case GPU_RG16I: + case GPU_RG16UI: + case GPU_RG32F: + return 2; + default: + return 1; + } +} + +static float *GPU_texture_3D_rescale(GPUTexture *tex, int w, int h, int d, int channels, const float *fpixels) +{ + const unsigned int xf = w / tex->w, yf = h / tex->h, zf = d / tex->d; + float *nfpixels = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, "GPUTexture Rescaled 3Dtex"); + + if (nfpixels) { + GPU_print_error_debug("You need to scale a 3D texture, feel the pain!"); + + for (unsigned k = 0; k < tex->d; k++) { + for (unsigned j = 0; j < tex->h; j++) { + for (unsigned i = 0; i < tex->w; i++) { + /* obviously doing nearest filtering here, + * it's going to be slow in any case, let's not make it worse */ + float xb = i * xf; + float yb = j * yf; + float zb = k * zf; + unsigned int offset = k * (tex->w * tex->h) + i * tex->h + j; + unsigned int offset_orig = (zb) * (w * h) + (xb) * h + (yb); + + if (channels == 4) { + nfpixels[offset * 4] = fpixels[offset_orig * 4]; + nfpixels[offset * 4 + 1] = fpixels[offset_orig * 4 + 1]; + nfpixels[offset * 4 + 2] = fpixels[offset_orig * 4 + 2]; + nfpixels[offset * 4 + 3] = fpixels[offset_orig * 4 + 3]; + } + else + nfpixels[offset] = fpixels[offset_orig]; + } + } + } + } + return nfpixels; +} + +/* This tries to allocate video memory for a given texture + * If alloc fails, lower the resolution until it fits. */ +static bool gpu_texture_try_alloc( + GPUTexture *tex, GLenum proxy, GLenum internalformat, GLenum format, GLenum data_format, + int channels, bool try_rescale, const float *fpixels, float **rescaled_fpixels) +{ + int r_width; + + switch (proxy) { + case GL_PROXY_TEXTURE_1D: + glTexImage1D(proxy, 0, internalformat, tex->w, 0, format, data_format, NULL); + break; + case GL_PROXY_TEXTURE_1D_ARRAY: + case GL_PROXY_TEXTURE_2D: + glTexImage2D(proxy, 0, internalformat, tex->w, tex->h, 0, format, data_format, NULL); + break; + case GL_PROXY_TEXTURE_2D_ARRAY: + case GL_PROXY_TEXTURE_3D: + glTexImage3D(proxy, 0, internalformat, tex->w, tex->h, tex->d, 0, format, data_format, NULL); + break; + } + + glGetTexLevelParameteriv(proxy, 0, GL_TEXTURE_WIDTH, &r_width); + + if (r_width == 0 && try_rescale) { + const int w = tex->w, h = tex->h, d = tex->d; + + /* Find largest texture possible */ + while (r_width == 0) { + tex->w /= 2; + tex->h /= 2; + tex->d /= 2; + + /* really unlikely to happen but keep this just in case */ + if (tex->w == 0) break; + if (tex->h == 0 && proxy != GL_PROXY_TEXTURE_1D) break; + if (tex->d == 0 && proxy == GL_PROXY_TEXTURE_3D) break; + + if (proxy == GL_PROXY_TEXTURE_1D) + glTexImage1D(proxy, 0, internalformat, tex->w, 0, format, data_format, NULL); + else if (proxy == GL_PROXY_TEXTURE_2D) + glTexImage2D(proxy, 0, internalformat, tex->w, tex->h, 0, format, data_format, NULL); + else if (proxy == GL_PROXY_TEXTURE_3D) + glTexImage3D(proxy, 0, internalformat, tex->w, tex->h, tex->d, 0, format, data_format, NULL); + + glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width); + } + + /* Rescale */ + if (r_width > 0) { + switch (proxy) { + case GL_PROXY_TEXTURE_1D: + case GL_PROXY_TEXTURE_2D: + /* Do nothing for now */ + return false; + case GL_PROXY_TEXTURE_3D: + *rescaled_fpixels = GPU_texture_3D_rescale(tex, w, h, d, channels, fpixels); + return (bool)*rescaled_fpixels; + } + } + } + + return (r_width > 0); +} + +static GPUTexture *GPU_texture_create_nD( + int w, int h, int d, int n, const float *fpixels, + GPUTextureFormat data_type, int samples, + const bool can_rescale, char err_out[256]) +{ if (samples) { CLAMP_MAX(samples, GPU_max_color_texture_samples()); } @@ -106,280 +402,287 @@ static GPUTexture *GPU_texture_create_nD( GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->w = w; tex->h = h; + tex->d = d; + tex->samples = samples; tex->number = -1; tex->refcount = 1; - tex->target = (n == 1) ? GL_TEXTURE_1D : (samples ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D); - tex->target_base = (n == 1) ? GL_TEXTURE_1D : GL_TEXTURE_2D; - tex->depth = depth; - tex->fb_attachment = -1; + tex->format = data_type; + tex->components = gpu_texture_get_component_count(data_type); + tex->format_flag = 0; + if (n == 2) { + if (d == 0) + tex->target_base = tex->target = GL_TEXTURE_2D; + else + tex->target_base = tex->target = GL_TEXTURE_2D_ARRAY; + } + else if (n == 1) { + if (h == 0) + tex->target_base = tex->target = GL_TEXTURE_1D; + else + tex->target_base = tex->target = GL_TEXTURE_1D_ARRAY; + } + else if (n == 3) { + tex->target_base = tex->target = GL_TEXTURE_3D; + } + else { + /* should never happen */ + MEM_freeN(tex); + return NULL; + } + + if (samples && n == 2 && d == 0) + tex->target = GL_TEXTURE_2D_MULTISAMPLE; + + GLenum format, internalformat, data_format; + internalformat = gpu_texture_get_format(tex->components, data_type, &format, &data_format, + &tex->format_flag, &tex->bytesize); + + gpu_texture_memory_footprint_add(tex); + + /* Generate Texture object */ glGenTextures(1, &tex->bindcode); if (!tex->bindcode) { - if (err_out) { - BLI_snprintf(err_out, 256, "GPUTexture: texture create failed: %d", - (int)glGetError()); - } - else { - fprintf(stderr, "GPUTexture: texture create failed: %d\n", - (int)glGetError()); - } + if (err_out) + BLI_snprintf(err_out, 256, "GPUTexture: texture create failed"); + else + fprintf(stderr, "GPUTexture: texture create failed"); GPU_texture_free(tex); return NULL; } - if (!GPU_full_non_power_of_two_support()) { - tex->w = power_of_2_max_i(tex->w); - tex->h = power_of_2_max_i(tex->h); - } - - tex->number = 0; glBindTexture(tex->target, tex->bindcode); - if (depth) { - type = GL_UNSIGNED_BYTE; - format = GL_DEPTH_COMPONENT; - internalformat = GL_DEPTH_COMPONENT; - } - else { - type = GL_FLOAT; - - if (components == 4) { - format = GL_RGBA; - switch (hdr_type) { - case GPU_HDR_NONE: - internalformat = GL_RGBA8; - break; - /* the following formats rely on ARB_texture_float or OpenGL 3.0 */ - case GPU_HDR_HALF_FLOAT: - internalformat = GL_RGBA16F_ARB; - break; - case GPU_HDR_FULL_FLOAT: - internalformat = GL_RGBA32F_ARB; - break; - default: - break; - } - } - else if (components == 2) { - /* these formats rely on ARB_texture_rg or OpenGL 3.0 */ - format = GL_RG; - switch (hdr_type) { - case GPU_HDR_NONE: - internalformat = GL_RG8; - break; - case GPU_HDR_HALF_FLOAT: - internalformat = GL_RG16F; - break; - case GPU_HDR_FULL_FLOAT: - internalformat = GL_RG32F; - break; - default: - break; - } - } + /* Check if texture fit in VRAM */ + GLenum proxy = GL_PROXY_TEXTURE_2D; - if (fpixels && hdr_type == GPU_HDR_NONE) { - type = GL_UNSIGNED_BYTE; - pixels = GPU_texture_convert_pixels(w * h, fpixels); - } + if (n == 2) { + if (d > 0) + proxy = GL_PROXY_TEXTURE_2D_ARRAY; + } + else if (n == 1) { + if (h == 0) + proxy = GL_PROXY_TEXTURE_1D; + else + proxy = GL_PROXY_TEXTURE_1D_ARRAY; + } + else if (n == 3) { + proxy = GL_PROXY_TEXTURE_3D; } - if (tex->target == GL_TEXTURE_1D) { - glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL); + float *rescaled_fpixels = NULL; + bool valid = gpu_texture_try_alloc(tex, proxy, internalformat, format, data_format, tex->components, can_rescale, + fpixels, &rescaled_fpixels); + if (!valid) { + if (err_out) + BLI_snprintf(err_out, 256, "GPUTexture: texture alloc failed"); + else + fprintf(stderr, "GPUTexture: texture alloc failed. Not enough Video Memory."); + GPU_texture_free(tex); + return NULL; + } - if (fpixels) { - glTexSubImage1D(tex->target, 0, 0, w, format, type, - pixels ? pixels : fpixels); + /* Upload Texture */ + const float *pix = (rescaled_fpixels) ? rescaled_fpixels : fpixels; - if (tex->w > w) { - gpu_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, 1); - } - } - } - else { + if (tex->target == GL_TEXTURE_2D || + tex->target == GL_TEXTURE_2D_MULTISAMPLE || + tex->target == GL_TEXTURE_1D_ARRAY) + { if (samples) { glTexImage2DMultisample(tex->target, samples, internalformat, tex->w, tex->h, true); + if (pix) + glTexSubImage2D(tex->target, 0, 0, 0, tex->w, tex->h, format, data_format, pix); } else { - glTexImage2D(tex->target, 0, internalformat, tex->w, tex->h, 0, - format, type, NULL); - } - - if (fpixels) { - glTexSubImage2D(tex->target, 0, 0, 0, w, h, - format, type, pixels ? pixels : fpixels); - - if (tex->w > w) { - gpu_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, tex->h); - } - if (tex->h > h) { - gpu_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h - h); - } + glTexImage2D(tex->target, 0, internalformat, tex->w, tex->h, 0, format, data_format, pix); } } + else if (tex->target == GL_TEXTURE_1D) { + glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, data_format, pix); + } + else { + glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->d, 0, format, data_format, pix); + } - if (pixels) - MEM_freeN(pixels); + if (rescaled_fpixels) + MEM_freeN(rescaled_fpixels); - if (depth) { + /* Texture Parameters */ + if (GPU_texture_stencil(tex) || /* Does not support filtering */ + GPU_texture_integer(tex) || /* Does not support filtering */ + GPU_texture_depth(tex)) + { glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glTexParameteri(tex->target_base, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY); + glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - if (tex->target_base != GL_TEXTURE_1D) { - glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + if (GPU_texture_depth(tex)) { + glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + if (n > 1) { glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - else - glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + if (n > 2) { + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + glBindTexture(tex->target, 0); return tex; } - -GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels) +static GPUTexture *GPU_texture_cube_create( + int w, int d, + const float *fpixels_px, const float *fpixels_py, const float *fpixels_pz, + const float *fpixels_nx, const float *fpixels_ny, const float *fpixels_nz, + GPUTextureFormat data_type, + char err_out[256]) { - GLenum type, format, internalformat; - void *pixels = NULL; + GLenum format, internalformat, data_format; GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->w = w; - tex->h = h; - tex->depth = depth; + tex->h = w; + tex->d = d; + tex->samples = 0; tex->number = -1; tex->refcount = 1; - tex->target = GL_TEXTURE_3D; - tex->target_base = GL_TEXTURE_3D; + tex->format = data_type; + tex->components = gpu_texture_get_component_count(data_type); + tex->format_flag = GPU_FORMAT_CUBE; + + if (d == 0) { + tex->target_base = tex->target = GL_TEXTURE_CUBE_MAP; + } + else { + BLI_assert(false && "Cubemap array Not implemented yet"); + // tex->target_base = tex->target = GL_TEXTURE_CUBE_MAP_ARRAY; + } + internalformat = gpu_texture_get_format(tex->components, data_type, &format, &data_format, + &tex->format_flag, &tex->bytesize); + + gpu_texture_memory_footprint_add(tex); + + /* Generate Texture object */ glGenTextures(1, &tex->bindcode); if (!tex->bindcode) { - fprintf(stderr, "GPUTexture: texture create failed: %d\n", - (int)glGetError()); + if (err_out) + BLI_snprintf(err_out, 256, "GPUTexture: texture create failed"); + else + fprintf(stderr, "GPUTexture: texture create failed"); GPU_texture_free(tex); return NULL; } - tex->number = 0; glBindTexture(tex->target, tex->bindcode); - GPU_ASSERT_NO_GL_ERRORS("3D glBindTexture"); - - type = GL_FLOAT; - if (channels == 4) { - format = GL_RGBA; - internalformat = GL_RGBA8; + /* Upload Texture */ + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_px); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_py); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_pz); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_nx); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_ny); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalformat, tex->w, tex->h, 0, format, data_format, fpixels_nz); + + /* Texture Parameters */ + if (GPU_texture_stencil(tex) || /* Does not support filtering */ + GPU_texture_integer(tex) || /* Does not support filtering */ + GPU_texture_depth(tex)) + { + glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { - format = GL_RED; - internalformat = GL_INTENSITY8; + glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - /* 3D textures are quite heavy, test if it's possible to create them first */ - glTexImage3D(GL_PROXY_TEXTURE_3D, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL); - - bool rescale = false; - int r_width; - - glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width); - - while (r_width == 0) { - rescale = true; - tex->w /= 2; - tex->h /= 2; - tex->depth /= 2; - glTexImage3D(GL_PROXY_TEXTURE_3D, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL); - glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width); + if (GPU_texture_depth(tex)) { + glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_NONE); + glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); } - /* really unlikely to happen but keep this just in case */ - tex->w = max_ii(tex->w, 1); - tex->h = max_ii(tex->h, 1); - tex->depth = max_ii(tex->depth, 1); - -#if 0 - if (fpixels) - pixels = GPU_texture_convert_pixels(w*h*depth, fpixels); -#endif - - GPU_ASSERT_NO_GL_ERRORS("3D glTexImage3D"); + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - /* hardcore stuff, 3D texture rescaling - warning, this is gonna hurt your performance a lot, but we need it - * for gooseberry */ - if (rescale && fpixels) { - /* FIXME: should these be floating point? */ - const unsigned int xf = w / tex->w, yf = h / tex->h, zf = depth / tex->depth; - float *tex3d = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->depth, "tex3d"); - - GPU_print_error_debug("You need to scale a 3D texture, feel the pain!"); + glBindTexture(tex->target, 0); - for (unsigned k = 0; k < tex->depth; k++) { - for (unsigned j = 0; j < tex->h; j++) { - for (unsigned i = 0; i < tex->w; i++) { - /* obviously doing nearest filtering here, - * it's going to be slow in any case, let's not make it worse */ - float xb = i * xf; - float yb = j * yf; - float zb = k * zf; - unsigned int offset = k * (tex->w * tex->h) + i * tex->h + j; - unsigned int offset_orig = (zb) * (w * h) + (xb) * h + (yb); - - if (channels == 4) { - tex3d[offset * 4] = fpixels[offset_orig * 4]; - tex3d[offset * 4 + 1] = fpixels[offset_orig * 4 + 1]; - tex3d[offset * 4 + 2] = fpixels[offset_orig * 4 + 2]; - tex3d[offset * 4 + 3] = fpixels[offset_orig * 4 + 3]; - } - else - tex3d[offset] = fpixels[offset_orig]; - } - } - } - - glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, tex3d); + return tex; +} - MEM_freeN(tex3d); - } - else { - if (fpixels) { - glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, fpixels); - GPU_ASSERT_NO_GL_ERRORS("3D glTexSubImage3D"); - } +/* Special buffer textures. data_type must be compatible with the buffer content. */ +GPUTexture *GPU_texture_create_buffer(GPUTextureFormat data_type, const GLuint buffer) +{ + GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + tex->number = -1; + tex->refcount = 1; + tex->format = data_type; + tex->components = gpu_texture_get_component_count(data_type); + tex->format_flag = 0; + tex->target_base = tex->target = GL_TEXTURE_BUFFER; + + GLenum format, internalformat, data_format; + internalformat = gpu_texture_get_format(tex->components, data_type, &format, &data_format, + &tex->format_flag, &tex->bytesize); + + if (!(ELEM(data_type, GPU_R8, GPU_R16) || + ELEM(data_type, GPU_R16F, GPU_R32F) || + ELEM(data_type, GPU_R8I, GPU_R16I, GPU_R32I) || + ELEM(data_type, GPU_R8UI, GPU_R16UI, GPU_R32UI) || + ELEM(data_type, GPU_RG8, GPU_RG16) || + ELEM(data_type, GPU_RG16F, GPU_RG32F) || + ELEM(data_type, GPU_RG8I, GPU_RG16I, GPU_RG32I) || + ELEM(data_type, GPU_RG8UI, GPU_RG16UI, GPU_RG32UI) || + //ELEM(data_type, GPU_RGB32F, GPU_RGB32I, GPU_RGB32UI) || /* Not available until gl 4.0 */ + ELEM(data_type, GPU_RGBA8, GPU_RGBA16) || + ELEM(data_type, GPU_RGBA16F, GPU_RGBA32F) || + ELEM(data_type, GPU_RGBA8I, GPU_RGBA16I, GPU_RGBA32I) || + ELEM(data_type, GPU_RGBA8UI, GPU_RGBA16UI, GPU_RGBA32UI))) + { + fprintf(stderr, "GPUTexture: invalid format for texture buffer"); + GPU_texture_free(tex); + return NULL; } + /* Generate Texture object */ + glGenTextures(1, &tex->bindcode); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - if (pixels) - MEM_freeN(pixels); + if (!tex->bindcode) { + fprintf(stderr, "GPUTexture: texture create failed"); + GPU_texture_free(tex); + BLI_assert(0 && "glGenTextures failled: Are you sure a valid OGL context is active on this thread?"); + return NULL; + } - GPU_texture_unbind(tex); + glBindTexture(tex->target, tex->bindcode); + glTexBuffer(tex->target, internalformat, buffer); + glBindTexture(tex->target, 0); return tex; } -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget, bool is_data, double time, int mipmap) +GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget, bool is_data, double UNUSED(time), int mipmap) { int gputt; /* this binds a texture, so that's why to restore it to 0 */ - GLint bindcode = GPU_verify_image(ima, iuser, textarget, 0, 0, mipmap, is_data); - GPU_update_image_time(ima, time); + GLint bindcode = GPU_verify_image(ima, iuser, textarget, 0, mipmap, is_data); /* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP * these values are correct for glDisable, so textarget can be safely used in * GPU_texture_bind/GPU_texture_unbind through tex->target_base */ + /* (is any of this obsolete now that we don't glEnable/Disable textures?) */ if (textarget == GL_TEXTURE_2D) gputt = TEXTARGET_TEXTURE_2D; else @@ -398,14 +701,17 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget tex->target = textarget; tex->target_base = textarget; tex->fromblender = 1; + tex->format = -1; + tex->components = -1; + tex->samples = 0; ima->gputexture[gputt] = tex; if (!glIsTexture(tex->bindcode)) { - GPU_ASSERT_NO_GL_ERRORS("Blender Texture Not Loaded"); + GPU_print_error_debug("Blender Texture Not Loaded"); } else { - GLint w, h, border; + GLint w, h; GLenum gettarget; @@ -417,10 +723,8 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget glBindTexture(textarget, tex->bindcode); glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w); glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_BORDER, &border); - - tex->w = w - border; - tex->h = h - border; + tex->w = w; + tex->h = h; } glBindTexture(textarget, 0); @@ -452,11 +756,13 @@ GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) tex->refcount = 1; tex->target = GL_TEXTURE_2D; tex->target_base = GL_TEXTURE_2D; + tex->format = -1; + tex->components = -1; prv->gputexture[0] = tex; if (!glIsTexture(tex->bindcode)) { - GPU_ASSERT_NO_GL_ERRORS("Blender Texture Not Loaded"); + GPU_print_error_debug("Blender Texture Not Loaded"); } else { GLint w, h; @@ -475,114 +781,165 @@ GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) } -GPUTexture *GPU_texture_create_1D(int w, const float *fpixels, char err_out[256]) +GPUTexture *GPU_texture_create_1D( + int w, GPUTextureFormat data_type, const float *pixels, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, 1, 1, fpixels, 0, GPU_HDR_NONE, 4, 0, err_out); - - if (tex) - GPU_texture_unbind(tex); - - return tex; + return GPU_texture_create_nD(w, 0, 0, 1, pixels, data_type, 0, false, err_out); } -GPUTexture *GPU_texture_create_2D(int w, int h, const float *fpixels, GPUHDRType hdr, char err_out[256]) +GPUTexture *GPU_texture_create_2D( + int w, int h, GPUTextureFormat data_type, const float *pixels, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, hdr, 4, 0, err_out); - - if (tex) - GPU_texture_unbind(tex); - - return tex; + return GPU_texture_create_nD(w, h, 0, 2, pixels, data_type, 0, false, err_out); } + GPUTexture *GPU_texture_create_2D_multisample( - int w, int h, const float *fpixels, GPUHDRType hdr, int samples, char err_out[256]) + int w, int h, GPUTextureFormat data_type, const float *pixels, int samples, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, hdr, 4, samples, err_out); - - if (tex) - GPU_texture_unbind(tex); - - return tex; + return GPU_texture_create_nD(w, h, 0, 2, pixels, data_type, samples, false, err_out); } -GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]) +GPUTexture *GPU_texture_create_2D_array( + int w, int h, int d, GPUTextureFormat data_type, const float *pixels, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, GPU_HDR_NONE, 1, 0, err_out); - - if (tex) - GPU_texture_unbind(tex); - - return tex; + return GPU_texture_create_nD(w, h, d, 2, pixels, data_type, 0, false, err_out); } -GPUTexture *GPU_texture_create_depth_multisample(int w, int h, int samples, char err_out[256]) -{ - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, GPU_HDR_NONE, 1, samples, err_out); - if (tex) - GPU_texture_unbind(tex); - - return tex; +GPUTexture *GPU_texture_create_3D( + int w, int h, int d, GPUTextureFormat data_type, const float *pixels, char err_out[256]) +{ + return GPU_texture_create_nD(w, h, d, 3, pixels, data_type, 0, true, err_out); } -/** - * A shadow map for VSM needs two components (depth and depth^2) - */ -GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]) +GPUTexture *GPU_texture_create_cube( + int w, GPUTextureFormat data_type, const float *fpixels, char err_out[256]) { - GPUTexture *tex = GPU_texture_create_nD(size, size, 2, NULL, 0, GPU_HDR_FULL_FLOAT, 2, 0, err_out); - - if (tex) { - /* Now we tweak some of the settings */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - GPU_texture_unbind(tex); + const float *fpixels_px, *fpixels_py, *fpixels_pz, *fpixels_nx, *fpixels_ny, *fpixels_nz; + const int channels = gpu_texture_get_component_count(data_type); + + if (fpixels) { + fpixels_px = fpixels + 0 * w * w * channels; + fpixels_nx = fpixels + 1 * w * w * channels; + fpixels_py = fpixels + 2 * w * w * channels; + fpixels_ny = fpixels + 3 * w * w * channels; + fpixels_pz = fpixels + 4 * w * w * channels; + fpixels_nz = fpixels + 5 * w * w * channels; + } + else { + fpixels_px = fpixels_py = fpixels_pz = fpixels_nx = fpixels_ny = fpixels_nz = NULL; } - return tex; + return GPU_texture_cube_create(w, 0, fpixels_px, fpixels_py, fpixels_pz, fpixels_nx, fpixels_ny, fpixels_nz, + data_type, err_out); } -GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]) +GPUTexture *GPU_texture_create_from_vertbuf(Gwn_VertBuf *vert) { - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out); + Gwn_VertFormat *format = &vert->format; + Gwn_VertAttr *attr = &format->attribs[0]; - if (tex) { - /* Now we tweak some of the settings */ - if (repeat) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + /* Detect incompatible cases (not supported by texture buffers) */ + BLI_assert(format->attrib_ct == 1 && vert->vbo_id != 0); + BLI_assert(attr->comp_ct != 3); /* Not until OGL 4.0 */ + BLI_assert(attr->comp_type != GWN_COMP_I10); + BLI_assert(attr->fetch_mode != GWN_FETCH_INT_TO_FLOAT); + + unsigned int byte_per_comp = attr->sz / attr->comp_ct; + bool is_uint = ELEM(attr->comp_type, GWN_COMP_U8, GWN_COMP_U16, GWN_COMP_U32); - GPU_texture_unbind(tex); + /* Cannot fetch signed int or 32bit ints as normalized float. */ + if (attr->fetch_mode == GWN_FETCH_INT_TO_FLOAT_UNIT) { + BLI_assert(is_uint || byte_per_comp <= 2); } - return tex; + GPUTextureFormat data_type; + switch (attr->fetch_mode) { + case GWN_FETCH_FLOAT: + switch (attr->comp_ct) { + case 1: data_type = GPU_R32F; break; + case 2: data_type = GPU_RG32F; break; + // case 3: data_type = GPU_RGB32F; break; /* Not supported */ + default: data_type = GPU_RGBA32F; break; + } + break; + case GWN_FETCH_INT: + switch (attr->comp_ct) { + case 1: + switch (byte_per_comp) { + case 1: data_type = (is_uint) ? GPU_R8UI : GPU_R8I; break; + case 2: data_type = (is_uint) ? GPU_R16UI : GPU_R16I; break; + default: data_type = (is_uint) ? GPU_R32UI : GPU_R32I; break; + } + break; + case 2: + switch (byte_per_comp) { + case 1: data_type = (is_uint) ? GPU_RG8UI : GPU_RG8I; break; + case 2: data_type = (is_uint) ? GPU_RG16UI : GPU_RG16I; break; + default: data_type = (is_uint) ? GPU_RG32UI : GPU_RG32I; break; + } + break; + default: + switch (byte_per_comp) { + case 1: data_type = (is_uint) ? GPU_RGBA8UI : GPU_RGBA8I; break; + case 2: data_type = (is_uint) ? GPU_RGBA16UI : GPU_RGBA16I; break; + default: data_type = (is_uint) ? GPU_RGBA32UI : GPU_RGBA32I; break; + } + break; + } + break; + case GWN_FETCH_INT_TO_FLOAT_UNIT: + switch (attr->comp_ct) { + case 1: data_type = (byte_per_comp == 1) ? GPU_R8 : GPU_R16; break; + case 2: data_type = (byte_per_comp == 1) ? GPU_RG8 : GPU_RG16; break; + default: data_type = (byte_per_comp == 1) ? GPU_RGBA8 : GPU_RGBA16; break; + } + break; + default: + BLI_assert(0); + return NULL; + } + + return GPU_texture_create_buffer(data_type, vert->vbo_id); } -GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256]) +void GPU_texture_update(GPUTexture *tex, const float *pixels) { - GPUTexture *tex = GPU_texture_create_nD(w, 0, 1, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out); + BLI_assert(tex->format > -1); + BLI_assert(tex->components > -1); - if (tex) { - /* Now we tweak some of the settings */ - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GLenum format, data_format; + gpu_texture_get_format(tex->components, tex->format, &format, &data_format, + &tex->format_flag, &tex->bytesize); + + glBindTexture(tex->target, tex->bindcode); - GPU_texture_unbind(tex); + switch (tex->target) { + case GL_TEXTURE_2D: + case GL_TEXTURE_2D_MULTISAMPLE: + case GL_TEXTURE_1D_ARRAY: + glTexSubImage2D(tex->target, 0, 0, 0, tex->w, tex->h, format, data_format, pixels); + break; + case GL_TEXTURE_1D: + glTexSubImage1D(tex->target, 0, 0, tex->w, format, data_format, pixels); + break; + case GL_TEXTURE_3D: + case GL_TEXTURE_2D_ARRAY: + glTexSubImage3D(tex->target, 0, 0, 0, 0, tex->w, tex->h, tex->d, format, data_format, pixels); + break; + default: + BLI_assert(!"tex->target mode not supported"); } - return tex; + glBindTexture(tex->target, 0); } void GPU_invalid_tex_init(void) { + memory_usage = 0; const float color[4] = {1.0f, 0.0f, 1.0f, 1.0f}; - GG.invalid_tex_1D = GPU_texture_create_1D(1, color, NULL); - GG.invalid_tex_2D = GPU_texture_create_2D(1, 1, color, GPU_HDR_NONE, NULL); - GG.invalid_tex_3D = GPU_texture_create_3D(1, 1, 1, 4, color); + GG.invalid_tex_1D = GPU_texture_create_1D(1, GPU_RGBA8, color, NULL); + GG.invalid_tex_2D = GPU_texture_create_2D(1, 1, GPU_RGBA8, color, NULL); + GG.invalid_tex_3D = GPU_texture_create_3D(1, 1, 1, GPU_RGBA8, color, NULL); } void GPU_invalid_tex_bind(int mode) @@ -610,61 +967,45 @@ void GPU_invalid_tex_free(void) GPU_texture_free(GG.invalid_tex_3D); } - void GPU_texture_bind(GPUTexture *tex, int number) { + BLI_assert(number >= 0); + if (number >= GPU_max_textures()) { fprintf(stderr, "Not enough texture slots.\n"); return; } if ((G.debug & G_DEBUG)) { - if (tex->fb && GPU_framebuffer_bound(tex->fb)) { - fprintf(stderr, "Feedback loop warning!: Attempting to bind texture attached to current framebuffer!\n"); + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] && GPU_framebuffer_bound(tex->fb[i])) { + fprintf(stderr, "Feedback loop warning!: Attempting to bind " + "texture attached to current framebuffer!\n"); + BLI_assert(0); /* Should never happen! */ + break; + } } } - if (number < 0) - return; - - GPU_ASSERT_NO_GL_ERRORS("Pre Texture Bind"); + glActiveTexture(GL_TEXTURE0 + number); - GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number); - if (number != 0) glActiveTexture(arbnumber); - if (tex->bindcode != 0) { - glBindTexture(tex->target_base, tex->bindcode); - } + if (tex->bindcode != 0) + glBindTexture(tex->target, tex->bindcode); else GPU_invalid_tex_bind(tex->target_base); - glEnable(tex->target_base); - if (number != 0) glActiveTexture(GL_TEXTURE0); tex->number = number; - - GPU_ASSERT_NO_GL_ERRORS("Post Texture Bind"); } void GPU_texture_unbind(GPUTexture *tex) { - if (tex->number >= GPU_max_textures()) { - fprintf(stderr, "Not enough texture slots.\n"); - return; - } - if (tex->number == -1) return; - GPU_ASSERT_NO_GL_ERRORS("Pre Texture Unbind"); - - GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); - if (tex->number != 0) glActiveTexture(arbnumber); - glBindTexture(tex->target_base, 0); - glDisable(tex->target_base); - if (tex->number != 0) glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0 + tex->number); + glBindTexture(tex->target, 0); tex->number = -1; - - GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); } int GPU_texture_bound_number(GPUTexture *tex) @@ -672,39 +1013,89 @@ int GPU_texture_bound_number(GPUTexture *tex) return tex->number; } -void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter) +#define WARN_NOT_BOUND(_tex) do { \ + if (_tex->number == -1) { \ + fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ + BLI_assert(0); \ + return; \ + } \ +} while (0); + +void GPU_texture_generate_mipmap(GPUTexture *tex) { - if (tex->number >= GPU_max_textures()) { - fprintf(stderr, "Not enough texture slots.\n"); - return; - } + WARN_NOT_BOUND(tex); - if (tex->number == -1) + glActiveTexture(GL_TEXTURE0 + tex->number); + glGenerateMipmap(tex->target_base); +} + +void GPU_texture_compare_mode(GPUTexture *tex, bool use_compare) +{ + WARN_NOT_BOUND(tex); + + /* Could become an assertion ? (fclem) */ + if (!GPU_texture_depth(tex)) return; - GPU_ASSERT_NO_GL_ERRORS("Pre Texture Unbind"); + GLenum mode = (use_compare) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; - GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); - if (tex->number != 0) glActiveTexture(arbnumber); + glActiveTexture(GL_TEXTURE0 + tex->number); + glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, mode); +} - if (tex->depth) { - if (compare) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); - } +void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter) +{ + WARN_NOT_BOUND(tex); - if (use_filter) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - if (tex->number != 0) glActiveTexture(GL_TEXTURE0); + /* Stencil and integer format does not support filtering. */ + BLI_assert(!use_filter || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex))); + + GLenum filter = (use_filter) ? GL_LINEAR : GL_NEAREST; + + glActiveTexture(GL_TEXTURE0 + tex->number); + glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, filter); +} + +void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter) +{ + WARN_NOT_BOUND(tex); + + /* Stencil and integer format does not support filtering. */ + BLI_assert((!use_filter && !use_mipmap) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex))); + + GLenum filter = (use_filter) ? GL_LINEAR : GL_NEAREST; + GLenum mipmap = (use_filter) + ? (use_mipmap) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR + : (use_mipmap) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST; + + glActiveTexture(GL_TEXTURE0 + tex->number); + glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, mipmap); + glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, filter); +} - GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); +void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat) +{ + WARN_NOT_BOUND(tex); + + GLenum repeat = (use_repeat) ? GL_REPEAT : GL_CLAMP_TO_EDGE; + + glActiveTexture(GL_TEXTURE0 + tex->number); + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, repeat); + if (tex->target_base != GL_TEXTURE_1D) + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, repeat); + if (tex->target_base == GL_TEXTURE_3D) + glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_R, repeat); +} + +static void gpu_texture_delete(GPUTexture *tex) +{ + if (tex->bindcode && !tex->fromblender) + glDeleteTextures(1, &tex->bindcode); + + gpu_texture_memory_footprint_remove(tex); + + MEM_freeN(tex); } void GPU_texture_free(GPUTexture *tex) @@ -715,15 +1106,46 @@ void GPU_texture_free(GPUTexture *tex) fprintf(stderr, "GPUTexture: negative refcount\n"); if (tex->refcount == 0) { - if (tex->fb) - GPU_framebuffer_texture_detach(tex); - if (tex->bindcode && !tex->fromblender) - glDeleteTextures(1, &tex->bindcode); + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] != NULL) { + GPU_framebuffer_texture_detach_slot(tex->fb[i], tex, tex->fb_attachment[i]); + } + } - MEM_freeN(tex); + /* TODO(fclem): Check if the thread has an ogl context. */ + if (BLI_thread_is_main()) { + gpu_texture_delete(tex); + } + else { + BLI_mutex_lock(&g_orphan_lock); + BLI_addtail(&g_orphaned_tex, BLI_genericNodeN(tex)); + BLI_mutex_unlock(&g_orphan_lock); + } } } +void GPU_texture_init_orphans(void) +{ + BLI_mutex_init(&g_orphan_lock); +} + +void GPU_texture_delete_orphans(void) +{ + BLI_mutex_lock(&g_orphan_lock); + LinkData *link; + while ((link = BLI_pophead(&g_orphaned_tex))) { + gpu_texture_delete((GPUTexture *)link->data); + MEM_freeN(link); + } + BLI_mutex_unlock(&g_orphan_lock); +} + +void GPU_texture_exit_orphans(void) +{ + GPU_texture_delete_orphans(); + BLI_mutex_end(&g_orphan_lock); +} + void GPU_texture_ref(GPUTexture *tex) { tex->refcount++; @@ -744,29 +1166,64 @@ int GPU_texture_height(const GPUTexture *tex) return tex->h; } -int GPU_texture_depth(const GPUTexture *tex) +GPUTextureFormat GPU_texture_format(const GPUTexture *tex) { - return tex->depth; + return tex->format; } -int GPU_texture_opengl_bindcode(const GPUTexture *tex) +int GPU_texture_samples(const GPUTexture *tex) { - return tex->bindcode; + return tex->samples; +} + +bool GPU_texture_depth(const GPUTexture *tex) +{ + return (tex->format_flag & GPU_FORMAT_DEPTH) != 0; +} + +bool GPU_texture_stencil(const GPUTexture *tex) +{ + return (tex->format_flag & GPU_FORMAT_STENCIL) != 0; +} + +bool GPU_texture_integer(const GPUTexture *tex) +{ + return (tex->format_flag & GPU_FORMAT_INTEGER) != 0; } -GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex) +bool GPU_texture_cube(const GPUTexture *tex) { - return tex->fb; + return (tex->format_flag & GPU_FORMAT_CUBE) != 0; } -int GPU_texture_framebuffer_attachment(GPUTexture *tex) +int GPU_texture_opengl_bindcode(const GPUTexture *tex) { - return tex->fb_attachment; + return tex->bindcode; } -void GPU_texture_framebuffer_set(GPUTexture *tex, GPUFrameBuffer *fb, int attachment) +void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int attachment) { - tex->fb = fb; - tex->fb_attachment = attachment; + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] == NULL) { + tex->fb[i] = fb; + tex->fb_attachment[i] = attachment; + return; + } + } + + BLI_assert(!"Error: Texture: Not enough Framebuffer slots"); } +/* Return previous attachment point */ +int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) +{ + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; ++i) { + if (tex->fb[i] == fb) { + tex->fb[i] = NULL; + return tex->fb_attachment[i]; + } + } + + BLI_assert(!"Error: Texture: Framebuffer is not attached"); + return 0; +} diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.c new file mode 100644 index 00000000000..afd43600d9b --- /dev/null +++ b/source/blender/gpu/intern/gpu_uniformbuffer.c @@ -0,0 +1,378 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Clement Foucault. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file gpu_uniformbuffer.c + * \ingroup gpu + */ + +#include <string.h> +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "gpu_codegen.h" + +#include "GPU_extensions.h" +#include "GPU_glew.h" +#include "GPU_material.h" +#include "GPU_uniformbuffer.h" + +typedef enum GPUUniformBufferFlag { + GPU_UBO_FLAG_INITIALIZED = (1 << 0), + GPU_UBO_FLAG_DIRTY = (1 << 1), +} GPUUniformBufferFlag; + +typedef enum GPUUniformBufferType { + GPU_UBO_STATIC = 0, + GPU_UBO_DYNAMIC = 1, +} GPUUniformBufferType; + +struct GPUUniformBuffer { + int size; /* in bytes */ + GLuint bindcode; /* opengl identifier for UBO */ + int bindpoint; /* current binding point */ + GPUUniformBufferType type; +}; + +#define GPUUniformBufferStatic GPUUniformBuffer + +typedef struct GPUUniformBufferDynamic { + GPUUniformBuffer buffer; + ListBase items; /* GPUUniformBufferDynamicItem */ + void *data; + char flag; +} GPUUniformBufferDynamic; + +struct GPUUniformBufferDynamicItem { + struct GPUUniformBufferDynamicItem *next, *prev; + GPUType gputype; + float *data; + int size; +}; + + +/* Prototypes */ +static GPUType get_padded_gpu_type(struct LinkData *link); +static void gpu_uniformbuffer_inputs_sort(struct ListBase *inputs); + +static GPUUniformBufferDynamicItem *gpu_uniformbuffer_populate( + GPUUniformBufferDynamic *ubo, const GPUType gputype, float *num); + +/* Only support up to this type, if you want to extend it, make sure the + * padding logic is correct for the new types. */ +#define MAX_UBO_GPU_TYPE GPU_VEC4 + +static void gpu_uniformbuffer_initialize(GPUUniformBuffer *ubo, const void *data) +{ + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]) +{ + GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBufferStatic), "GPUUniformBufferStatic"); + ubo->size = size; + ubo->bindpoint = -1; + + /* Generate Buffer object */ + glGenBuffers(1, &ubo->bindcode); + + if (!ubo->bindcode) { + if (err_out) + BLI_snprintf(err_out, 256, "GPUUniformBuffer: UBO create failed"); + GPU_uniformbuffer_free(ubo); + return NULL; + } + + if (ubo->size > GPU_max_ubo_size()) { + if (err_out) + BLI_snprintf(err_out, 256, "GPUUniformBuffer: UBO too big"); + GPU_uniformbuffer_free(ubo); + return NULL; + } + + gpu_uniformbuffer_initialize(ubo, data); + return ubo; +} + +/** + * Create dynamic UBO from parameters + * Return NULL if failed to create or if \param inputs is empty. + * + * \param inputs ListBase of BLI_genericNodeN(GPUInput) + */ +GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) +{ + /* There is no point on creating an UBO if there is no arguments. */ + if (BLI_listbase_is_empty(inputs)) { + return NULL; + } + + GPUUniformBufferDynamic *ubo = MEM_callocN(sizeof(GPUUniformBufferDynamic), "GPUUniformBufferDynamic"); + ubo->buffer.type = GPU_UBO_DYNAMIC; + ubo->buffer.bindpoint = -1; + ubo->flag = GPU_UBO_FLAG_DIRTY; + + /* Generate Buffer object. */ + glGenBuffers(1, &ubo->buffer.bindcode); + + if (!ubo->buffer.bindcode) { + if (err_out) + BLI_snprintf(err_out, 256, "GPUUniformBuffer: UBO create failed"); + GPU_uniformbuffer_free(&ubo->buffer); + return NULL; + } + + if (ubo->buffer.size > GPU_max_ubo_size()) { + if (err_out) + BLI_snprintf(err_out, 256, "GPUUniformBuffer: UBO too big"); + GPU_uniformbuffer_free(&ubo->buffer); + return NULL; + } + + /* Make sure we comply to the ubo alignment requirements. */ + gpu_uniformbuffer_inputs_sort(inputs); + + for (LinkData *link = inputs->first; link; link = link->next) { + GPUInput *input = link->data; + GPUType gputype = get_padded_gpu_type(link); + gpu_uniformbuffer_populate(ubo, gputype, input->dynamicvec); + } + + ubo->data = MEM_mallocN(ubo->buffer.size, __func__); + + /* Initialize buffer data. */ + GPU_uniformbuffer_dynamic_update(&ubo->buffer); + return &ubo->buffer; +} + +/** + * Free the data, and clean the items list. + */ +static void gpu_uniformbuffer_dynamic_reset(GPUUniformBufferDynamic *ubo) +{ + ubo->buffer.size = 0; + if (ubo->data) { + MEM_freeN(ubo->data); + } + BLI_freelistN(&ubo->items); +} + +void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) +{ + if (ubo->type == GPU_UBO_DYNAMIC) { + gpu_uniformbuffer_dynamic_reset((GPUUniformBufferDynamic *)ubo); + } + + glDeleteBuffers(1, &ubo->bindcode); + MEM_freeN(ubo); +} + +static void gpu_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +{ + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +{ + BLI_assert(ubo->type == GPU_UBO_STATIC); + gpu_uniformbuffer_update(ubo, data); +} + +/** + * We need to recalculate the internal data, and re-generate it + * from its populated items. + */ +void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_) +{ + BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); + GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; + + float *offset = ubo->data; + for (GPUUniformBufferDynamicItem *item = ubo->items.first; item; item = item->next) { + memcpy(offset, item->data, item->size); + offset += item->gputype; + } + + if (ubo->flag & GPU_UBO_FLAG_INITIALIZED) { + gpu_uniformbuffer_update(ubo_, ubo->data); + } + else { + ubo->flag |= GPU_UBO_FLAG_INITIALIZED; + gpu_uniformbuffer_initialize(ubo_, ubo->data); + } + + ubo->flag &= ~GPU_UBO_FLAG_DIRTY; +} + +/** + * We need to pad some data types (vec3) on the C side + * To match the GPU expected memory block alignment. + */ +static GPUType get_padded_gpu_type(LinkData *link) +{ + GPUInput *input = link->data; + GPUType gputype = input->type; + + /* Unless the vec3 is followed by a float we need to treat it as a vec4. */ + if (gputype == GPU_VEC3 && + (link->next != NULL) && + (((GPUInput *)link->next->data)->type != GPU_FLOAT)) + { + gputype = GPU_VEC4; + } + + return gputype; +} + +/** + * Returns 1 if the first item shold be after second item. + * We make sure the vec4 uniforms come first. + */ +static int inputs_cmp(const void *a, const void *b) +{ + const LinkData *link_a = a, *link_b = b; + const GPUInput *input_a = link_a->data, *input_b = link_b->data; + return input_a->type < input_b->type ? 1 : 0; +} + +/** + * Make sure we respect the expected alignment of UBOs. + * vec4, pad vec3 as vec4, then vec2, then floats. + */ +static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) +{ + /* Order them as vec4, vec3, vec2, float. */ + BLI_listbase_sort(inputs, inputs_cmp); + + /* Creates a lookup table for the different types; */ + LinkData *inputs_lookup[MAX_UBO_GPU_TYPE + 1] = {NULL}; + GPUType cur_type = MAX_UBO_GPU_TYPE + 1; + + for (LinkData *link = inputs->first; link; link = link->next) { + GPUInput *input = link->data; + if (input->type == cur_type) { + continue; + } + else { + inputs_lookup[input->type] = link; + cur_type = input->type; + } + } + + /* If there is no GPU_VEC3 there is no need for alignment. */ + if (inputs_lookup[GPU_VEC3] == NULL) { + return; + } + + LinkData *link = inputs_lookup[GPU_VEC3]; + while (link != NULL && ((GPUInput *)link->data)->type == GPU_VEC3) { + LinkData *link_next = link->next; + + /* If GPU_VEC3 is followed by nothing or a GPU_FLOAT, no need for aligment. */ + if ((link_next == NULL) || + ((GPUInput *)link_next->data)->type == GPU_FLOAT) + { + break; + } + + /* If there is a float, move it next to current vec3. */ + if (inputs_lookup[GPU_FLOAT] != NULL) { + LinkData *float_input = inputs_lookup[GPU_FLOAT]; + inputs_lookup[GPU_FLOAT] = float_input->next; + + BLI_remlink(inputs, float_input); + BLI_insertlinkafter(inputs, link, float_input); + } + + link = link_next; + } +} + +/** + * This may now happen from the main thread, so we can't update the UBO + * We simply flag it as dirty + */ +static GPUUniformBufferDynamicItem *gpu_uniformbuffer_populate( + GPUUniformBufferDynamic *ubo, const GPUType gputype, float *num) +{ + BLI_assert(gputype <= MAX_UBO_GPU_TYPE); + GPUUniformBufferDynamicItem *item = MEM_callocN(sizeof(GPUUniformBufferDynamicItem), __func__); + + item->gputype = gputype; + item->data = num; + item->size = gputype * sizeof(float); + ubo->buffer.size += item->size; + + ubo->flag |= GPU_UBO_FLAG_DIRTY; + BLI_addtail(&ubo->items, item); + + return item; +} + +void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) +{ + if (number >= GPU_max_ubo_binds()) { + fprintf(stderr, "Not enough UBO slots.\n"); + return; + } + + if (ubo->type == GPU_UBO_DYNAMIC) { + GPUUniformBufferDynamic *ubo_dynamic = (GPUUniformBufferDynamic *)ubo; + if (ubo_dynamic->flag & GPU_UBO_FLAG_DIRTY) { + GPU_uniformbuffer_dynamic_update(ubo); + } + } + + if (ubo->bindcode != 0) { + glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); + } + + ubo->bindpoint = number; +} + +void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) +{ + ubo->bindpoint = -1; +} + +int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo) +{ + return ubo->bindpoint; +} + +void GPU_uniformbuffer_tag_dirty(GPUUniformBuffer *ubo_) +{ + BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); + GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; + ubo->flag |= GPU_UBO_FLAG_DIRTY; +} + +#undef MAX_UBO_GPU_TYPE diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c new file mode 100644 index 00000000000..0bf215f31a8 --- /dev/null +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -0,0 +1,644 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/gpu/intern/gpu_viewport.c + * \ingroup gpu + * + * System that manages viewport drawing. + */ + +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_mempool.h" + +#include "BIF_gl.h" + +#include "DNA_vec_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_global.h" + +#include "GPU_framebuffer.h" +#include "GPU_glew.h" +#include "GPU_immediate.h" +#include "GPU_texture.h" +#include "GPU_viewport.h" +#include "GPU_draw.h" + +#include "DRW_engine.h" + +#include "MEM_guardedalloc.h" + +static const int default_fbl_len = (sizeof(DefaultFramebufferList)) / sizeof(void *); +static const int default_txl_len = (sizeof(DefaultTextureList)) / sizeof(void *); + +/* Maximum number of simultaneous engine enabled at the same time. + * Setting it lower than the real number will do lead to + * higher VRAM usage due to sub-efficient buffer reuse. */ +#define MAX_ENGINE_BUFFER_SHARING 5 + +typedef struct ViewportTempTexture { + struct ViewportTempTexture *next, *prev; + void *user[MAX_ENGINE_BUFFER_SHARING]; + GPUTexture *texture; +} ViewportTempTexture; + +struct GPUViewport { + int size[2]; + int samples; + int flag; + + ListBase data; /* ViewportEngineData wrapped in LinkData */ + unsigned int data_hash; /* If hash mismatch we free all ViewportEngineData in this viewport */ + + DefaultFramebufferList *fbl; + DefaultTextureList *txl; + + ViewportMemoryPool vmempool; /* Used for rendering data structure. */ + struct DRWInstanceDataList *idatalist; /* Used for rendering data structure. */ + + ListBase tex_pool; /* ViewportTempTexture list : Temporary textures shared across draw engines */ + + /* Profiling data */ + double cache_time; +}; + +enum { + DO_UPDATE = (1 << 0), +}; + +static void gpu_viewport_buffers_free(FramebufferList *fbl, int fbl_len, TextureList *txl, int txl_len); +static void gpu_viewport_storage_free(StorageList *stl, int stl_len); +static void gpu_viewport_passes_free(PassList *psl, int psl_len); +static void gpu_viewport_texture_pool_free(GPUViewport *viewport); +static void gpu_viewport_default_fb_create(GPUViewport *viewport); + +void GPU_viewport_tag_update(GPUViewport *viewport) +{ + viewport->flag |= DO_UPDATE; +} + +bool GPU_viewport_do_update(GPUViewport *viewport) +{ + bool ret = (viewport->flag & DO_UPDATE); + viewport->flag &= ~DO_UPDATE; + return ret; +} + +GPUViewport *GPU_viewport_create(void) +{ + GPUViewport *viewport = MEM_callocN(sizeof(GPUViewport), "GPUViewport"); + viewport->fbl = MEM_callocN(sizeof(DefaultFramebufferList), "FramebufferList"); + viewport->txl = MEM_callocN(sizeof(DefaultTextureList), "TextureList"); + viewport->idatalist = DRW_instance_data_list_create(); + + viewport->size[0] = viewport->size[1] = -1; + + return viewport; +} + +GPUViewport *GPU_viewport_create_from_offscreen(struct GPUOffScreen *ofs) +{ + GPUViewport *viewport = GPU_viewport_create(); + GPUTexture *color, *depth; + GPUFrameBuffer *fb; + viewport->size[0] = GPU_offscreen_width(ofs); + viewport->size[1] = GPU_offscreen_height(ofs); + + GPU_offscreen_viewport_data_get(ofs, &fb, &color, &depth); + + if (GPU_texture_samples(color)) { + viewport->txl->multisample_color = color; + viewport->txl->multisample_depth = depth; + viewport->fbl->multisample_fb = fb; + gpu_viewport_default_fb_create(viewport); + } + else { + viewport->fbl->default_fb = fb; + viewport->txl->color = color; + viewport->txl->depth = depth; + GPU_framebuffer_ensure_config(&viewport->fbl->color_only_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(viewport->txl->color) + }); + GPU_framebuffer_ensure_config(&viewport->fbl->depth_only_fb, { + GPU_ATTACHMENT_TEXTURE(viewport->txl->depth), + GPU_ATTACHMENT_NONE + }); + } + + return viewport; +} +/** + * Clear vars assigned from offscreen, so we don't free data owned by `GPUOffScreen`. + */ +void GPU_viewport_clear_from_offscreen(GPUViewport *viewport) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + DefaultTextureList *dtxl = viewport->txl; + + if (dfbl->multisample_fb) { + /* GPUViewport expect the final result to be in default_fb but + * GPUOffscreen wants it in its multisample_fb, so we sync it back. */ + GPU_framebuffer_blit(dfbl->default_fb, 0, dfbl->multisample_fb, 0, GPU_COLOR_BIT | GPU_DEPTH_BIT); + dfbl->multisample_fb = NULL; + dtxl->multisample_color = NULL; + dtxl->multisample_depth = NULL; + } + else { + viewport->fbl->default_fb = NULL; + dtxl->color = NULL; + dtxl->depth = NULL; + } +} + +void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type) +{ + LinkData *ld = MEM_callocN(sizeof(LinkData), "LinkData"); + ViewportEngineData *data = MEM_callocN(sizeof(ViewportEngineData), "ViewportEngineData"); + int fbl_len, txl_len, psl_len, stl_len; + + DRW_engine_viewport_data_size_get(engine_type, &fbl_len, &txl_len, &psl_len, &stl_len); + + data->engine_type = engine_type; + + data->fbl = MEM_callocN((sizeof(void *) * fbl_len) + sizeof(FramebufferList), "FramebufferList"); + data->txl = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), "TextureList"); + data->psl = MEM_callocN((sizeof(void *) * psl_len) + sizeof(PassList), "PassList"); + data->stl = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), "StorageList"); + + ld->data = data; + BLI_addtail(&viewport->data, ld); + + return data; +} + +static void gpu_viewport_engines_data_free(GPUViewport *viewport) +{ + int fbl_len, txl_len, psl_len, stl_len; + + LinkData *next; + for (LinkData *link = viewport->data.first; link; link = next) { + next = link->next; + ViewportEngineData *data = link->data; + DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, &psl_len, &stl_len); + + gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, txl_len); + gpu_viewport_passes_free(data->psl, psl_len); + gpu_viewport_storage_free(data->stl, stl_len); + + MEM_freeN(data->fbl); + MEM_freeN(data->txl); + MEM_freeN(data->psl); + MEM_freeN(data->stl); + + /* We could handle this in the DRW module */ + if (data->text_draw_cache) { + extern void DRW_text_cache_destroy(struct DRWTextStore *dt); + DRW_text_cache_destroy(data->text_draw_cache); + data->text_draw_cache = NULL; + } + + MEM_freeN(data); + + BLI_remlink(&viewport->data, link); + MEM_freeN(link); + } + + gpu_viewport_texture_pool_free(viewport); +} + +void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_type) +{ + for (LinkData *link = viewport->data.first; link; link = link->next) { + ViewportEngineData *vdata = link->data; + if (vdata->engine_type == engine_type) { + return vdata; + } + } + return NULL; +} + +ViewportMemoryPool *GPU_viewport_mempool_get(GPUViewport *viewport) +{ + return &viewport->vmempool; +} + +struct DRWInstanceDataList *GPU_viewport_instance_data_list_get(GPUViewport *viewport) +{ + return viewport->idatalist; +} + +void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport) +{ + return viewport->fbl; +} + +void *GPU_viewport_texture_list_get(GPUViewport *viewport) +{ + return viewport->txl; +} + +void GPU_viewport_size_get(const GPUViewport *viewport, int size[2]) +{ + size[0] = viewport->size[0]; + size[1] = viewport->size[1]; +} + +/** + * Special case, this is needed for when we have a viewport without a frame-buffer output + * (occlusion queries for eg) but still need to set the size since it may be used for other calculations. + */ +void GPU_viewport_size_set(GPUViewport *viewport, const int size[2]) +{ + viewport->size[0] = size[0]; + viewport->size[1] = size[1]; +} + +double *GPU_viewport_cache_time_get(GPUViewport *viewport) +{ + return &viewport->cache_time; +} + +/** + * Try to find a texture coresponding to params into the texture pool. + * If no texture was found, create one and add it to the pool. + */ +GPUTexture *GPU_viewport_texture_pool_query(GPUViewport *viewport, void *engine, int width, int height, int format) +{ + GPUTexture *tex; + + for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex->next) { + if ((GPU_texture_format(tmp_tex->texture) == format) && + (GPU_texture_width(tmp_tex->texture) == width) && + (GPU_texture_height(tmp_tex->texture) == height)) + { + /* Search if the engine is not already using this texture */ + for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; ++i) { + if (tmp_tex->user[i] == engine) { + break; + } + + if (tmp_tex->user[i] == NULL) { + tmp_tex->user[i] = engine; + return tmp_tex->texture; + } + } + } + } + + tex = GPU_texture_create_2D(width, height, format, NULL, NULL); + GPU_texture_bind(tex, 0); + /* Doing filtering for depth does not make sense when not doing shadow mapping, + * and enabling texture filtering on integer texture make them unreadable. */ + bool do_filter = !GPU_texture_depth(tex) && !GPU_texture_integer(tex); + GPU_texture_filter_mode(tex, do_filter); + GPU_texture_unbind(tex); + + ViewportTempTexture *tmp_tex = MEM_callocN(sizeof(ViewportTempTexture), "ViewportTempTexture"); + tmp_tex->texture = tex; + tmp_tex->user[0] = engine; + BLI_addtail(&viewport->tex_pool, tmp_tex); + + return tex; +} + +static void gpu_viewport_texture_pool_clear_users(GPUViewport *viewport) +{ + ViewportTempTexture *tmp_tex_next; + + for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex_next) { + tmp_tex_next = tmp_tex->next; + bool no_user = true; + for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; ++i) { + if (tmp_tex->user[i] != NULL) { + tmp_tex->user[i] = NULL; + no_user = false; + } + } + + if (no_user) { + GPU_texture_free(tmp_tex->texture); + BLI_freelinkN(&viewport->tex_pool, tmp_tex); + } + } +} + +static void gpu_viewport_texture_pool_free(GPUViewport *viewport) +{ + for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex->next) { + GPU_texture_free(tmp_tex->texture); + } + + BLI_freelistN(&viewport->tex_pool); +} + +bool GPU_viewport_engines_data_validate(GPUViewport *viewport, unsigned int hash) +{ + bool dirty = false; + + if (viewport->data_hash != hash) { + gpu_viewport_engines_data_free(viewport); + dirty = true; + } + + viewport->data_hash = hash; + + return dirty; +} + +void GPU_viewport_cache_release(GPUViewport *viewport) +{ + for (LinkData *link = viewport->data.first; link; link = link->next) { + ViewportEngineData *data = link->data; + int psl_len; + DRW_engine_viewport_data_size_get(data->engine_type, NULL, NULL, &psl_len, NULL); + gpu_viewport_passes_free(data->psl, psl_len); + } +} + +static void gpu_viewport_default_fb_create(GPUViewport *viewport) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + DefaultTextureList *dtxl = viewport->txl; + int *size = viewport->size; + bool ok = true; + + dtxl->color = GPU_texture_create_2D(size[0], size[1], GPU_RGBA8, NULL, NULL); + dtxl->depth = GPU_texture_create_2D(size[0], size[1], GPU_DEPTH24_STENCIL8, NULL, NULL); + + if (!(dtxl->depth && dtxl->color)) { + ok = false; + goto cleanup; + } + + GPU_framebuffer_ensure_config(&dfbl->default_fb, { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(dtxl->color) + }); + + GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_NONE + }); + + GPU_framebuffer_ensure_config(&dfbl->color_only_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(dtxl->color) + }); + + ok = ok && GPU_framebuffer_check_valid(dfbl->default_fb, NULL); + ok = ok && GPU_framebuffer_check_valid(dfbl->color_only_fb, NULL); + ok = ok && GPU_framebuffer_check_valid(dfbl->depth_only_fb, NULL); + +cleanup: + if (!ok) { + GPU_viewport_free(viewport); + DRW_opengl_context_disable(); + return; + } + + GPU_framebuffer_restore(); +} + +static void gpu_viewport_default_multisample_fb_create(GPUViewport *viewport) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + DefaultTextureList *dtxl = viewport->txl; + int *size = viewport->size; + int samples = viewport->samples; + bool ok = true; + + dtxl->multisample_color = GPU_texture_create_2D_multisample(size[0], size[1], GPU_RGBA8, NULL, samples, NULL); + dtxl->multisample_depth = GPU_texture_create_2D_multisample(size[0], size[1], GPU_DEPTH24_STENCIL8, NULL, samples, NULL); + + if (!(dtxl->multisample_depth && dtxl->multisample_color)) { + ok = false; + goto cleanup; + } + + GPU_framebuffer_ensure_config(&dfbl->multisample_fb, { + GPU_ATTACHMENT_TEXTURE(dtxl->multisample_depth), + GPU_ATTACHMENT_TEXTURE(dtxl->multisample_color) + }); + + ok = ok && GPU_framebuffer_check_valid(dfbl->multisample_fb, NULL); + +cleanup: + if (!ok) { + GPU_viewport_free(viewport); + DRW_opengl_context_disable(); + return; + } + + GPU_framebuffer_restore(); +} + +void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + int fbl_len, txl_len; + + /* add one pixel because of scissor test */ + int rect_w = BLI_rcti_size_x(rect) + 1; + int rect_h = BLI_rcti_size_y(rect) + 1; + + DRW_opengl_context_enable(); + + if (dfbl->default_fb) { + if (rect_w != viewport->size[0] || rect_h != viewport->size[1] || U.ogl_multisamples != viewport->samples) { + gpu_viewport_buffers_free( + (FramebufferList *)viewport->fbl, default_fbl_len, + (TextureList *)viewport->txl, default_txl_len); + + for (LinkData *link = viewport->data.first; link; link = link->next) { + ViewportEngineData *data = link->data; + DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, NULL, NULL); + gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, txl_len); + } + + gpu_viewport_texture_pool_free(viewport); + } + } + + viewport->size[0] = rect_w; + viewport->size[1] = rect_h; + viewport->samples = U.ogl_multisamples; + + gpu_viewport_texture_pool_clear_users(viewport); + + /* Multisample Buffer */ + if (viewport->samples > 0) { + if (!dfbl->default_fb) { + gpu_viewport_default_multisample_fb_create(viewport); + } + } + + if (!dfbl->default_fb) { + gpu_viewport_default_fb_create(viewport); + } +} + +void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + + if (dfbl->default_fb == NULL) + return; + + DefaultTextureList *dtxl = viewport->txl; + + GPUTexture *color = dtxl->color; + + const float w = (float)GPU_texture_width(color); + const float h = (float)GPU_texture_height(color); + + BLI_assert(w == BLI_rcti_size_x(rect) + 1); + BLI_assert(h == BLI_rcti_size_y(rect) + 1); + + /* wmOrtho for the screen has this same offset */ + const float halfx = GLA_PIXEL_OFS / w; + const float halfy = GLA_PIXEL_OFS / h; + + float x1 = rect->xmin; + float x2 = rect->xmin + w; + float y1 = rect->ymin; + float y2 = rect->ymin + h; + + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); + GPU_shader_bind(shader); + + GPU_texture_bind(color, 0); + glUniform1i(GPU_shader_get_uniform(shader, "image"), 0); + glUniform4f(GPU_shader_get_uniform(shader, "rect_icon"), halfx, halfy, 1.0f + halfx, 1.0f + halfy); + glUniform4f(GPU_shader_get_uniform(shader, "rect_geom"), x1, y1, x2, y2); + glUniform4f(GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_COLOR), 1.0f, 1.0f, 1.0f, 1.0f); + + GWN_draw_primitive(GWN_PRIM_TRI_STRIP, 4); + + GPU_texture_unbind(color); +} + +void GPU_viewport_unbind(GPUViewport *UNUSED(viewport)) +{ + GPU_framebuffer_restore(); + DRW_opengl_context_disable(); +} + + +GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport) +{ + DefaultFramebufferList *dfbl = viewport->fbl; + + if (dfbl->default_fb) { + DefaultTextureList *dtxl = viewport->txl; + return dtxl->color; + } + + return NULL; +} + +static void gpu_viewport_buffers_free( + FramebufferList *fbl, int fbl_len, + TextureList *txl, int txl_len) +{ + for (int i = 0; i < fbl_len; i++) { + GPUFrameBuffer *fb = fbl->framebuffers[i]; + if (fb) { + GPU_framebuffer_free(fb); + fbl->framebuffers[i] = NULL; + } + } + for (int i = 0; i < txl_len; i++) { + GPUTexture *tex = txl->textures[i]; + if (tex) { + GPU_texture_free(tex); + txl->textures[i] = NULL; + } + } +} + +static void gpu_viewport_storage_free(StorageList *stl, int stl_len) +{ + for (int i = 0; i < stl_len; i++) { + void *storage = stl->storage[i]; + if (storage) { + MEM_freeN(storage); + stl->storage[i] = NULL; + } + } +} + +static void gpu_viewport_passes_free(PassList *psl, int psl_len) +{ + for (int i = 0; i < psl_len; i++) { + struct DRWPass *pass = psl->passes[i]; + if (pass) { + DRW_pass_free(pass); + psl->passes[i] = NULL; + } + } +} + +/* Must be executed inside Drawmanager Opengl Context. */ +void GPU_viewport_free(GPUViewport *viewport) +{ + gpu_viewport_engines_data_free(viewport); + + gpu_viewport_buffers_free( + (FramebufferList *)viewport->fbl, default_fbl_len, + (TextureList *)viewport->txl, default_txl_len); + + gpu_viewport_texture_pool_free(viewport); + + MEM_freeN(viewport->fbl); + MEM_freeN(viewport->txl); + + if (viewport->vmempool.calls != NULL) { + BLI_mempool_destroy(viewport->vmempool.calls); + } + if (viewport->vmempool.states != NULL) { + BLI_mempool_destroy(viewport->vmempool.states); + } + if (viewport->vmempool.shgroups != NULL) { + BLI_mempool_destroy(viewport->vmempool.shgroups); + } + if (viewport->vmempool.uniforms != NULL) { + BLI_mempool_destroy(viewport->vmempool.uniforms); + } + if (viewport->vmempool.passes != NULL) { + BLI_mempool_destroy(viewport->vmempool.passes); + } + + DRW_instance_data_list_free(viewport->idatalist); + MEM_freeN(viewport->idatalist); + + MEM_freeN(viewport); +} |