From 0dfc8d69392a57e0210644428571dd455cb61241 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 6 Dec 2015 21:20:19 +0100 Subject: OpenGL: split off framebuffer, shader and texture code into separate files. --- source/blender/blenkernel/intern/DerivedMesh.c | 2 +- source/blender/blenkernel/intern/cdderivedmesh.c | 2 +- source/blender/blenkernel/intern/editderivedmesh.c | 2 +- source/blender/blenkernel/intern/icons.c | 2 +- source/blender/blenkernel/intern/subsurf_ccg.c | 2 +- source/blender/editors/render/render_opengl.c | 2 +- source/blender/editors/space_view3d/drawmesh.c | 2 +- source/blender/editors/space_view3d/drawobject.c | 2 +- source/blender/editors/space_view3d/drawvolume.c | 3 +- source/blender/editors/space_view3d/space_view3d.c | 4 +- source/blender/editors/space_view3d/view3d_draw.c | 2 +- source/blender/gpu/CMakeLists.txt | 18 +- source/blender/gpu/GPU_draw.h | 2 +- source/blender/gpu/GPU_extensions.h | 187 +- source/blender/gpu/GPU_framebuffer.h | 84 + source/blender/gpu/GPU_shader.h | 127 ++ source/blender/gpu/GPU_texture.h | 104 + source/blender/gpu/intern/gpu_basic_shader.c | 5 +- source/blender/gpu/intern/gpu_codegen.c | 4 +- source/blender/gpu/intern/gpu_compositing.c | 6 +- source/blender/gpu/intern/gpu_draw.c | 4 +- source/blender/gpu/intern/gpu_extensions.c | 2169 +------------------- source/blender/gpu/intern/gpu_framebuffer.c | 633 ++++++ source/blender/gpu/intern/gpu_material.c | 3 + source/blender/gpu/intern/gpu_shader.c | 783 +++++++ source/blender/gpu/intern/gpu_texture.c | 762 +++++++ source/blender/python/intern/gpu_offscreen.c | 2 +- source/gameengine/Ketsji/BL_BlenderShader.cpp | 2 +- .../RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp | 3 +- 29 files changed, 2567 insertions(+), 2356 deletions(-) create mode 100644 source/blender/gpu/GPU_framebuffer.h create mode 100644 source/blender/gpu/GPU_shader.h create mode 100644 source/blender/gpu/GPU_texture.h create mode 100644 source/blender/gpu/intern/gpu_framebuffer.c create mode 100644 source/blender/gpu/intern/gpu_shader.c create mode 100644 source/blender/gpu/intern/gpu_texture.c diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 07a1d3bea7f..e26e5148ff6 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -74,8 +74,8 @@ static DerivedMesh *navmesh_dm_createNavMeshForVisualization(DerivedMesh *dm); #include "BLI_sys_types.h" /* for intptr_t support */ #include "GPU_buffers.h" -#include "GPU_extensions.h" #include "GPU_glew.h" +#include "GPU_shader.h" #ifdef WITH_OPENSUBDIV # include "DNA_userdef_types.h" diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 1acb9ad3d20..836072df67d 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -57,8 +57,8 @@ #include "GPU_buffers.h" #include "GPU_draw.h" -#include "GPU_extensions.h" #include "GPU_glew.h" +#include "GPU_shader.h" #include "WM_api.h" diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index b6d009deb2a..dc50e39a862 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -55,8 +55,8 @@ #include "MEM_guardedalloc.h" -#include "GPU_extensions.h" #include "GPU_glew.h" +#include "GPU_shader.h" extern GLubyte stipple_quarttone[128]; /* glutil.c, bad level data */ diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index 2171f193bac..45ebada4d81 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -55,7 +55,7 @@ #include "BLI_sys_types.h" // for intptr_t support -#include "GPU_extensions.h" +#include "GPU_texture.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 23bc2470945..53da38b2da8 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -74,9 +74,9 @@ #endif #include "GPU_draw.h" -#include "GPU_extensions.h" #include "GPU_glew.h" #include "GPU_buffers.h" +#include "GPU_shader.h" #include "CCGSubSurf.h" diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index f94f3cdf758..5268afae270 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -69,9 +69,9 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_extensions.h" #include "GPU_glew.h" #include "GPU_compositing.h" +#include "GPU_framebuffer.h" #include "render_intern.h" diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index de7a8ed0a7c..f6e66ca5e90 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -60,10 +60,10 @@ #include "UI_resources.h" -#include "GPU_extensions.h" #include "GPU_draw.h" #include "GPU_material.h" #include "GPU_basic_shader.h" +#include "GPU_shader.h" #include "RE_engine.h" diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index f2240c662ab..e03f81742b7 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -87,9 +87,9 @@ #include "BIF_glutil.h" #include "GPU_draw.h" -#include "GPU_extensions.h" #include "GPU_select.h" #include "GPU_basic_shader.h" +#include "GPU_shader.h" #include "ED_mesh.h" #include "ED_particle.h" diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index 3029aaeeef1..102bfc55a2c 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -46,7 +46,8 @@ #include "BIF_gl.h" -#include "GPU_extensions.h" +#include "GPU_shader.h" +#include "GPU_texture.h" #include "view3d_intern.h" // own include diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a1c2c8cca17..fa14ca96fe2 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -54,9 +54,9 @@ #include "ED_space_api.h" #include "ED_screen.h" -#include "GPU_extensions.h" -#include "GPU_material.h" #include "GPU_compositing.h" +#include "GPU_framebuffer.h" +#include "GPU_material.h" #include "BIF_gl.h" diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 781c848285a..5a839c25e05 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -96,8 +96,8 @@ #include "UI_resources.h" #include "GPU_draw.h" +#include "GPU_framebuffer.h" #include "GPU_material.h" -#include "GPU_extensions.h" #include "GPU_compositing.h" #include "view3d_intern.h" /* own include */ diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 60fa77babd9..6d3a5d37d66 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -46,16 +46,19 @@ set(INC_SYS ) set(SRC + intern/gpu_basic_shader.c intern/gpu_buffers.c intern/gpu_codegen.c + intern/gpu_compositing.c + intern/gpu_debug.c intern/gpu_draw.c intern/gpu_extensions.c + intern/gpu_framebuffer.c intern/gpu_init_exit.c intern/gpu_material.c - intern/gpu_basic_shader.c intern/gpu_select.c - intern/gpu_compositing.c - intern/gpu_debug.c + intern/gpu_shader.c + intern/gpu_texture.c shaders/gpu_program_smoke_frag.glsl shaders/gpu_program_smoke_color_frag.glsl @@ -78,16 +81,19 @@ set(SRC shaders/gpu_shader_vsm_store_vert.glsl shaders/gpu_shader_fx_depth_resolve.glsl + GPU_basic_shader.h GPU_buffers.h - GPU_draw.h + GPU_compositing.h GPU_debug.h + GPU_draw.h GPU_extensions.h + GPU_framebuffer.h GPU_glew.h GPU_init_exit.h GPU_material.h - GPU_basic_shader.h GPU_select.h - GPU_compositing.h + GPU_shader.h + GPU_texture.h intern/gpu_codegen.h intern/gpu_private.h ) diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index cb288e9f448..3e6c26f608c 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -129,7 +129,7 @@ void GPU_set_gpu_mipmapping(int gpu_mipmap); /* Image updates and free * - these deal with images bound as opengl textures */ -void GPU_paint_update_image(struct Image *ima, ImageUser *iuser, int x, int y, int w, int h); +void GPU_paint_update_image(struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); void GPU_update_images_framechange(void); int GPU_update_image_time(struct Image *ima, double time); int GPU_verify_image(struct Image *ima, struct ImageUser *iuser, int tftile, bool compare, bool mipmap, bool is_data); diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index ef582b6d97f..f3be52f324b 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -36,25 +36,6 @@ extern "C" { #endif -struct Image; -struct ImageUser; -struct PreviewImage; - -struct GPUTexture; -typedef struct GPUTexture GPUTexture; - -struct GPUFrameBuffer; -typedef struct GPUFrameBuffer GPUFrameBuffer; - -struct GPUOffScreen; -typedef struct GPUOffScreen GPUOffScreen; - -struct GPUShader; -typedef struct GPUShader GPUShader; - -struct GPUProgram; -typedef struct GPUProgram GPUProgram; - /* GPU extensions support */ void GPU_extensions_disable(void); @@ -65,9 +46,12 @@ bool GPU_full_non_power_of_two_support(void); bool GPU_display_list_support(void); bool GPU_bicubic_bump_support(void); bool GPU_geometry_shader_support(void); +bool GPU_geometry_shader_support_via_extension(void); bool GPU_instanced_drawing_support(void); int GPU_max_texture_size(void); +int GPU_max_textures(void); +int GPU_max_color_texture_samples(void); int GPU_color_depth(void); void GPU_get_dfdy_factors(float fac[2]); @@ -103,171 +87,6 @@ typedef enum GPUDriverType { bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver); -/* GPU Texture - * - always returns unsigned char RGBA textures - * - if texture with non square dimensions is created, depending on the - * graphics card capabilities the texture may actually be stored in a - * larger texture with power of two dimensions. - * - can use reference counting: - * - reference counter after GPU_texture_create is 1 - * - GPU_texture_ref increases by one - * - GPU_texture_free decreases by one, and frees if 0 - * - if created with from_blender, will not free the texture - */ - -typedef enum GPUHDRType { - GPU_HDR_NONE = 0, - GPU_HDR_HALF_FLOAT = 1, - GPU_HDR_FULL_FLOAT = (1 << 1), -} GPUHDRType; - -GPUTexture *GPU_texture_create_1D(int w, const float *pixels, char err_out[256]); -GPUTexture *GPU_texture_create_2D(int w, int h, const float *pixels, GPUHDRType hdr, char err_out[256]); -GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels); -GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]); -GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]); -GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]); -GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256]); -GPUTexture *GPU_texture_create_2D_multisample(int w, int h, const float *pixels, GPUHDRType hdr, int samples, char err_out[256]); -GPUTexture *GPU_texture_create_depth_multisample(int w, int h, int samples, char err_out[256]); -GPUTexture *GPU_texture_from_blender(struct Image *ima, - struct ImageUser *iuser, bool is_data, double time, int mipmap); -GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap); -void GPU_invalid_tex_init(void); -void GPU_invalid_tex_bind(int mode); -void GPU_invalid_tex_free(void); - -void GPU_texture_free(GPUTexture *tex); - -void GPU_texture_ref(GPUTexture *tex); - -void GPU_texture_bind(GPUTexture *tex, int number); -void GPU_texture_unbind(GPUTexture *tex); - -void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter); - -GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex); - -int GPU_texture_target(const GPUTexture *tex); -int GPU_texture_width(const GPUTexture *tex); -int GPU_texture_height(const GPUTexture *tex); -int GPU_texture_opengl_bindcode(const GPUTexture *tex); - -/* GPU Framebuffer - * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice - * multiple FBO's may be created, to get around limitations on the number - * of attached textures and the dimension requirements. - * - after any of the GPU_framebuffer_* functions, GPU_framebuffer_restore must - * be called before rendering to the window framebuffer again */ - -void GPU_texture_bind_as_framebuffer(GPUTexture *tex); - -GPUFrameBuffer *GPU_framebuffer_create(void); -int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]); -void GPU_framebuffer_texture_detach(GPUTexture *tex); -void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot); -void GPU_framebuffer_texture_unbind(GPUFrameBuffer *fb, GPUTexture *tex); -void GPU_framebuffer_free(GPUFrameBuffer *fb); -bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]); - -void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot); - -void GPU_framebuffer_restore(void); -void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *blurfb, GPUTexture *blurtex); - -/* GPU OffScreen - * - wrapper around framebuffer and texture for simple offscreen drawing - * - changes size if graphics card can't support it */ - -GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256]); -void GPU_offscreen_free(GPUOffScreen *ofs); -void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); -void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); -int GPU_offscreen_width(const GPUOffScreen *ofs); -int GPU_offscreen_height(const GPUOffScreen *ofs); -int GPU_offscreen_color_texture(const GPUOffScreen *ofs); - -/* Builtin/Non-generated shaders */ -typedef enum GPUProgramType { - GPU_PROGRAM_TYPE_FRAGMENT = 0 -} GPUProgramType; - -/* TODO: remove ARB program support (recode smoke shader in GLSL) */ -GPUProgram *GPU_program_shader_create(GPUProgramType type, const char *code); -void GPU_program_free(GPUProgram *program); -void GPU_program_parameter_4f(GPUProgram *program, unsigned int location, float x, float y, float z, float w); -void GPU_program_bind(GPUProgram *); -void GPU_program_unbind(GPUProgram *); - -/* GPU Shader - * - only for fragment shaders now - * - must call texture bind before setting a texture as uniform! */ - -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); -enum { - GPU_SHADER_FLAGS_NONE = 0, - GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV = (1 << 0), -}; -GPUShader *GPU_shader_create_ex(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - int input, - int output, - int number, - const int flags); -void GPU_shader_free(GPUShader *shader); - -void GPU_shader_bind(GPUShader *shader); -void GPU_shader_unbind(void); - -int GPU_shader_get_uniform(GPUShader *shader, const char *name); -void GPU_shader_uniform_vector(GPUShader *shader, int location, int length, - int arraysize, const float *value); -void GPU_shader_uniform_vector_int(GPUShader *shader, int location, int length, - int arraysize, const int *value); - -void GPU_shader_uniform_texture(GPUShader *shader, int location, GPUTexture *tex); -void GPU_shader_uniform_int(GPUShader *shader, int location, int value); -void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number); - -int GPU_shader_get_attribute(GPUShader *shader, const char *name); - -/* Builtin/Non-generated shaders */ -typedef enum GPUBuiltinShader { - GPU_SHADER_VSM_STORE = 0, - GPU_SHADER_SEP_GAUSSIAN_BLUR = 1, -} GPUBuiltinShader; - -typedef enum GPUBuiltinProgram { - GPU_PROGRAM_SMOKE = 0, - GPU_PROGRAM_SMOKE_COLORED = 1, -} GPUBuiltinProgram; - -GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader); -GPUProgram *GPU_shader_get_builtin_program(GPUBuiltinProgram program); -GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp); - -void GPU_shader_free_builtin_shaders(void); - -/* Vertex attributes for shaders */ - -#define GPU_MAX_ATTRIB 32 - -typedef struct GPUVertexAttribs { - struct { - int type; - int glindex; - int gltexco; - int attribid; - char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ - } layer[GPU_MAX_ATTRIB]; - - int totlayer; -} GPUVertexAttribs; - #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h new file mode 100644 index 00000000000..4a7b0454181 --- /dev/null +++ b/source/blender/gpu/GPU_framebuffer.h @@ -0,0 +1,84 @@ +/* + * ***** 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): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file GPU_framebuffer.h + * \ingroup gpu + */ + +#ifndef __GPU_FRAMEBUFFER_H__ +#define __GPU_FRAMEBUFFER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct GPUFrameBuffer GPUFrameBuffer; +typedef struct GPUOffScreen GPUOffScreen; +typedef struct GPUTexture GPUTexture; + +/* GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created, to get around limitations on the number + * of attached textures and the dimension requirements. + * - after any of the GPU_framebuffer_* functions, GPU_framebuffer_restore must + * be called before rendering to the window framebuffer again */ + +void GPU_texture_bind_as_framebuffer(GPUTexture *tex); + +GPUFrameBuffer *GPU_framebuffer_create(void); +int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]); +void GPU_framebuffer_texture_detach(GPUTexture *tex); +void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot); +void GPU_framebuffer_texture_unbind(GPUFrameBuffer *fb, GPUTexture *tex); +void GPU_framebuffer_free(GPUFrameBuffer *fb); +bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]); + +void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot); + +bool GPU_framebuffer_bound(GPUFrameBuffer *fb); + +void GPU_framebuffer_restore(void); +void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *blurfb, GPUTexture *blurtex); + +/* GPU OffScreen + * - wrapper around framebuffer and texture for simple offscreen drawing + * - changes size if graphics card can't support it */ + +GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, char err_out[256]); +void GPU_offscreen_free(GPUOffScreen *ofs); +void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); +void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); +int GPU_offscreen_width(const GPUOffScreen *ofs); +int GPU_offscreen_height(const GPUOffScreen *ofs); +int GPU_offscreen_color_texture(const GPUOffScreen *ofs); + +#ifdef __cplusplus +} +#endif + +#endif /* __GPU_FRAMEBUFFER_H__ */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h new file mode 100644 index 00000000000..a7cfd4e262f --- /dev/null +++ b/source/blender/gpu/GPU_shader.h @@ -0,0 +1,127 @@ +/* + * ***** 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): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file GPU_shader.h + * \ingroup gpu + */ + +#ifndef __GPU_SHADER_H__ +#define __GPU_SHADER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct GPUShader GPUShader; +typedef struct GPUProgram GPUProgram; +typedef struct GPUTexture GPUTexture; + +/* Builtin/Non-generated shaders */ +typedef enum GPUProgramType { + GPU_PROGRAM_TYPE_FRAGMENT = 0 +} GPUProgramType; + +/* TODO: remove ARB program support (recode smoke shader in GLSL) */ +GPUProgram *GPU_program_shader_create(GPUProgramType type, const char *code); +void GPU_program_free(GPUProgram *program); +void GPU_program_parameter_4f(GPUProgram *program, unsigned int location, float x, float y, float z, float w); +void GPU_program_bind(GPUProgram *); +void GPU_program_unbind(GPUProgram *); + +/* GPU Shader + * - only for fragment shaders now + * - must call texture bind before setting a texture as uniform! */ + +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); +enum { + GPU_SHADER_FLAGS_NONE = 0, + GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV = (1 << 0), +}; +GPUShader *GPU_shader_create_ex(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + int input, + int output, + int number, + const int flags); +void GPU_shader_free(GPUShader *shader); + +void GPU_shader_bind(GPUShader *shader); +void GPU_shader_unbind(void); + +int GPU_shader_get_uniform(GPUShader *shader, const char *name); +void GPU_shader_uniform_vector(GPUShader *shader, int location, int length, + int arraysize, const float *value); +void GPU_shader_uniform_vector_int(GPUShader *shader, int location, int length, + int arraysize, const int *value); + +void GPU_shader_uniform_texture(GPUShader *shader, int location, GPUTexture *tex); +void GPU_shader_uniform_int(GPUShader *shader, int location, int value); +void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number); + +int GPU_shader_get_attribute(GPUShader *shader, const char *name); + +/* Builtin/Non-generated shaders */ +typedef enum GPUBuiltinShader { + GPU_SHADER_VSM_STORE = 0, + GPU_SHADER_SEP_GAUSSIAN_BLUR = 1, +} GPUBuiltinShader; + +typedef enum GPUBuiltinProgram { + GPU_PROGRAM_SMOKE = 0, + GPU_PROGRAM_SMOKE_COLORED = 1, +} GPUBuiltinProgram; + +GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader); +GPUProgram *GPU_shader_get_builtin_program(GPUBuiltinProgram program); +GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp); + +void GPU_shader_free_builtin_shaders(void); + +/* Vertex attributes for shaders */ + +#define GPU_MAX_ATTRIB 32 + +typedef struct GPUVertexAttribs { + struct { + int type; + int glindex; + int gltexco; + int attribid; + char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ + } layer[GPU_MAX_ATTRIB]; + + int totlayer; +} GPUVertexAttribs; + +#ifdef __cplusplus +} +#endif + +#endif /* __GPU_SHADER_H__ */ diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h new file mode 100644 index 00000000000..a17da53b890 --- /dev/null +++ b/source/blender/gpu/GPU_texture.h @@ -0,0 +1,104 @@ +/* + * ***** 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): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file GPU_texture.h + * \ingroup gpu + */ + +#ifndef __GPU_TEXTURE_H__ +#define __GPU_TEXTURE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct Image; +struct ImageUser; +struct PreviewImage; + +typedef struct GPUFrameBuffer GPUFrameBuffer; +typedef struct GPUTexture GPUTexture; + +/* GPU Texture + * - always returns unsigned char RGBA textures + * - if texture with non square dimensions is created, depending on the + * graphics card capabilities the texture may actually be stored in a + * larger texture with power of two dimensions. + * - can use reference counting: + * - reference counter after GPU_texture_create is 1 + * - GPU_texture_ref increases by one + * - GPU_texture_free decreases by one, and frees if 0 + * - if created with from_blender, will not free the texture + */ + +typedef enum GPUHDRType { + GPU_HDR_NONE = 0, + GPU_HDR_HALF_FLOAT = 1, + GPU_HDR_FULL_FLOAT = (1 << 1), +} GPUHDRType; + +GPUTexture *GPU_texture_create_1D(int w, const float *pixels, char err_out[256]); +GPUTexture *GPU_texture_create_2D(int w, int h, const float *pixels, GPUHDRType hdr, char err_out[256]); +GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels); +GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]); +GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256]); +GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]); +GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256]); +GPUTexture *GPU_texture_create_2D_multisample(int w, int h, const float *pixels, GPUHDRType hdr, int samples, char err_out[256]); +GPUTexture *GPU_texture_create_depth_multisample(int w, int h, int samples, char err_out[256]); +GPUTexture *GPU_texture_from_blender(struct Image *ima, + struct ImageUser *iuser, bool is_data, double time, int mipmap); +GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap); +void GPU_invalid_tex_init(void); +void GPU_invalid_tex_bind(int mode); +void GPU_invalid_tex_free(void); + +void GPU_texture_free(GPUTexture *tex); + +void GPU_texture_ref(GPUTexture *tex); + +void GPU_texture_bind(GPUTexture *tex, int number); +void GPU_texture_unbind(GPUTexture *tex); +int GPU_texture_bound_number(GPUTexture *tex); + +void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter); + +GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex); +int GPU_texture_framebuffer_attachment(GPUTexture *tex); +void GPU_texture_framebuffer_set(GPUTexture *tex, GPUFrameBuffer *fb, int attachment); + +int GPU_texture_target(const GPUTexture *tex); +int GPU_texture_width(const GPUTexture *tex); +int GPU_texture_height(const GPUTexture *tex); +int GPU_texture_depth(const GPUTexture *tex); +int GPU_texture_opengl_bindcode(const GPUTexture *tex); + +#ifdef __cplusplus +} +#endif + +#endif /* __GPU_TEXTURE_H__ */ diff --git a/source/blender/gpu/intern/gpu_basic_shader.c b/source/blender/gpu/intern/gpu_basic_shader.c index 2dcee877279..6b01ee85170 100644 --- a/source/blender/gpu/intern/gpu_basic_shader.c +++ b/source/blender/gpu/intern/gpu_basic_shader.c @@ -42,13 +42,12 @@ * - Optimize for case where no texture matrix is used. */ -#include "GPU_glew.h" - #include "BLI_math.h" #include "BLI_utildefines.h" -#include "GPU_extensions.h" #include "GPU_basic_shader.h" +#include "GPU_glew.h" +#include "GPU_shader.h" /* State */ diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index c7f77011197..800ed9399a9 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -42,9 +42,11 @@ #include "BLI_dynstr.h" #include "BLI_ghash.h" +#include "GPU_extensions.h" #include "GPU_glew.h" #include "GPU_material.h" -#include "GPU_extensions.h" +#include "GPU_shader.h" +#include "GPU_texture.h" #include "BLI_sys_types.h" /* for intptr_t support */ diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index dfea4a5a72b..05968bf1e4c 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -47,10 +47,12 @@ #include "DNA_camera_types.h" #include "DNA_gpu_types.h" -#include "GPU_extensions.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" diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 469e2aebad2..05d11549656 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -75,11 +75,13 @@ #include "BKE_subsurf.h" #include "BKE_DerivedMesh.h" +#include "GPU_basic_shader.h" #include "GPU_buffers.h" #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_material.h" -#include "GPU_basic_shader.h" +#include "GPU_shader.h" +#include "GPU_texture.h" #include "PIL_time.h" diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index 3aab5f5421f..c855c53ace4 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -32,9 +32,6 @@ * with checks for drivers and GPU support. */ - -#include "DNA_image_types.h" - #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" @@ -44,12 +41,11 @@ #include "BKE_global.h" -#include "GPU_glew.h" -#include "GPU_debug.h" +#include "GPU_basic_shader.h" #include "GPU_draw.h" #include "GPU_extensions.h" -#include "GPU_compositing.h" -#include "GPU_basic_shader.h" +#include "GPU_glew.h" +#include "GPU_texture.h" #include "intern/gpu_private.h" @@ -61,10 +57,6 @@ # include "BLI_winstuff.h" #endif -/* TODO(sergey): Find better default values for this constants. */ -#define MAX_DEFINE_LENGTH 1024 -#define MAX_EXT_DEFINE_LENGTH 1024 - /* Extensions support */ /* -- extension: version of GL that absorbs it @@ -80,61 +72,20 @@ * ARB_texture_query_lod: 4.0 */ -/* Non-generated shaders */ -extern char datatoc_gpu_program_smoke_frag_glsl[]; -extern char datatoc_gpu_program_smoke_color_frag_glsl[]; -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[]; - -typedef struct GPUShaders { - GPUShader *vsm_store; - GPUShader *sep_gaussian_blur; - GPUProgram *smoke; - GPUProgram *smoke_colored; - /* cache for shader fx. Those can exist in combinations so store them here */ - GPUShader *fx_shaders[MAX_FX_SHADERS * 2]; -} GPUShaders; - static struct GPUGlobal { GLint maxtexsize; GLint maxtextures; - GLuint currentfb; bool extdisabled; int colordepth; int samples_color_texture_max; GPUDeviceType device; GPUOSType os; GPUDriverType driver; - GPUShaders shaders; - GPUTexture *invalid_tex_1D; /* texture used in place of invalid textures (not loaded correctly, missing) */ - GPUTexture *invalid_tex_2D; - GPUTexture *invalid_tex_3D; float dfdyfactors[2]; /* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers calculate dfdy in shader differently when drawing to an offscreen buffer. First number is factor on screen and second is off-screen */ } GG = {1, 0}; -/* 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 - -struct GPUFrameBuffer { - GLuint object; - GPUTexture *colortex[GPU_FB_MAX_SLOTS]; - GPUTexture *depthtex; -}; - - /* GPU Types */ bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver) @@ -154,6 +105,16 @@ int GPU_max_texture_size(void) return GG.maxtexsize; } +int GPU_max_textures(void) +{ + return GG.maxtextures; +} + +int GPU_max_color_texture_samples(void) +{ + return GG.samples_color_texture_max; +} + void GPU_get_dfdy_factors(float fac[2]) { copy_v2_v2(fac, GG.dfdyfactors); @@ -323,7 +284,7 @@ bool GPU_geometry_shader_support(void) return (GLEW_VERSION_3_2 && GPU_legacy_support()) || GLEW_EXT_geometry_shader4; } -static bool GPU_geometry_shader_support_via_extension(void) +bool GPU_geometry_shader_support_via_extension(void) { return GLEW_EXT_geometry_shader4 && !(GLEW_VERSION_3_2 && GPU_legacy_support()); } @@ -338,1990 +299,26 @@ int GPU_color_depth(void) return GG.colordepth; } -static void GPU_print_framebuffer_error(GLenum status, char err_out[256]) -{ - const char *err = "unknown"; - - 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; - } - - if (err_out) { - BLI_snprintf(err_out, 256, "GPUFrameBuffer: framebuffer incomplete error %d '%s'", - (int)status, err); - } - else { - fprintf(stderr, "GPUFrameBuffer: framebuffer incomplete error %d '%s'\n", - (int)status, err); - } -} - -/* GPUTexture */ - -struct GPUTexture { - int w, h; /* width/height */ - int number; /* number for multitexture binding */ - int refcount; /* reference count */ - GLenum target; /* GL_TEXTURE_* */ - GLenum target_base; /* same as target, (but no multisample) */ - 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? */ -}; - -static unsigned char *GPU_texture_convert_pixels(int length, const float *fpixels) -{ - unsigned char *pixels, *p; - const float *fp = fpixels; - const int len = 4 * length; - int a; - - p = pixels = MEM_callocN(sizeof(unsigned char) * len, "GPUTexturePixels"); - - for (a = 0; a < len; a++, p++, fp++) - *p = FTOCHAR((*fp)); - - return pixels; -} - -static void GPU_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h) -{ - void *pixels = MEM_callocN(sizeof(char) * 4 * w * h, "GPUTextureEmptyPixels"); - - 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); - - MEM_freeN(pixels); -} - -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]) -{ - GPUTexture *tex; - GLenum type, format, internalformat; - void *pixels = NULL; - - if (samples) { - CLAMP_MAX(samples, GG.samples_color_texture_max); - } - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->w = w; - tex->h = h; - 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; - - 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()); - } - 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; - } - } - - if (fpixels && hdr_type == GPU_HDR_NONE) { - type = GL_UNSIGNED_BYTE; - pixels = GPU_texture_convert_pixels(w*h, fpixels); - } - } - - if (tex->target == GL_TEXTURE_1D) { - glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL); - - if (fpixels) { - glTexSubImage1D(tex->target, 0, 0, w, format, type, - pixels ? pixels : fpixels); - - if (tex->w > w) - GPU_glTexSubImageEmpty(tex->target, format, w, 0, - tex->w-w, 1); - } - } - else { - if (samples) { - glTexImage2DMultisample(tex->target, samples, internalformat, tex->w, tex->h, true); - } - 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); - } - } - - if (pixels) - MEM_freeN(pixels); - - if (depth) { - 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); - } - 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); - 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); - - return tex; -} - - -GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels) -{ - GPUTexture *tex; - GLenum type, format, internalformat; - void *pixels = NULL; - int r_width; - bool rescale = false; - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->w = w; - tex->h = h; - tex->depth = depth; - tex->number = -1; - tex->refcount = 1; - tex->target = GL_TEXTURE_3D; - tex->target_base = GL_TEXTURE_3D; - - glGenTextures(1, &tex->bindcode); - - if (!tex->bindcode) { - fprintf(stderr, "GPUTexture: texture create failed: %d\n", - (int)glGetError()); - 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; - } - else { - format = GL_RED; - internalformat = GL_INTENSITY8; - } - - /* 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); - 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); - } - - /* 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"); - - /* hardcore stuff, 3D texture rescaling - warning, this is gonna hurt your performance a lot, but we need it - * for gooseberry */ - if (rescale && fpixels) { - unsigned int i, j, k; - 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!"); - - for (k = 0; k < tex->depth; k++) { - for (j = 0; j < tex->h; j++) { - for (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); - - 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"); - } - } - - - 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); - - GPU_texture_unbind(tex); - - return tex; -} - -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, bool is_data, double time, int mipmap) -{ - GPUTexture *tex; - GLint w, h, border, bindcode; - - GPU_update_image_time(ima, time); - /* this binds a texture, so that's why to restore it to 0 */ - bindcode = GPU_verify_image(ima, iuser, 0, 0, mipmap, is_data); - - if (ima->gputexture) { - ima->gputexture->bindcode = bindcode; - glBindTexture(GL_TEXTURE_2D, 0); - return ima->gputexture; - } - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; - tex->number = -1; - tex->refcount = 1; - tex->target = GL_TEXTURE_2D; - tex->target_base = GL_TEXTURE_2D; - tex->fromblender = 1; - - ima->gputexture= tex; - - if (!glIsTexture(tex->bindcode)) { - GPU_ASSERT_NO_GL_ERRORS("Blender Texture Not Loaded"); - } - else { - glBindTexture(GL_TEXTURE_2D, tex->bindcode); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER, &border); - - tex->w = w - border; - tex->h = h - border; - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return tex; -} - -GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) -{ - GPUTexture *tex = prv->gputexture[0]; - GLint w, h; - GLuint bindcode = 0; - - if (tex) - bindcode = tex->bindcode; - - /* this binds a texture, so that's why we restore it to 0 */ - if (bindcode == 0) { - GPU_create_gl_tex(&bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], mipmap, 0, NULL); - } - if (tex) { - tex->bindcode = bindcode; - glBindTexture(GL_TEXTURE_2D, 0); - return tex; - } - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; - tex->number = -1; - tex->refcount = 1; - tex->target = GL_TEXTURE_2D; - tex->target_base = GL_TEXTURE_2D; - - prv->gputexture[0] = tex; - - if (!glIsTexture(tex->bindcode)) { - GPU_ASSERT_NO_GL_ERRORS("Blender Texture Not Loaded"); - } - else { - glBindTexture(GL_TEXTURE_2D, tex->bindcode); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - - tex->w = w; - tex->h = h; - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return tex; -} - -GPUTexture *GPU_texture_create_1D(int w, const float *fpixels, 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; -} - -GPUTexture *GPU_texture_create_2D(int w, int h, const float *fpixels, GPUHDRType hdr, 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; -} -GPUTexture *GPU_texture_create_2D_multisample(int w, int h, const float *fpixels, GPUHDRType hdr, 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; -} - -GPUTexture *GPU_texture_create_depth(int w, int h, 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; -} -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; -} - -/** - * 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 *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); - } - - return tex; -} - -GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]) -{ - GPUTexture *tex = GPU_texture_create_nD(w, h, 2, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out); - - 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); - - GPU_texture_unbind(tex); - } - - return tex; -} - -GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256]) -{ - GPUTexture *tex = GPU_texture_create_nD(w, 0, 1, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out); - - 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); - - GPU_texture_unbind(tex); - } - - return tex; -} - -void GPU_invalid_tex_init(void) -{ - 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); -} - -void GPU_invalid_tex_bind(int mode) -{ - switch (mode) { - case GL_TEXTURE_1D: - glBindTexture(GL_TEXTURE_1D, GG.invalid_tex_1D->bindcode); - break; - case GL_TEXTURE_2D: - glBindTexture(GL_TEXTURE_2D, GG.invalid_tex_2D->bindcode); - break; - case GL_TEXTURE_3D: - glBindTexture(GL_TEXTURE_3D, GG.invalid_tex_3D->bindcode); - break; - } -} - -void GPU_invalid_tex_free(void) -{ - if (GG.invalid_tex_1D) - GPU_texture_free(GG.invalid_tex_1D); - if (GG.invalid_tex_2D) - GPU_texture_free(GG.invalid_tex_2D); - if (GG.invalid_tex_3D) - GPU_texture_free(GG.invalid_tex_3D); -} - - -void GPU_texture_bind(GPUTexture *tex, int number) +bool GPU_mem_stats_supported(void) { - GLenum arbnumber; - - if (number >= GG.maxtextures) { - fprintf(stderr, "Not enough texture slots.\n"); - return; - } - - if ((G.debug & G_DEBUG)) { - if (tex->fb && tex->fb->object == GG.currentfb) { - fprintf(stderr, "Feedback loop warning!: Attempting to bind texture attached to current framebuffer!\n"); - } - } - - if (number < 0) - return; - - GPU_ASSERT_NO_GL_ERRORS("Pre Texture Bind"); - - arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number); - if (number != 0) glActiveTexture(arbnumber); - if (tex->bindcode != 0) { - glBindTexture(tex->target, tex->bindcode); - } - else - GPU_invalid_tex_bind(tex->target); - glEnable(tex->target); - if (number != 0) glActiveTexture(GL_TEXTURE0); - - tex->number = number; - - GPU_ASSERT_NO_GL_ERRORS("Post Texture Bind"); + return (GLEW_NVX_gpu_memory_info || (GLEW_ATI_meminfo)) && (G.debug & G_DEBUG_GPU_MEM); } -void GPU_texture_unbind(GPUTexture *tex) -{ - GLenum arbnumber; - - if (tex->number >= GG.maxtextures) { - fprintf(stderr, "Not enough texture slots.\n"); - return; - } - - if (tex->number == -1) - return; - - GPU_ASSERT_NO_GL_ERRORS("Pre Texture Unbind"); - - arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); - if (tex->number != 0) glActiveTexture(arbnumber); - glBindTexture(tex->target, 0); - glDisable(tex->target_base); - if (tex->number != 0) glActiveTexture(GL_TEXTURE0); - tex->number = -1; - - GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); -} - -void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter) +void GPU_mem_stats_get(int *totalmem, int *freemem) { - GLenum arbnumber; - - if (tex->number >= GG.maxtextures) { - fprintf(stderr, "Not enough texture slots.\n"); - return; - } - - if (tex->number == -1) - return; - - GPU_ASSERT_NO_GL_ERRORS("Pre Texture Unbind"); - - arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); - if (tex->number != 0) glActiveTexture(arbnumber); + if (GLEW_NVX_gpu_memory_info) { + /* returned value in Kb */ + glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, totalmem); - 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); + glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, freemem); } + else if (GLEW_ATI_meminfo) { + int stats[4]; - 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); - - GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); -} - -void GPU_texture_free(GPUTexture *tex) -{ - tex->refcount--; - - if (tex->refcount < 0) - 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); - - MEM_freeN(tex); - } -} - -void GPU_texture_ref(GPUTexture *tex) -{ - tex->refcount++; -} - -int GPU_texture_target(const GPUTexture *tex) -{ - return tex->target; -} - -int GPU_texture_width(const GPUTexture *tex) -{ - return tex->w; -} - -int GPU_texture_height(const GPUTexture *tex) -{ - return tex->h; -} - -int GPU_texture_opengl_bindcode(const GPUTexture *tex) -{ - return tex->bindcode; -} - -GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex) -{ - return tex->fb; -} - -/* GPUFrameBuffer */ - -GPUFrameBuffer *GPU_framebuffer_create(void) -{ - GPUFrameBuffer *fb; - - if (!(GLEW_VERSION_3_0 || GLEW_ARB_framebuffer_object || (GLEW_EXT_framebuffer_object && GLEW_EXT_framebuffer_blit))) - return NULL; - - fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); - glGenFramebuffersEXT(1, &fb->object); - - if (!fb->object) { - fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed. %d\n", - (int)glGetError()); - GPU_framebuffer_free(fb); - return NULL; - } - - /* 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; -} - -int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) -{ - GLenum attachment; - GLenum error; - - if (slot >= GPU_FB_MAX_SLOTS) { - 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 (tex->number != -1) { - fprintf(stderr, - "Feedback loop warning!: " - "Attempting to attach texture to framebuffer while still bound to texture unit for drawing!\n"); - } - } - - if (tex->depth) - 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, - tex->target, tex->bindcode, 0); - - error = glGetError(); - - if (error == GL_INVALID_OPERATION) { - GPU_framebuffer_restore(); - GPU_print_framebuffer_error(error, err_out); - return 0; - } - - if (tex->depth) - fb->depthtex = tex; - else - fb->colortex[slot] = tex; - - tex->fb= fb; - tex->fb_attachment = slot; - - return 1; -} - -void GPU_framebuffer_texture_detach(GPUTexture *tex) -{ - GLenum attachment; - GPUFrameBuffer *fb; - - if (!tex->fb) - return; - - fb = tex->fb; - - if (GG.currentfb != fb->object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - GG.currentfb = tex->fb->object; - } - - if (tex->depth) { - fb->depthtex = NULL; - attachment = GL_DEPTH_ATTACHMENT_EXT; - } - else { - BLI_assert(fb->colortex[tex->fb_attachment] == tex); - fb->colortex[tex->fb_attachment] = NULL; - attachment = GL_COLOR_ATTACHMENT0_EXT + tex->fb_attachment; - } - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, tex->target, 0, 0); - - tex->fb = NULL; - tex->fb_attachment = -1; -} - -void GPU_texture_bind_as_framebuffer(GPUTexture *tex) -{ - if (!tex->fb) { - fprintf(stderr, "Error, texture not bound to framebuffer!\n"); - return; - } - - /* push attributes */ - glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); - glDisable(GL_SCISSOR_TEST); - - /* bind framebuffer */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tex->fb->object); - - if (tex->depth) { - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - } - else { - /* last bound prevails here, better allow explicit control here too */ - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + tex->fb_attachment); - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + tex->fb_attachment); - } - - if (tex->target == GL_TEXTURE_2D_MULTISAMPLE) { - glEnable(GL_MULTISAMPLE); - } - - /* push matrices and set default viewport and matrix */ - glViewport(0, 0, tex->w, tex->h); - GG.currentfb = tex->fb->object; - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); -} - -void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot) -{ - int numslots = 0, i; - GLenum attachments[4]; - - if (!fb->colortex[slot]) { - fprintf(stderr, "Error, framebuffer slot empty!\n"); - return; - } - - for (i = 0; i < 4; i++) { - if (fb->colortex[i]) { - attachments[numslots] = GL_COLOR_ATTACHMENT0_EXT + i; - numslots++; - } - } - - /* push attributes */ - glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); - glDisable(GL_SCISSOR_TEST); - - /* bind framebuffer */ - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - - /* last bound prevails here, better allow explicit control here too */ - glDrawBuffers(numslots, attachments); - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + slot); - - /* push matrices and set default viewport and matrix */ - glViewport(0, 0, fb->colortex[slot]->w, fb->colortex[slot]->h); - GG.currentfb = fb->object; - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); -} - - -void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex)) -{ - /* restore matrix */ - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - /* restore attributes */ - glPopAttrib(); -} - -void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot) -{ - 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, fb->colortex[slot]->w, fb->colortex[slot]->h); - GG.currentfb = fb->object; - GG.currentfb = fb->object; -} - -bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) -{ - GLenum status; - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - GG.currentfb = fb->object; - - /* Clean glError buffer. */ - while (glGetError() != GL_NO_ERROR) {} - - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - GPU_framebuffer_restore(); - GPU_print_framebuffer_error(status, err_out); - return false; - } - - return true; -} - -void GPU_framebuffer_free(GPUFrameBuffer *fb) -{ - int i; - if (fb->depthtex) - GPU_framebuffer_texture_detach(fb->depthtex); - - for (i = 0; i < GPU_FB_MAX_SLOTS; i++) { - if (fb->colortex[i]) { - GPU_framebuffer_texture_detach(fb->colortex[i]); - } - } - - if (fb->object) { - glDeleteFramebuffersEXT(1, &fb->object); - - if (GG.currentfb == fb->object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - GG.currentfb = 0; - } - } - - MEM_freeN(fb); -} - -void GPU_framebuffer_restore(void) -{ - if (GG.currentfb != 0) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - GG.currentfb = 0; - } -} - -void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *blurfb, GPUTexture *blurtex) -{ - const float scaleh[2] = {1.0f / blurtex->w, 0.0f}; - const float scalev[2] = {0.0f, 1.0f / tex->h}; - - GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR); - int scale_uniform, texture_source_uniform; - - if (!blur_shader) - return; - - scale_uniform = GPU_shader_get_uniform(blur_shader, "ScaleU"); - texture_source_uniform = GPU_shader_get_uniform(blur_shader, "textureSource"); - - /* Blurring horizontally */ - - /* 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); - - /* avoid warnings from texture binding */ - GG.currentfb = blurfb->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, blurtex->w, blurtex->h); - - /* Peparing to draw quad */ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - glDisable(GL_DEPTH_TEST); - - GPU_texture_bind(tex, 0); - - /* 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(); - - /* Blurring vertically */ - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - - GG.currentfb = fb->object; - - glViewport(0, 0, tex->w, tex->h); - 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); - - 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(); - - GPU_shader_unbind(); -} - -/* GPUOffScreen */ - -struct GPUOffScreen { - GPUFrameBuffer *fb; - GPUTexture *color; - GPUTexture *depth; -}; - -GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, 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; - } - - 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 (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) { - 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; - } - - if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) { - GPU_offscreen_free(ofs); - return NULL; - } - - /* check validity at the very end! */ - if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) { - GPU_offscreen_free(ofs); - return NULL; - } - - GPU_framebuffer_restore(); - - return ofs; -} - -void GPU_offscreen_free(GPUOffScreen *ofs) -{ - if (ofs->fb) - GPU_framebuffer_free(ofs->fb); - if (ofs->color) - GPU_texture_free(ofs->color); - if (ofs->depth) - GPU_texture_free(ofs->depth); - - MEM_freeN(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); - } -} - -void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) -{ - if (restore) - GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); - GPU_framebuffer_restore(); - glEnable(GL_SCISSOR_TEST); -} - -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) -{ - const int w = ofs->color->w; - const int h = ofs->color->h; - - if (ofs->color->target == 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 - - /* 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) { - goto finally; - } - - /* perform the copy */ - glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - - /* read the results */ - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 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 - - -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"); - } - else { - glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); - } -} - -int GPU_offscreen_width(const GPUOffScreen *ofs) -{ - return ofs->color->w; -} - -int GPU_offscreen_height(const GPUOffScreen *ofs) -{ - return ofs->color->h; -} - -int GPU_offscreen_color_texture(const GPUOffScreen *ofs) -{ - return ofs->color->bindcode; -} - -/* 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 */ -}; - -struct GPUProgram { - GPUProgramType type; - GLuint prog; -}; - - -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++) { - const char *c, *pos, *end = code[i] + strlen(code[i]); - - if (G.debug & G_DEBUG) { - fprintf(stderr, "===== shader string %d ====\n", i + 1); - - c = code[i]; - while ((c < end) && (pos = strchr(c, '\n'))) { - fprintf(stderr, "%2d ", line); - fwrite(c, (pos + 1) - c, 1, stderr); - c = pos + 1; - line++; - } - - fprintf(stderr, "%s", c); - } - } - - fprintf(stderr, "%s\n", log); -} - -static const char *gpu_shader_version(void) -{ - if (GLEW_VERSION_3_2) { - if (GPU_legacy_support()) { - 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 */ - } -} - - -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH], bool use_geometry_shader) -{ - /* 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 */ - 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) -{ - /* some useful defines to detect GPU type */ - if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_ATI\n"); - if (GLEW_VERSION_3_0) { - /* TODO(merwin): revisit this version check; GLEW_VERSION_3_0 means GL 3.0 or newer */ - strcat(defines, "#define CLIP_WORKAROUND\n"); - } - } - else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) - strcat(defines, "#define GPU_NVIDIA\n"); - else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) - strcat(defines, "#define GPU_INTEL\n"); - - if (GPU_bicubic_bump_support()) - strcat(defines, "#define BUMP_BICUBIC\n"); - -#ifdef WITH_OPENSUBDIV - /* TODO(sergey): Check whether we actually compiling shader for - * the OpenSubdiv mesh. - */ - if (use_opensubdiv) { - strcat(defines, "#define USE_OPENSUBDIV\n"); - - /* TODO(sergey): not strictly speaking a define, but this is - * a global typedef which we don't have better place to define - * in yet. - */ - strcat(defines, "struct VertexData {\n" - " vec4 position;\n" - " vec3 normal;\n" - " vec2 uv;" - "};\n"); - } -#else - UNUSED_VARS(use_opensubdiv); -#endif - - return; -} - -void GPU_program_bind(GPUProgram *program) -{ - glEnable(program->type); - glBindProgramARB(program->type, program->prog); -} - -void GPU_program_unbind(GPUProgram *program) -{ - glDisable(program->type); - glBindProgramARB(program->type, 0); -} - - -GPUProgram *GPU_program_shader_create(GPUProgramType type, const char *code) -{ - /* TODO(merwin): remove ARB program support (recode smoke shader in GLSL) */ - - GPUProgram *program; - GLint error_pos, is_native; - - if (!(GLEW_ARB_fragment_program && type == GPU_PROGRAM_TYPE_FRAGMENT)) - return NULL; - - program = MEM_callocN(sizeof(GPUProgram), "GPUProgram"); - - switch (type) { - case GPU_PROGRAM_TYPE_FRAGMENT: - program->type = GL_FRAGMENT_PROGRAM_ARB; - break; - } - - /* create the object and set its code string */ - glGenProgramsARB(1, &program->prog); - glBindProgramARB(program->type, program->prog); - - glProgramStringARB(program->type, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(code), code); - - glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_pos); - glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &is_native); - if ((error_pos == -1) && (is_native == 1)) { - return program; - } - else { - /* glGetError is set before that, clear it */ - while (glGetError() != GL_NO_ERROR) - ; - shader_print_errors("compile", (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB), &code, 1); - MEM_freeN(program); - } - - return NULL; -} - -void GPU_program_free(GPUProgram *program) -{ - glDeleteProgramsARB(1, &program->prog); - MEM_freeN(program); -} - -void GPU_program_parameter_4f(GPUProgram *program, unsigned int location, float x, float y, float z, float w) -{ - glProgramLocalParameter4fARB(program->type, location, x, y, z, w); -} - -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) -{ - return GPU_shader_create_ex(vertexcode, - fragcode, - geocode, - libcode, - defines, - input, - output, - number, - GPU_SHADER_FLAGS_NONE); -} - -GPUShader *GPU_shader_create_ex(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - int input, - int output, - int number, - const int flags) -{ -#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); - bool use_opensubdiv = false; -#endif - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GPUShader *shader; - 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"); - - if (vertexcode) - shader->vertex = glCreateShader(GL_VERTEX_SHADER); - if (fragcode) - shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); - if (geocode) - shader->geometry = glCreateShader(GL_GEOMETRY_SHADER_EXT); - - shader->program = glCreateProgram(); - - if (!shader->program || - (vertexcode && !shader->vertex) || - (fragcode && !shader->fragment) || - (geocode && !shader->geometry)) - { - fprintf(stderr, "GPUShader, object creation failed.\n"); - GPU_shader_free(shader); - return NULL; - } - - gpu_shader_standard_defines(standard_defines, use_opensubdiv); - gpu_shader_standard_extensions(standard_extensions, geocode != NULL); - - if (vertexcode) { - const char *source[5]; - /* custom limit, may be too small, beware */ - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) source[num_source++] = defines; - source[num_source++] = vertexcode; - - glAttachShader(shader->program, shader->vertex); - glShaderSource(shader->vertex, num_source, source, NULL); - - glCompileShader(shader->vertex); - glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (fragcode) { - const char *source[7]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - -#ifdef WITH_OPENSUBDIV - /* TODO(sergey): Move to fragment shader source code generation. */ - if (use_opensubdiv) { - source[num_source++] = - "#ifdef USE_OPENSUBDIV\n" - "in block {\n" - " VertexData v;\n" - "} inpt;\n" - "#endif\n"; - } -#endif - - if (defines) source[num_source++] = defines; - if (libcode) source[num_source++] = libcode; - source[num_source++] = fragcode; - - glAttachShader(shader->program, shader->fragment); - glShaderSource(shader->fragment, num_source, source, NULL); - - glCompileShader(shader->fragment); - glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (geocode) { - const char *source[6]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) source[num_source++] = defines; - source[num_source++] = geocode; - - glAttachShader(shader->program, shader->geometry); - glShaderSource(shader->geometry, num_source, source, NULL); - - glCompileShader(shader->geometry); - glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - 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 - - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof(log), &length, log); - /* print attached shaders in pipeline order */ - if (vertexcode) shader_print_errors("linking", log, &vertexcode, 1); - if (geocode) shader_print_errors("linking", log, &geocode, 1); - if (libcode) shader_print_errors("linking", log, &libcode, 1); - if (fragcode) shader_print_errors("linking", log, &fragcode, 1); - - GPU_shader_free(shader); - return NULL; - } - -#ifdef WITH_OPENSUBDIV - /* TODO(sergey): Find a better place for this. */ - if (use_opensubdiv && GLEW_VERSION_4_1) { - glProgramUniform1i(shader->program, - glGetUniformLocation(shader->program, "FVarDataBuffer"), - 31); /* GL_TEXTURE31 */ - } -#endif - - return shader; -} - -void GPU_shader_bind(GPUShader *shader) -{ - GPU_ASSERT_NO_GL_ERRORS("Pre Shader Bind"); - glUseProgram(shader->program); - GPU_ASSERT_NO_GL_ERRORS("Post Shader Bind"); -} - -void GPU_shader_unbind(void) -{ - GPU_ASSERT_NO_GL_ERRORS("Pre Shader Unbind"); - glUseProgram(0); - GPU_ASSERT_NO_GL_ERRORS("Post Shader Unbind"); -} - -void GPU_shader_free(GPUShader *shader) -{ - if (shader->vertex) - glDeleteShader(shader->vertex); - if (shader->geometry) - glDeleteShader(shader->geometry); - if (shader->fragment) - glDeleteShader(shader->fragment); - if (shader->program) - glDeleteProgram(shader->program); - MEM_freeN(shader); -} - -int GPU_shader_get_uniform(GPUShader *shader, const char *name) -{ - return glGetUniformLocation(shader->program, name); -} - -void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) -{ - 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) -{ - 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) -{ - if (location == -1) - return; - - GPU_CHECK_ERRORS_AROUND(glUniform1i(location, value)); -} - -void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) -{ - 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); - } -} - -void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) -{ - GLenum arbnumber; - - if (tex->number >= GG.maxtextures) { - fprintf(stderr, "Not enough texture slots.\n"); - return; - } - - if (tex->number == -1) - return; - - if (location == -1) - return; - - GPU_ASSERT_NO_GL_ERRORS("Pre Uniform Texture"); - - arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); - - if (tex->number != 0) glActiveTexture(arbnumber); - if (tex->bindcode != 0) - glBindTexture(tex->target, tex->bindcode); - else - GPU_invalid_tex_bind(tex->target); - glUniform1i(location, tex->number); - glEnable(tex->target); - if (tex->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; -} - -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; - } - - if (retval == NULL) - printf("Unable to create a GPUShader for builtin shader: %u\n", shader); - - return retval; -} - -GPUProgram *GPU_shader_get_builtin_program(GPUBuiltinProgram program) -{ - GPUProgram *retval = NULL; - - switch (program) { - case GPU_PROGRAM_SMOKE: - if (!GG.shaders.smoke) - GG.shaders.smoke = GPU_program_shader_create(GPU_PROGRAM_TYPE_FRAGMENT, datatoc_gpu_program_smoke_frag_glsl); - retval = GG.shaders.smoke; - break; - case GPU_PROGRAM_SMOKE_COLORED: - if (!GG.shaders.smoke_colored) - GG.shaders.smoke_colored = GPU_program_shader_create(GPU_PROGRAM_TYPE_FRAGMENT, datatoc_gpu_program_smoke_color_frag_glsl); - retval = GG.shaders.smoke_colored; - break; - } - - if (retval == NULL) - printf("Unable to create a GPUProgram for builtin program: %u\n", program); - - return retval; -} - -#define MAX_DEFINES 100 - -GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp) -{ - int offset; - char defines[MAX_DEFINES] = ""; - /* avoid shaders out of range */ - if (effects >= MAX_FX_SHADERS) - return NULL; - - offset = 2 * effects; - - if (persp) { - offset += 1; - strcat(defines, "#define PERSP_MATRIX\n"); - } - - if (!GG.shaders.fx_shaders[offset]) { - GPUShader *shader; - - switch (effects) { - case GPU_SHADER_FX_SSAO: - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE: - strcat(defines, "#define FIRST_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO: - strcat(defines, "#define SECOND_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE: - strcat(defines, "#define THIRD_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR: - strcat(defines, "#define FOURTH_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE: - strcat(defines, "#define FIFTH_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE: - strcat(defines, "#define FIRST_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - 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); - GG.shaders.fx_shaders[offset] = shader; - break; - - case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE: - strcat(defines, "#define THIRD_PASS\n"); - GG.shaders.fx_shaders[offset] = 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); - break; - - case GPU_SHADER_FX_DEPTH_RESOLVE: - GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_depth_resolve_glsl, NULL, NULL, defines, 0, 0, 0); - } - } - - 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 (GG.shaders.sep_gaussian_blur) { - GPU_shader_free(GG.shaders.sep_gaussian_blur); - GG.shaders.sep_gaussian_blur = NULL; - } - - if (GG.shaders.smoke) { - GPU_program_free(GG.shaders.smoke); - GG.shaders.smoke = NULL; - } - - if (GG.shaders.smoke_colored) { - GPU_program_free(GG.shaders.smoke_colored); - GG.shaders.smoke_colored = NULL; - } - - 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; - } - } -} - -bool GPU_mem_stats_supported(void) -{ - return (GLEW_NVX_gpu_memory_info || (GLEW_ATI_meminfo)) && (G.debug & G_DEBUG_GPU_MEM); -} - - -void GPU_mem_stats_get(int *totalmem, int *freemem) -{ - if (GLEW_NVX_gpu_memory_info) { - /* returned value in Kb */ - glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, totalmem); - - glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, freemem); - } - else if (GLEW_ATI_meminfo) { - int stats[4]; - - glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, stats); - *freemem = stats[0]; - *totalmem = 0; + glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, stats); + *freemem = stats[0]; + *totalmem = 0; } else { *totalmem = 0; @@ -2329,117 +326,3 @@ void GPU_mem_stats_get(int *totalmem, int *freemem) } } - -#if 0 /* unused */ - -/* GPUPixelBuffer */ - -typedef struct GPUPixelBuffer { - GLuint bindcode[2]; - GLuint current; - int datasize; - int numbuffers; - int halffloat; -} GPUPixelBuffer; - -void GPU_pixelbuffer_free(GPUPixelBuffer *pb) -{ - if (pb->bindcode[0]) - glDeleteBuffers(pb->numbuffers, pb->bindcode); - MEM_freeN(pb); -} - -GPUPixelBuffer *gpu_pixelbuffer_create(int x, int y, int halffloat, int numbuffers) -{ - GPUPixelBuffer *pb; - - pb = MEM_callocN(sizeof(GPUPixelBuffer), "GPUPBO"); - pb->datasize = x * y * 4 * (halffloat ? 16 : 8); - pb->numbuffers = numbuffers; - pb->halffloat = halffloat; - - glGenBuffers(pb->numbuffers, pb->bindcode); - - if (!pb->bindcode[0]) { - fprintf(stderr, "GPUPixelBuffer allocation failed\n"); - GPU_pixelbuffer_free(pb); - return NULL; - } - - return pb; -} - -void GPU_pixelbuffer_texture(GPUTexture *tex, GPUPixelBuffer *pb) -{ - void *pixels; - int i; - - glBindTexture(GL_TEXTURE_RECTANGLE, tex->bindcode); - - for (i = 0; i < pb->numbuffers; i++) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pb->bindcode[pb->current]); - glBufferData(GL_PIXEL_UNPACK_BUFFER, pb->datasize, NULL, - GL_STREAM_DRAW); - - pixels = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - -# if 0 - memcpy(pixels, _oImage.data(), pb->datasize); -# endif - - if (!glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)) { - fprintf(stderr, "Could not unmap OpenGL PBO\n"); - break; - } - } - - glBindTexture(GL_TEXTURE_RECTANGLE, 0); -} - -static int pixelbuffer_map_into_gpu(GLuint bindcode) -{ - void *pixels; - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bindcode); - pixels = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - - /* do stuff in pixels */ - - if (!glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)) { - fprintf(stderr, "Could not unmap OpenGL PBO\n"); - return 0; - } - - return 1; -} - -static void pixelbuffer_copy_to_texture(GPUTexture *tex, GPUPixelBuffer *pb, GLuint bindcode) -{ - GLenum type = (pb->halffloat)? GL_HALF_FLOAT_NV: GL_UNSIGNED_BYTE; - glBindTexture(GL_TEXTURE_RECTANGLE, tex->bindcode); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bindcode); - - glTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, tex->w, tex->h, GL_RGBA, type, NULL); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glBindTexture(GL_TEXTURE_RECTANGLE, 0); -} - -void GPU_pixelbuffer_async_to_gpu(GPUTexture *tex, GPUPixelBuffer *pb) -{ - int newbuffer; - - if (pb->numbuffers == 1) { - pixelbuffer_copy_to_texture(tex, pb, pb->bindcode[0]); - pixelbuffer_map_into_gpu(pb->bindcode[0]); - } - else { - pb->current = (pb->current + 1) % pb->numbuffers; - newbuffer = (pb->current + 1) % pb->numbuffers; - - pixelbuffer_map_into_gpu(pb->bindcode[newbuffer]); - pixelbuffer_copy_to_texture(tex, pb, pb->bindcode[pb->current]); - } -} -#endif /* unused */ - diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c new file mode 100644 index 00000000000..4ef7611a3f3 --- /dev/null +++ b/source/blender/gpu/intern/gpu_framebuffer.c @@ -0,0 +1,633 @@ +/* + * ***** 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): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_base.h" +#include "BLI_math_vector.h" + +#include "BKE_global.h" + +#include "GPU_debug.h" +#include "GPU_glew.h" +#include "GPU_framebuffer.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +static struct GPUFrameBufferGlobal { + GLuint currentfb; +} GG = {0}; + +/* 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 + +struct GPUFrameBuffer { + GLuint object; + GPUTexture *colortex[GPU_FB_MAX_SLOTS]; + GPUTexture *depthtex; +}; + +static void GPU_print_framebuffer_error(GLenum status, char err_out[256]) +{ + const char *err = "unknown"; + + 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; + } + + if (err_out) { + BLI_snprintf(err_out, 256, "GPUFrameBuffer: framebuffer incomplete error %d '%s'", + (int)status, err); + } + else { + fprintf(stderr, "GPUFrameBuffer: framebuffer incomplete error %d '%s'\n", + (int)status, err); + } +} + +/* GPUFrameBuffer */ + +GPUFrameBuffer *GPU_framebuffer_create(void) +{ + GPUFrameBuffer *fb; + + if (!(GLEW_VERSION_3_0 || GLEW_ARB_framebuffer_object || (GLEW_EXT_framebuffer_object && GLEW_EXT_framebuffer_blit))) + return NULL; + + fb = MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + glGenFramebuffersEXT(1, &fb->object); + + if (!fb->object) { + fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed. %d\n", + (int)glGetError()); + GPU_framebuffer_free(fb); + return NULL; + } + + /* 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; +} + +int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, char err_out[256]) +{ + GLenum attachment; + GLenum error; + + if (slot >= GPU_FB_MAX_SLOTS) { + 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"); + } + } + + 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); + + error = glGetError(); + + if (error == GL_INVALID_OPERATION) { + GPU_framebuffer_restore(); + GPU_print_framebuffer_error(error, err_out); + return 0; + } + + if (GPU_texture_depth(tex)) + fb->depthtex = tex; + else + fb->colortex[slot] = tex; + + GPU_texture_framebuffer_set(tex, fb, slot); + + return 1; +} + +void GPU_framebuffer_texture_detach(GPUTexture *tex) +{ + GLenum attachment; + GPUFrameBuffer *fb = GPU_texture_framebuffer(tex); + int fb_attachment = GPU_texture_framebuffer_attachment(tex); + + if (!fb) + return; + + if (GG.currentfb != fb->object) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); + GG.currentfb = fb->object; + } + + 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; + } + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, GPU_texture_target(tex), 0, 0); + + GPU_texture_framebuffer_set(tex, NULL, -1); +} + +void GPU_texture_bind_as_framebuffer(GPUTexture *tex) +{ + GPUFrameBuffer *fb = GPU_texture_framebuffer(tex); + int fb_attachment = GPU_texture_framebuffer_attachment(tex); + + if (!fb) { + fprintf(stderr, "Error, texture not bound to framebuffer!\n"); + return; + } + + /* push attributes */ + glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); + glDisable(GL_SCISSOR_TEST); + + /* bind framebuffer */ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); + + if (GPU_texture_depth(tex)) { + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + 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); + } + + 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(); +} + +void GPU_framebuffer_slots_bind(GPUFrameBuffer *fb, int slot) +{ + int numslots = 0, i; + GLenum attachments[4]; + + if (!fb->colortex[slot]) { + fprintf(stderr, "Error, framebuffer slot empty!\n"); + return; + } + + for (i = 0; i < 4; i++) { + if (fb->colortex[i]) { + attachments[numslots] = GL_COLOR_ATTACHMENT0_EXT + i; + numslots++; + } + } + + /* push attributes */ + glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT); + glDisable(GL_SCISSOR_TEST); + + /* bind framebuffer */ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); + + /* last bound prevails here, better allow explicit control here too */ + glDrawBuffers(numslots, attachments); + 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; + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); +} + + +void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex)) +{ + /* restore matrix */ + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + /* restore attributes */ + glPopAttrib(); +} + +void GPU_framebuffer_bind_no_save(GPUFrameBuffer *fb, int slot) +{ + 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; +} + +bool GPU_framebuffer_bound(GPUFrameBuffer *fb) +{ + return fb->object == GG.currentfb; +} + +bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) +{ + GLenum status; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); + GG.currentfb = fb->object; + + /* Clean glError buffer. */ + while (glGetError() != GL_NO_ERROR) {} + + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + GPU_framebuffer_restore(); + GPU_print_framebuffer_error(status, err_out); + return false; + } + + return true; +} + +void GPU_framebuffer_free(GPUFrameBuffer *fb) +{ + int i; + if (fb->depthtex) + GPU_framebuffer_texture_detach(fb->depthtex); + + for (i = 0; i < GPU_FB_MAX_SLOTS; i++) { + if (fb->colortex[i]) { + GPU_framebuffer_texture_detach(fb->colortex[i]); + } + } + + if (fb->object) { + glDeleteFramebuffersEXT(1, &fb->object); + + if (GG.currentfb == fb->object) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + GG.currentfb = 0; + } + } + + MEM_freeN(fb); +} + +void GPU_framebuffer_restore(void) +{ + if (GG.currentfb != 0) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + GG.currentfb = 0; + } +} + +void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *blurfb, GPUTexture *blurtex) +{ + const float scaleh[2] = {1.0f / GPU_texture_width(blurtex), 0.0f}; + const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(tex)}; + + GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR); + int scale_uniform, texture_source_uniform; + + if (!blur_shader) + return; + + scale_uniform = GPU_shader_get_uniform(blur_shader, "ScaleU"); + texture_source_uniform = GPU_shader_get_uniform(blur_shader, "textureSource"); + + /* Blurring horizontally */ + + /* 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); + + /* avoid warnings from texture binding */ + GG.currentfb = blurfb->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)); + + /* Peparing to draw quad */ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glDisable(GL_DEPTH_TEST); + + GPU_texture_bind(tex, 0); + + /* 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(); + + /* Blurring vertically */ + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + + GG.currentfb = fb->object; + + 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); + + 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(); + + GPU_shader_unbind(); +} + +/* GPUOffScreen */ + +struct GPUOffScreen { + GPUFrameBuffer *fb; + GPUTexture *color; + GPUTexture *depth; +}; + +GPUOffScreen *GPU_offscreen_create(int width, int height, int samples, 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; + } + + 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 (!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, 0, err_out)) { + 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; + } + + if (!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, 0, err_out)) { + GPU_offscreen_free(ofs); + return NULL; + } + + /* check validity at the very end! */ + if (!GPU_framebuffer_check_valid(ofs->fb, err_out)) { + GPU_offscreen_free(ofs); + return NULL; + } + + GPU_framebuffer_restore(); + + return ofs; +} + +void GPU_offscreen_free(GPUOffScreen *ofs) +{ + if (ofs->fb) + GPU_framebuffer_free(ofs->fb); + if (ofs->color) + GPU_texture_free(ofs->color); + if (ofs->depth) + GPU_texture_free(ofs->depth); + + MEM_freeN(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); + } +} + +void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore) +{ + if (restore) + GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); + GPU_framebuffer_restore(); + glEnable(GL_SCISSOR_TEST); +} + +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); + + 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 = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + goto finally; + } +#endif + + /* write into new single-sample buffer */ + glGenFramebuffersEXT(1, &fbo_blit); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit); + glFramebufferTexture2DEXT( + GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tex_blit, 0); + status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + goto finally; + } + + /* perform the copy */ + glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + /* read the results */ + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 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 + + +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"); + } + else { + glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); + } +} + +int GPU_offscreen_width(const GPUOffScreen *ofs) +{ + return GPU_texture_width(ofs->color); +} + +int GPU_offscreen_height(const GPUOffScreen *ofs) +{ + return GPU_texture_height(ofs->color); +} + +int GPU_offscreen_color_texture(const GPUOffScreen *ofs) +{ + return GPU_texture_opengl_bindcode(ofs->color); +} + diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 8926e6914ec..a176297de28 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -61,7 +61,10 @@ #include "IMB_imbuf_types.h" #include "GPU_extensions.h" +#include "GPU_framebuffer.h" #include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_texture.h" #include "gpu_codegen.h" diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c new file mode 100644 index 00000000000..83413dfdcb1 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader.c @@ -0,0 +1,783 @@ +/* + * ***** 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): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_base.h" +#include "BLI_math_vector.h" + +#include "BKE_global.h" + +#include "GPU_compositing.h" +#include "GPU_debug.h" +#include "GPU_extensions.h" +#include "GPU_glew.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +/* TODO(sergey): Find better default values for this constants. */ +#define MAX_DEFINE_LENGTH 1024 +#define MAX_EXT_DEFINE_LENGTH 1024 + +/* Non-generated shaders */ +extern char datatoc_gpu_program_smoke_frag_glsl[]; +extern char datatoc_gpu_program_smoke_color_frag_glsl[]; +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; + GPUProgram *smoke; + GPUProgram *smoke_colored; + /* 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 */ +}; + +struct GPUProgram { + GPUProgramType type; + GLuint prog; +}; + +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++) { + const char *c, *pos, *end = code[i] + strlen(code[i]); + + if (G.debug & G_DEBUG) { + fprintf(stderr, "===== shader string %d ====\n", i + 1); + + c = code[i]; + while ((c < end) && (pos = strchr(c, '\n'))) { + fprintf(stderr, "%2d ", line); + fwrite(c, (pos + 1) - c, 1, stderr); + c = pos + 1; + line++; + } + + fprintf(stderr, "%s", c); + } + } + + fprintf(stderr, "%s\n", log); +} + +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 */ + } +} + + +static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH], bool use_geometry_shader) +{ + /* 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 */ + 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) +{ + /* some useful defines to detect GPU type */ + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_ATI\n"); + if (GLEW_VERSION_3_0) { + /* TODO(merwin): revisit this version check; GLEW_VERSION_3_0 means GL 3.0 or newer */ + strcat(defines, "#define CLIP_WORKAROUND\n"); + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) + strcat(defines, "#define GPU_NVIDIA\n"); + else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) + strcat(defines, "#define GPU_INTEL\n"); + + if (GPU_bicubic_bump_support()) + strcat(defines, "#define BUMP_BICUBIC\n"); + +#ifdef WITH_OPENSUBDIV + /* TODO(sergey): Check whether we actually compiling shader for + * the OpenSubdiv mesh. + */ + if (use_opensubdiv) { + strcat(defines, "#define USE_OPENSUBDIV\n"); + + /* TODO(sergey): not strictly speaking a define, but this is + * a global typedef which we don't have better place to define + * in yet. + */ + strcat(defines, "struct VertexData {\n" + " vec4 position;\n" + " vec3 normal;\n" + " vec2 uv;" + "};\n"); + } +#else + UNUSED_VARS(use_opensubdiv); +#endif + + return; +} + +void GPU_program_bind(GPUProgram *program) +{ + glEnable(program->type); + glBindProgramARB(program->type, program->prog); +} + +void GPU_program_unbind(GPUProgram *program) +{ + glDisable(program->type); + glBindProgramARB(program->type, 0); +} + + +GPUProgram *GPU_program_shader_create(GPUProgramType type, const char *code) +{ + /* TODO(merwin): remove ARB program support (recode smoke shader in GLSL) */ + + GPUProgram *program; + GLint error_pos, is_native; + + if (!(GLEW_ARB_fragment_program && type == GPU_PROGRAM_TYPE_FRAGMENT)) + return NULL; + + program = MEM_callocN(sizeof(GPUProgram), "GPUProgram"); + + switch (type) { + case GPU_PROGRAM_TYPE_FRAGMENT: + program->type = GL_FRAGMENT_PROGRAM_ARB; + break; + } + + /* create the object and set its code string */ + glGenProgramsARB(1, &program->prog); + glBindProgramARB(program->type, program->prog); + + glProgramStringARB(program->type, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(code), code); + + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_pos); + glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &is_native); + if ((error_pos == -1) && (is_native == 1)) { + return program; + } + else { + /* glGetError is set before that, clear it */ + while (glGetError() != GL_NO_ERROR) + ; + shader_print_errors("compile", (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB), &code, 1); + MEM_freeN(program); + } + + return NULL; +} + +void GPU_program_free(GPUProgram *program) +{ + glDeleteProgramsARB(1, &program->prog); + MEM_freeN(program); +} + +void GPU_program_parameter_4f(GPUProgram *program, unsigned int location, float x, float y, float z, float w) +{ + glProgramLocalParameter4fARB(program->type, location, x, y, z, w); +} + +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) +{ + return GPU_shader_create_ex(vertexcode, + fragcode, + geocode, + libcode, + defines, + input, + output, + number, + GPU_SHADER_FLAGS_NONE); +} + +GPUShader *GPU_shader_create_ex(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + int input, + int output, + int number, + const int flags) +{ +#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); + bool use_opensubdiv = false; +#endif + GLint status; + GLchar log[5000]; + GLsizei length = 0; + GPUShader *shader; + 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"); + + if (vertexcode) + shader->vertex = glCreateShader(GL_VERTEX_SHADER); + if (fragcode) + shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); + if (geocode) + shader->geometry = glCreateShader(GL_GEOMETRY_SHADER_EXT); + + shader->program = glCreateProgram(); + + if (!shader->program || + (vertexcode && !shader->vertex) || + (fragcode && !shader->fragment) || + (geocode && !shader->geometry)) + { + fprintf(stderr, "GPUShader, object creation failed.\n"); + GPU_shader_free(shader); + return NULL; + } + + gpu_shader_standard_defines(standard_defines, use_opensubdiv); + gpu_shader_standard_extensions(standard_extensions, geocode != NULL); + + if (vertexcode) { + const char *source[5]; + /* custom limit, may be too small, beware */ + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (defines) source[num_source++] = defines; + source[num_source++] = vertexcode; + + glAttachShader(shader->program, shader->vertex); + glShaderSource(shader->vertex, num_source, source, NULL); + + glCompileShader(shader->vertex); + glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (fragcode) { + const char *source[7]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + +#ifdef WITH_OPENSUBDIV + /* TODO(sergey): Move to fragment shader source code generation. */ + if (use_opensubdiv) { + source[num_source++] = + "#ifdef USE_OPENSUBDIV\n" + "in block {\n" + " VertexData v;\n" + "} inpt;\n" + "#endif\n"; + } +#endif + + if (defines) source[num_source++] = defines; + if (libcode) source[num_source++] = libcode; + source[num_source++] = fragcode; + + glAttachShader(shader->program, shader->fragment); + glShaderSource(shader->fragment, num_source, source, NULL); + + glCompileShader(shader->fragment); + glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (geocode) { + const char *source[6]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (defines) source[num_source++] = defines; + source[num_source++] = geocode; + + glAttachShader(shader->program, shader->geometry); + glShaderSource(shader->geometry, num_source, source, NULL); + + glCompileShader(shader->geometry); + glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + 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 + + glLinkProgram(shader->program); + glGetProgramiv(shader->program, GL_LINK_STATUS, &status); + if (!status) { + glGetProgramInfoLog(shader->program, sizeof(log), &length, log); + /* print attached shaders in pipeline order */ + if (vertexcode) shader_print_errors("linking", log, &vertexcode, 1); + if (geocode) shader_print_errors("linking", log, &geocode, 1); + if (libcode) shader_print_errors("linking", log, &libcode, 1); + if (fragcode) shader_print_errors("linking", log, &fragcode, 1); + + GPU_shader_free(shader); + return NULL; + } + +#ifdef WITH_OPENSUBDIV + /* TODO(sergey): Find a better place for this. */ + if (use_opensubdiv && GLEW_VERSION_4_1) { + glProgramUniform1i(shader->program, + glGetUniformLocation(shader->program, "FVarDataBuffer"), + 31); /* GL_TEXTURE31 */ + } +#endif + + return shader; +} + +void GPU_shader_bind(GPUShader *shader) +{ + GPU_ASSERT_NO_GL_ERRORS("Pre Shader Bind"); + glUseProgram(shader->program); + GPU_ASSERT_NO_GL_ERRORS("Post Shader Bind"); +} + +void GPU_shader_unbind(void) +{ + GPU_ASSERT_NO_GL_ERRORS("Pre Shader Unbind"); + glUseProgram(0); + GPU_ASSERT_NO_GL_ERRORS("Post Shader Unbind"); +} + +void GPU_shader_free(GPUShader *shader) +{ + if (shader->vertex) + glDeleteShader(shader->vertex); + if (shader->geometry) + glDeleteShader(shader->geometry); + if (shader->fragment) + glDeleteShader(shader->fragment); + if (shader->program) + glDeleteProgram(shader->program); + MEM_freeN(shader); +} + +int GPU_shader_get_uniform(GPUShader *shader, const char *name) +{ + return glGetUniformLocation(shader->program, name); +} + +void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) +{ + 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) +{ + 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) +{ + if (location == -1) + return; + + GPU_CHECK_ERRORS_AROUND(glUniform1i(location, value)); +} + +void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) +{ + 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); + } +} + +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"); + 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; +} + +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; + } + + if (retval == NULL) + printf("Unable to create a GPUShader for builtin shader: %u\n", shader); + + return retval; +} + +GPUProgram *GPU_shader_get_builtin_program(GPUBuiltinProgram program) +{ + GPUProgram *retval = NULL; + + switch (program) { + case GPU_PROGRAM_SMOKE: + if (!GG.shaders.smoke) + GG.shaders.smoke = GPU_program_shader_create(GPU_PROGRAM_TYPE_FRAGMENT, datatoc_gpu_program_smoke_frag_glsl); + retval = GG.shaders.smoke; + break; + case GPU_PROGRAM_SMOKE_COLORED: + if (!GG.shaders.smoke_colored) + GG.shaders.smoke_colored = GPU_program_shader_create(GPU_PROGRAM_TYPE_FRAGMENT, datatoc_gpu_program_smoke_color_frag_glsl); + retval = GG.shaders.smoke_colored; + break; + } + + if (retval == NULL) + printf("Unable to create a GPUProgram for builtin program: %u\n", program); + + return retval; +} + +#define MAX_DEFINES 100 + +GPUShader *GPU_shader_get_builtin_fx_shader(int effects, bool persp) +{ + int offset; + char defines[MAX_DEFINES] = ""; + /* avoid shaders out of range */ + if (effects >= MAX_FX_SHADERS) + return NULL; + + offset = 2 * effects; + + if (persp) { + offset += 1; + strcat(defines, "#define PERSP_MATRIX\n"); + } + + if (!GG.shaders.fx_shaders[offset]) { + GPUShader *shader; + + switch (effects) { + case GPU_SHADER_FX_SSAO: + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_ONE: + strcat(defines, "#define FIRST_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_TWO: + strcat(defines, "#define SECOND_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_THREE: + strcat(defines, "#define THIRD_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FOUR: + strcat(defines, "#define FOURTH_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_PASS_FIVE: + strcat(defines, "#define FIFTH_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_ONE: + strcat(defines, "#define FIRST_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + 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); + GG.shaders.fx_shaders[offset] = shader; + break; + + case GPU_SHADER_FX_DEPTH_OF_FIELD_HQ_PASS_THREE: + strcat(defines, "#define THIRD_PASS\n"); + GG.shaders.fx_shaders[offset] = 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); + break; + + case GPU_SHADER_FX_DEPTH_RESOLVE: + GG.shaders.fx_shaders[offset] = GPU_shader_create(datatoc_gpu_shader_fx_vert_glsl, datatoc_gpu_shader_fx_depth_resolve_glsl, NULL, NULL, defines, 0, 0, 0); + } + } + + 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 (GG.shaders.sep_gaussian_blur) { + GPU_shader_free(GG.shaders.sep_gaussian_blur); + GG.shaders.sep_gaussian_blur = NULL; + } + + if (GG.shaders.smoke) { + GPU_program_free(GG.shaders.smoke); + GG.shaders.smoke = NULL; + } + + if (GG.shaders.smoke_colored) { + GPU_program_free(GG.shaders.smoke_colored); + GG.shaders.smoke_colored = NULL; + } + + 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; + } + } +} + + diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c new file mode 100644 index 00000000000..7803c0d0a87 --- /dev/null +++ b/source/blender/gpu/intern/gpu_texture.c @@ -0,0 +1,762 @@ +/* + * ***** 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): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_image_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_base.h" +#include "BLI_math_vector.h" + +#include "BKE_global.h" + +#include "GPU_debug.h" +#include "GPU_draw.h" +#include "GPU_extensions.h" +#include "GPU_framebuffer.h" +#include "GPU_glew.h" +#include "GPU_texture.h" + +static struct GPUTextureGlobal { + GPUTexture *invalid_tex_1D; /* texture used in place of invalid textures (not loaded correctly, missing) */ + GPUTexture *invalid_tex_2D; + GPUTexture *invalid_tex_3D; +} GG = {NULL, NULL, NULL}; + +/* GPUTexture */ + +struct GPUTexture { + int w, h; /* width/height */ + int number; /* number for multitexture binding */ + int refcount; /* reference count */ + GLenum target; /* GL_TEXTURE_* */ + GLenum target_base; /* same as target, (but no multisample) */ + 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? */ +}; + +static unsigned char *GPU_texture_convert_pixels(int length, const float *fpixels) +{ + unsigned char *pixels, *p; + const float *fp = fpixels; + const int len = 4 * length; + int a; + + p = pixels = MEM_callocN(sizeof(unsigned char) * len, "GPUTexturePixels"); + + for (a = 0; a < len; a++, p++, fp++) + *p = FTOCHAR((*fp)); + + return pixels; +} + +static void GPU_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h) +{ + void *pixels = MEM_callocN(sizeof(char) * 4 * w * h, "GPUTextureEmptyPixels"); + + 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); + + MEM_freeN(pixels); +} + +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]) +{ + GPUTexture *tex; + GLenum type, format, internalformat; + void *pixels = NULL; + + if (samples) { + CLAMP_MAX(samples, GPU_max_color_texture_samples()); + } + + tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + tex->w = w; + tex->h = h; + 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; + + 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()); + } + 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; + } + } + + if (fpixels && hdr_type == GPU_HDR_NONE) { + type = GL_UNSIGNED_BYTE; + pixels = GPU_texture_convert_pixels(w*h, fpixels); + } + } + + if (tex->target == GL_TEXTURE_1D) { + glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL); + + if (fpixels) { + glTexSubImage1D(tex->target, 0, 0, w, format, type, + pixels ? pixels : fpixels); + + if (tex->w > w) + GPU_glTexSubImageEmpty(tex->target, format, w, 0, + tex->w-w, 1); + } + } + else { + if (samples) { + glTexImage2DMultisample(tex->target, samples, internalformat, tex->w, tex->h, true); + } + 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); + } + } + + if (pixels) + MEM_freeN(pixels); + + if (depth) { + 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); + } + 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); + 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); + + return tex; +} + + +GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels) +{ + GPUTexture *tex; + GLenum type, format, internalformat; + void *pixels = NULL; + int r_width; + bool rescale = false; + + if (!GLEW_VERSION_1_2) + return NULL; + + tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + tex->w = w; + tex->h = h; + tex->depth = depth; + tex->number = -1; + tex->refcount = 1; + tex->target = GL_TEXTURE_3D; + tex->target_base = GL_TEXTURE_3D; + + glGenTextures(1, &tex->bindcode); + + if (!tex->bindcode) { + fprintf(stderr, "GPUTexture: texture create failed: %d\n", + (int)glGetError()); + 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; + } + else { + format = GL_RED; + internalformat = GL_INTENSITY8; + } + + /* 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); + 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); + } + + /* 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"); + + /* hardcore stuff, 3D texture rescaling - warning, this is gonna hurt your performance a lot, but we need it + * for gooseberry */ + if (rescale && fpixels) { + unsigned int i, j, k; + 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!"); + + for (k = 0; k < tex->depth; k++) { + for (j = 0; j < tex->h; j++) { + for (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); + + 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"); + } + } + + + 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); + + GPU_texture_unbind(tex); + + return tex; +} + +GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, bool is_data, double time, int mipmap) +{ + GPUTexture *tex; + GLint w, h, border, bindcode; + + GPU_update_image_time(ima, time); + /* this binds a texture, so that's why to restore it to 0 */ + bindcode = GPU_verify_image(ima, iuser, 0, 0, mipmap, is_data); + + if (ima->gputexture) { + ima->gputexture->bindcode = bindcode; + glBindTexture(GL_TEXTURE_2D, 0); + return ima->gputexture; + } + + tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + tex->bindcode = bindcode; + tex->number = -1; + tex->refcount = 1; + tex->target = GL_TEXTURE_2D; + tex->target_base = GL_TEXTURE_2D; + tex->fromblender = 1; + + ima->gputexture= tex; + + if (!glIsTexture(tex->bindcode)) { + GPU_ASSERT_NO_GL_ERRORS("Blender Texture Not Loaded"); + } + else { + glBindTexture(GL_TEXTURE_2D, tex->bindcode); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER, &border); + + tex->w = w - border; + tex->h = h - border; + } + + glBindTexture(GL_TEXTURE_2D, 0); + + return tex; +} + +GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) +{ + GPUTexture *tex = prv->gputexture[0]; + GLint w, h; + GLuint bindcode = 0; + + if (tex) + bindcode = tex->bindcode; + + /* this binds a texture, so that's why we restore it to 0 */ + if (bindcode == 0) { + GPU_create_gl_tex(&bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], mipmap, 0, NULL); + } + if (tex) { + tex->bindcode = bindcode; + glBindTexture(GL_TEXTURE_2D, 0); + return tex; + } + + tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + tex->bindcode = bindcode; + tex->number = -1; + tex->refcount = 1; + tex->target = GL_TEXTURE_2D; + tex->target_base = GL_TEXTURE_2D; + + prv->gputexture[0] = tex; + + if (!glIsTexture(tex->bindcode)) { + GPU_ASSERT_NO_GL_ERRORS("Blender Texture Not Loaded"); + } + else { + glBindTexture(GL_TEXTURE_2D, tex->bindcode); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + + tex->w = w; + tex->h = h; + } + + glBindTexture(GL_TEXTURE_2D, 0); + + return tex; + +} + +GPUTexture *GPU_texture_create_1D(int w, const float *fpixels, 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; +} + +GPUTexture *GPU_texture_create_2D(int w, int h, const float *fpixels, GPUHDRType hdr, 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; +} +GPUTexture *GPU_texture_create_2D_multisample(int w, int h, const float *fpixels, GPUHDRType hdr, 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; +} + +GPUTexture *GPU_texture_create_depth(int w, int h, 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; +} +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; +} + +/** + * 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 *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); + } + + return tex; +} + +GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256]) +{ + GPUTexture *tex = GPU_texture_create_nD(w, h, 2, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out); + + 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); + + GPU_texture_unbind(tex); + } + + return tex; +} + +GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256]) +{ + GPUTexture *tex = GPU_texture_create_nD(w, 0, 1, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out); + + 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); + + GPU_texture_unbind(tex); + } + + return tex; +} + +void GPU_invalid_tex_init(void) +{ + 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); +} + +void GPU_invalid_tex_bind(int mode) +{ + switch (mode) { + case GL_TEXTURE_1D: + glBindTexture(GL_TEXTURE_1D, GG.invalid_tex_1D->bindcode); + break; + case GL_TEXTURE_2D: + glBindTexture(GL_TEXTURE_2D, GG.invalid_tex_2D->bindcode); + break; + case GL_TEXTURE_3D: + glBindTexture(GL_TEXTURE_3D, GG.invalid_tex_3D->bindcode); + break; + } +} + +void GPU_invalid_tex_free(void) +{ + if (GG.invalid_tex_1D) + GPU_texture_free(GG.invalid_tex_1D); + if (GG.invalid_tex_2D) + GPU_texture_free(GG.invalid_tex_2D); + if (GG.invalid_tex_3D) + GPU_texture_free(GG.invalid_tex_3D); +} + + +void GPU_texture_bind(GPUTexture *tex, int number) +{ + GLenum arbnumber; + + 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"); + } + } + + if (number < 0) + return; + + GPU_ASSERT_NO_GL_ERRORS("Pre Texture Bind"); + + arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number); + if (number != 0) glActiveTexture(arbnumber); + if (tex->bindcode != 0) { + glBindTexture(tex->target, tex->bindcode); + } + else + GPU_invalid_tex_bind(tex->target); + glEnable(tex->target); + if (number != 0) glActiveTexture(GL_TEXTURE0); + + tex->number = number; + + GPU_ASSERT_NO_GL_ERRORS("Post Texture Bind"); +} + +void GPU_texture_unbind(GPUTexture *tex) +{ + GLenum arbnumber; + + 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"); + + arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); + if (tex->number != 0) glActiveTexture(arbnumber); + glBindTexture(tex->target, 0); + glDisable(tex->target_base); + if (tex->number != 0) glActiveTexture(GL_TEXTURE0); + + tex->number = -1; + + GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); +} + +int GPU_texture_bound_number(GPUTexture *tex) +{ + return tex->number; +} + +void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter) +{ + GLenum arbnumber; + + 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"); + + arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); + if (tex->number != 0) glActiveTexture(arbnumber); + + 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); + } + + 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); + + GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); +} + +void GPU_texture_free(GPUTexture *tex) +{ + tex->refcount--; + + if (tex->refcount < 0) + 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); + + MEM_freeN(tex); + } +} + +void GPU_texture_ref(GPUTexture *tex) +{ + tex->refcount++; +} + +int GPU_texture_target(const GPUTexture *tex) +{ + return tex->target; +} + +int GPU_texture_width(const GPUTexture *tex) +{ + return tex->w; +} + +int GPU_texture_height(const GPUTexture *tex) +{ + return tex->h; +} + +int GPU_texture_depth(const GPUTexture *tex) +{ + return tex->depth; +} + +int GPU_texture_opengl_bindcode(const GPUTexture *tex) +{ + return tex->bindcode; +} + +GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex) +{ + return tex->fb; +} + +int GPU_texture_framebuffer_attachment(GPUTexture *tex) +{ + return tex->fb_attachment; +} + +void GPU_texture_framebuffer_set(GPUTexture *tex, GPUFrameBuffer *fb, int attachment) +{ + tex->fb = fb; + tex->fb_attachment = attachment; +} + diff --git a/source/blender/python/intern/gpu_offscreen.c b/source/blender/python/intern/gpu_offscreen.c index ed31ac052c6..c4863b2a92f 100644 --- a/source/blender/python/intern/gpu_offscreen.c +++ b/source/blender/python/intern/gpu_offscreen.c @@ -37,8 +37,8 @@ #include "ED_screen.h" -#include "GPU_extensions.h" #include "GPU_compositing.h" +#include "GPU_framebuffer.h" #include "../mathutils/mathutils.h" diff --git a/source/gameengine/Ketsji/BL_BlenderShader.cpp b/source/gameengine/Ketsji/BL_BlenderShader.cpp index b5151615f3b..0f71f8d3bac 100644 --- a/source/gameengine/Ketsji/BL_BlenderShader.cpp +++ b/source/gameengine/Ketsji/BL_BlenderShader.cpp @@ -35,8 +35,8 @@ #include "BL_BlenderShader.h" #include "BL_Material.h" -#include "GPU_extensions.h" #include "GPU_material.h" +#include "GPU_shader.h" #include "RAS_BucketManager.h" #include "RAS_MeshObject.h" diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp index da5229f1441..bbdf17b5bc4 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp @@ -51,8 +51,9 @@ #include "RAS_StorageVBO.h" #include "GPU_draw.h" -#include "GPU_material.h" #include "GPU_extensions.h" +#include "GPU_material.h" +#include "GPU_shader.h" extern "C"{ #include "BLF_api.h" -- cgit v1.2.3