/* * ***** 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 blender/gpu/intern/gpu_extensions.c * \ingroup gpu * * Wrap OpenGL features such as textures, shaders and GLSL * with checks for drivers and GPU support. */ #include "DNA_image_types.h" #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_glew.h" #include "GPU_debug.h" #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_compositing.h" #include "GPU_simple_shader.h" #include "intern/gpu_private.h" #include #include #include #ifdef WIN32 # 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 */ /* extensions used: * - texture border clamp: 1.3 core * - fragment shader: 2.0 core * - framebuffer object: ext specification * - multitexture 1.3 core * - arb non power of two: 2.0 core * - pixel buffer objects? 2.1 core * - arb draw buffers? 2.0 core */ /* 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; int glslsupport; int extdisabled; int colordepth; int npotdisabled; /* ATI 3xx-5xx (and more) chipsets support NPoT partially (== not enough) */ int dlistsdisabled; /* Legacy ATI driver does not support display lists well */ 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) { return (GG.device & device) && (GG.os & os) && (GG.driver & driver); } /* GPU Extensions */ void GPU_extensions_disable(void) { GG.extdisabled = 1; } int GPU_max_texture_size(void) { return GG.maxtexsize; } void GPU_get_dfdy_factors(float fac[2]) { copy_v2_v2(fac, GG.dfdyfactors); } void gpu_extensions_init(void) { GLint r, g, b; const char *vendor, *renderer, *version; /* glewIsSupported("GL_VERSION_2_0") */ if (GLEW_ARB_multitexture) glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &GG.maxtextures); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &GG.maxtexsize); GG.glslsupport = 1; if (!GLEW_ARB_multitexture) GG.glslsupport = 0; if (!GLEW_ARB_vertex_shader) GG.glslsupport = 0; if (!GLEW_ARB_fragment_shader) GG.glslsupport = 0; glGetIntegerv(GL_RED_BITS, &r); glGetIntegerv(GL_GREEN_BITS, &g); glGetIntegerv(GL_BLUE_BITS, &b); GG.colordepth = r + g + b; /* assumes same depth for RGB */ vendor = (const char *)glGetString(GL_VENDOR); renderer = (const char *)glGetString(GL_RENDERER); version = (const char *)glGetString(GL_VERSION); if (strstr(vendor, "ATI")) { GG.device = GPU_DEVICE_ATI; GG.driver = GPU_DRIVER_OFFICIAL; } else if (strstr(vendor, "NVIDIA")) { GG.device = GPU_DEVICE_NVIDIA; GG.driver = GPU_DRIVER_OFFICIAL; } else if (strstr(vendor, "Intel") || /* src/mesa/drivers/dri/intel/intel_context.c */ strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) { GG.device = GPU_DEVICE_INTEL; GG.driver = GPU_DRIVER_OFFICIAL; } else if (strstr(renderer, "Mesa DRI R") || (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI "))) { GG.device = GPU_DEVICE_ATI; GG.driver = GPU_DRIVER_OPENSOURCE; } else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) { GG.device = GPU_DEVICE_NVIDIA; GG.driver = GPU_DRIVER_OPENSOURCE; } else if (strstr(vendor, "Mesa")) { GG.device = GPU_DEVICE_SOFTWARE; GG.driver = GPU_DRIVER_SOFTWARE; } else if (strstr(vendor, "Microsoft")) { GG.device = GPU_DEVICE_SOFTWARE; GG.driver = GPU_DRIVER_SOFTWARE; } else if (strstr(renderer, "Apple Software Renderer")) { GG.device = GPU_DEVICE_SOFTWARE; GG.driver = GPU_DRIVER_SOFTWARE; } else { GG.device = GPU_DEVICE_ANY; GG.driver = GPU_DRIVER_ANY; } if (GG.device == GPU_DEVICE_ATI) { /* ATI 9500 to X2300 cards support NPoT textures poorly * Incomplete list http://dri.freedesktop.org/wiki/ATIRadeon * New IDs from MESA's src/gallium/drivers/r300/r300_screen.c */ /* This list is close enough to those using the legacy driver which * has a bug with display lists and glVertexAttrib */ if (strstr(renderer, "R3") || strstr(renderer, "RV3") || strstr(renderer, "R4") || strstr(renderer, "RV4") || strstr(renderer, "RS4") || strstr(renderer, "RC4") || strstr(renderer, "R5") || strstr(renderer, "RV5") || strstr(renderer, "RS600") || strstr(renderer, "RS690") || strstr(renderer, "RS740") || strstr(renderer, "X1") || strstr(renderer, "X2") || strstr(renderer, "Radeon 9") || strstr(renderer, "RADEON 9")) { GG.npotdisabled = 1; GG.dlistsdisabled = 1; } } /* make sure double side isn't used by default and only getting enabled in places where it's * really needed to prevent different unexpected behaviors like with intel gme965 card (sergey) */ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); #ifdef _WIN32 GG.os = GPU_OS_WIN; #elif defined(__APPLE__) GG.os = GPU_OS_MAC; #else GG.os = GPU_OS_UNIX; #endif /* df/dy calculation factors, those are dependent on driver */ if ((strstr(vendor, "ATI") && strstr(version, "3.3.10750"))) { GG.dfdyfactors[0] = 1.0; GG.dfdyfactors[1] = -1.0; } else if (GG.device == GPU_DEVICE_INTEL && GG.os == GPU_OS_WIN && (strstr(version, "4.0.0 - Build 10.18.10.3308") || strstr(version, "4.0.0 - Build 9.18.10.3186") || strstr(version, "4.0.0 - Build 9.18.10.3165") || strstr(version, "3.1.0 - Build 9.17.10.3347") || strstr(version, "3.1.0 - Build 9.17.10.4101"))) { GG.dfdyfactors[0] = -1.0; GG.dfdyfactors[1] = 1.0; } else { GG.dfdyfactors[0] = 1.0; GG.dfdyfactors[1] = 1.0; } GPU_invalid_tex_init(); GPU_simple_shaders_init(); } void gpu_extensions_exit(void) { GPU_simple_shaders_exit(); GPU_invalid_tex_free(); } bool GPU_glsl_support(void) { return !GG.extdisabled && GG.glslsupport; } bool GPU_non_power_of_two_support(void) { if (GG.npotdisabled) return false; return GLEW_ARB_texture_non_power_of_two; } bool GPU_vertex_buffer_support(void) { return GLEW_ARB_vertex_buffer_object || GLEW_VERSION_1_5; } bool GPU_display_list_support(void) { return !GG.dlistsdisabled; } bool GPU_bicubic_bump_support(void) { return GLEW_ARB_texture_query_lod && GLEW_VERSION_3_0; } bool GPU_geometry_shader_support(void) { return GLEW_EXT_geometry_shader4 || GLEW_VERSION_3_2; } bool GPU_instanced_drawing_support(void) { return GLEW_ARB_draw_instanced; } 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_* */ 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, char err_out[256]) { GPUTexture *tex; GLenum type, format, internalformat; void *pixels = NULL; if (depth && !GLEW_ARB_depth_texture) return NULL; 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: 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_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; case GPU_HDR_HALF_FLOAT: internalformat = GL_RGBA16F; break; case GPU_HDR_FULL_FLOAT: internalformat = GL_RGBA32F; break; default: break; } } else if (components == 2) { 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 { 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, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(tex->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(tex->target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); glTexParameteri(tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY); } else { glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (tex->target != GL_TEXTURE_1D) { glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else glTexParameteri(tex->target, 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; glGenTextures(1, &tex->bindcode); if (!tex->bindcode) { fprintf(stderr, "GPUTexture: texture create failed: %d\n", (int)glGetError()); GPU_texture_free(tex); return NULL; } if (!GPU_non_power_of_two_support()) { tex->w = power_of_2_max_i(tex->w); tex->h = power_of_2_max_i(tex->h); tex->depth = power_of_2_max_i(tex->depth); } 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_RGBA; } else { format = GL_RED; internalformat = GL_INTENSITY; } /* 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) { if (!GPU_non_power_of_two_support() && (w != tex->w || h != tex->h || depth != tex->depth)) { /* clear first to avoid unitialized pixels */ float *zero= MEM_callocN(sizeof(float)*tex->w*tex->h*tex->depth, "zero"); glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL); glTexSubImage3D(tex->target, 0, 0, 0, 0, tex->w, tex->h, tex->depth, GL_INTENSITY, GL_FLOAT, zero); glTexSubImage3D(tex->target, 0, 0, 0, 0, w, h, depth, format, type, fpixels); MEM_freeN(zero); } else { 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, lastbindcode, bindcode; glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastbindcode); GPU_update_image_time(ima, time); /* this binds a texture, so that's why to restore it with lastbindcode */ bindcode = GPU_verify_image(ima, iuser, 0, 0, mipmap, is_data); if (ima->gputexture) { ima->gputexture->bindcode = bindcode; glBindTexture(GL_TEXTURE_2D, lastbindcode); return ima->gputexture; } tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->bindcode = bindcode; tex->number = -1; tex->refcount = 1; tex->target = 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, lastbindcode); return tex; } GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) { GPUTexture *tex = prv->gputexture[0]; GLint w, h, lastbindcode; GLuint bindcode = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastbindcode); if (tex) bindcode = tex->bindcode; /* this binds a texture, so that's why to restore it */ 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, lastbindcode); return tex; } tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->bindcode = bindcode; tex->number = -1; tex->refcount = 1; tex->target = 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, lastbindcode); 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, 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, 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, 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, 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, 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, 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 >= GG.maxtextures) { fprintf(stderr, "Not enough texture slots."); 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_ARB + number); if (number != 0) glActiveTextureARB(arbnumber); if (tex->bindcode != 0) { glBindTexture(tex->target, tex->bindcode); } else GPU_invalid_tex_bind(tex->target); glEnable(tex->target); if (number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); tex->number = number; GPU_ASSERT_NO_GL_ERRORS("Post Texture Bind"); } void GPU_texture_unbind(GPUTexture *tex) { GLenum arbnumber; if (tex->number >= GG.maxtextures) { fprintf(stderr, "Not enough texture slots."); return; } if (tex->number == -1) return; GPU_ASSERT_NO_GL_ERRORS("Pre Texture Unbind"); arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); if (tex->number != 0) glActiveTextureARB(arbnumber); glBindTexture(tex->target, 0); glDisable(tex->target); if (tex->number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); tex->number = -1; GPU_ASSERT_NO_GL_ERRORS("Post Texture Unbind"); } void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter) { GLenum arbnumber; if (tex->number >= GG.maxtextures) { fprintf(stderr, "Not enough texture slots."); return; } if (tex->number == -1) return; GPU_ASSERT_NO_GL_ERRORS("Pre Texture Unbind"); arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); if (tex->number != 0) glActiveTextureARB(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) glActiveTextureARB(GL_TEXTURE0_ARB); 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_opengl_width(const GPUTexture *tex) { return tex->w; } int GPU_texture_opengl_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_EXT_framebuffer_object) 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 in blender 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!"); } } 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!"); 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); } /* 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!"); 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 / GPU_texture_opengl_width(blurtex), 0.0f}; const float scalev[2] = {0.0f, 1.0f / GPU_texture_opengl_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); /* 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_opengl_width(blurtex), GPU_texture_opengl_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); GG.currentfb = fb->object; glViewport(0, 0, GPU_texture_opengl_width(tex), GPU_texture_opengl_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, char err_out[256]) { GPUOffScreen *ofs; ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); ofs->fb = GPU_framebuffer_create(); if (!ofs->fb) { GPU_offscreen_free(ofs); return NULL; } ofs->depth = GPU_texture_create_depth(width, height, 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(width, height, NULL, GPU_HDR_NONE, 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) { glReadPixels(0, 0, ofs->color->w, ofs->color->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; } /* GPUShader */ struct GPUShader { GLhandleARB object; /* handle for full shader */ GLhandleARB vertex; /* handle for vertex shader */ GLhandleARB fragment; /* handle for fragment shader */ GLhandleARB geometry; /* handle for geometry shader */ GLhandleARB lib; /* handle for libment 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(bool use_opensubdiv) { #ifdef WITH_OPENSUBDIV if (use_opensubdiv) { return "#version 130\n"; } #else UNUSED_VARS(use_opensubdiv); #endif /* turn on glsl 1.30 for bicubic bump mapping and ATI clipping support */ if (GLEW_VERSION_3_0 && (GPU_bicubic_bump_support() || GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY))) { return "#version 130\n"; } return ""; } static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH], bool use_opensubdiv) { #ifdef WITH_OPENSUBDIV if (use_opensubdiv) { strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n" "#extension GL_ARB_gpu_shader5 : enable\n" "#extension GL_ARB_explicit_attrib_location : require\n"); } else if (GPU_bicubic_bump_support()) strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); #else /* need this extension for high quality bump mapping */ if (GPU_bicubic_bump_support()) strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); (void) use_opensubdiv; #endif if (GPU_geometry_shader_support()) strcat(defines, "#extension GL_EXT_geometry_shader4: enable\n"); if (GPU_instanced_drawing_support()) { strcat(defines, "#extension GL_EXT_gpu_shader4: enable\n"); strcat(defines, "#extension GL_ARB_draw_instanced: enable\n"); } } static void gpu_shader_standard_defines(bool use_opensubdiv, char defines[MAX_DEFINE_LENGTH]) { /* 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) 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) { 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) { #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 evry much * liely any of geometry code will want to use GLSL 1.5. */ bool use_opensubdiv = geocode != NULL; #else bool use_opensubdiv = false; #endif GLint status; GLcharARB log[5000]; GLsizei length = 0; GPUShader *shader; char standard_defines[MAX_DEFINE_LENGTH] = ""; char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader || (geocode && !GPU_geometry_shader_support())) return NULL; shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); if (vertexcode) shader->vertex = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); if (fragcode) shader->fragment = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); if (geocode) shader->geometry = glCreateShaderObjectARB(GL_GEOMETRY_SHADER_EXT); shader->object = glCreateProgramObjectARB(); if (!shader->object || (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(use_opensubdiv, standard_defines); gpu_shader_standard_extensions(standard_extensions, use_opensubdiv); if (vertexcode) { const char *source[5]; /* custom limit, may be too small, beware */ int num_source = 0; source[num_source++] = gpu_shader_version(use_opensubdiv); source[num_source++] = standard_extensions; source[num_source++] = standard_defines; if (defines) source[num_source++] = defines; source[num_source++] = vertexcode; glAttachObjectARB(shader->object, shader->vertex); glShaderSourceARB(shader->vertex, num_source, source, NULL); glCompileShaderARB(shader->vertex); glGetObjectParameterivARB(shader->vertex, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (!status) { glGetInfoLogARB(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(use_opensubdiv); 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; glAttachObjectARB(shader->object, shader->fragment); glShaderSourceARB(shader->fragment, num_source, source, NULL); glCompileShaderARB(shader->fragment); glGetObjectParameterivARB(shader->fragment, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (!status) { glGetInfoLogARB(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(use_opensubdiv); source[num_source++] = standard_extensions; source[num_source++] = standard_defines; if (defines) source[num_source++] = defines; source[num_source++] = geocode; glAttachObjectARB(shader->object, shader->geometry); glShaderSourceARB(shader->geometry, num_source, source, NULL); glCompileShaderARB(shader->geometry); glGetObjectParameterivARB(shader->geometry, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (!status) { glGetInfoLogARB(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); } } #if 0 if (lib && lib->lib) glAttachObjectARB(shader->object, lib->lib); #endif #ifdef WITH_OPENSUBDIV if (use_opensubdiv) { glBindAttribLocation(shader->object, 0, "position"); glBindAttribLocation(shader->object, 1, "normal"); GPU_shader_geometry_stage_primitive_io(shader, GL_LINES_ADJACENCY_EXT, GL_TRIANGLE_STRIP, 4); } #endif glLinkProgramARB(shader->object); glGetObjectParameterivARB(shader->object, GL_OBJECT_LINK_STATUS_ARB, &status); if (!status) { glGetInfoLogARB(shader->object, sizeof(log), &length, log); if (fragcode) shader_print_errors("linking", log, &fragcode, 1); else if (vertexcode) shader_print_errors("linking", log, &vertexcode, 1); else if (libcode) shader_print_errors("linking", log, &libcode, 1); else if (geocode) shader_print_errors("linking", log, &geocode, 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->object, glGetUniformLocation(shader->object, "FVarDataBuffer"), 31); /* GL_TEXTURE31 */ } #endif return shader; } #if 0 GPUShader *GPU_shader_create_lib(const char *code) { GLint status; GLcharARB log[5000]; GLsizei length = 0; GPUShader *shader; if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader) return NULL; shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); shader->lib = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); if (!shader->lib) { fprintf(stderr, "GPUShader, object creation failed.\n"); GPU_shader_free(shader); return NULL; } glShaderSourceARB(shader->lib, 1, (const char**)&code, NULL); glCompileShaderARB(shader->lib); glGetObjectParameterivARB(shader->lib, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (!status) { glGetInfoLogARB(shader->lib, sizeof(log), &length, log); shader_print_errors("compile", log, code); GPU_shader_free(shader); return NULL; } return shader; } #endif void GPU_shader_bind(GPUShader *shader) { GPU_ASSERT_NO_GL_ERRORS("Pre Shader Bind"); glUseProgramObjectARB(shader->object); GPU_ASSERT_NO_GL_ERRORS("Post Shader Bind"); } void GPU_shader_unbind(void) { GPU_ASSERT_NO_GL_ERRORS("Pre Shader Unbind"); glUseProgramObjectARB(0); GPU_ASSERT_NO_GL_ERRORS("Post Shader Unbind"); } void GPU_shader_free(GPUShader *shader) { if (shader->lib) glDeleteObjectARB(shader->lib); if (shader->vertex) glDeleteObjectARB(shader->vertex); if (shader->fragment) glDeleteObjectARB(shader->fragment); if (shader->object) glDeleteObjectARB(shader->object); MEM_freeN(shader); } int GPU_shader_get_uniform(GPUShader *shader, const char *name) { return glGetUniformLocationARB(shader->object, 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) glUniform1fvARB(location, arraysize, value); else if (length == 2) glUniform2fvARB(location, arraysize, value); else if (length == 3) glUniform3fvARB(location, arraysize, value); else if (length == 4) glUniform4fvARB(location, arraysize, value); else if (length == 9) glUniformMatrix3fvARB(location, arraysize, 0, value); else if (length == 16) glUniformMatrix4fvARB(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) glUniform1ivARB(location, arraysize, value); else if (length == 2) glUniform2ivARB(location, arraysize, value); else if (length == 3) glUniform3ivARB(location, arraysize, value); else if (length == 4) glUniform4ivARB(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(glUniform1iARB(location, value)); } void GPU_shader_geometry_stage_primitive_io(GPUShader *shader, int input, int output, int number) { glProgramParameteriEXT(shader->object, GL_GEOMETRY_INPUT_TYPE_EXT, input); glProgramParameteriEXT(shader->object, GL_GEOMETRY_OUTPUT_TYPE_EXT, output); glProgramParameteriEXT(shader->object, 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."); return; } if (tex->number == -1) return; if (location == -1) return; GPU_ASSERT_NO_GL_ERRORS("Pre Uniform Texture"); arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); if (tex->number != 0) glActiveTextureARB(arbnumber); if (tex->bindcode != 0) glBindTexture(tex->target, tex->bindcode); else GPU_invalid_tex_bind(tex->target); glUniform1iARB(location, tex->number); glEnable(tex->target); if (tex->number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); 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 = glGetAttribLocationARB(shader->object, 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; } else { *totalmem = 0; *freemem = 0; } } #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]) glDeleteBuffersARB(pb->numbuffers, pb->bindcode); MEM_freeN(pb); } GPUPixelBuffer *gpu_pixelbuffer_create(int x, int y, int halffloat, int numbuffers) { GPUPixelBuffer *pb; if (!GLEW_ARB_multitexture || !GLEW_EXT_pixel_buffer_object) return NULL; pb = MEM_callocN(sizeof(GPUPixelBuffer), "GPUPBO"); pb->datasize = x * y * 4 * (halffloat ? 16 : 8); pb->numbuffers = numbuffers; pb->halffloat = halffloat; glGenBuffersARB(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_EXT, tex->bindcode); for (i = 0; i < pb->numbuffers; i++) { glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb->bindcode[pb->current]); glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb->datasize, NULL, GL_STREAM_DRAW_ARB); pixels = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); # if 0 memcpy(pixels, _oImage.data(), pb->datasize); # endif if (!glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT)) { fprintf(stderr, "Could not unmap OpenGL PBO\n"); break; } } glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0); } static int pixelbuffer_map_into_gpu(GLuint bindcode) { void *pixels; glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, bindcode); pixels = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); /* do stuff in pixels */ if (!glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT)) { 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_EXT, tex->bindcode); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, bindcode); glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, tex->w, tex->h, GL_RGBA, type, NULL); glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 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 */