|
Blender
V2.59
|
00001 /* 00002 * $Id: gpu_extensions.c 36276 2011-04-21 15:53:30Z campbellbarton $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. The Blender 00010 * Foundation also sells licenses for use in proprietary software under 00011 * the Blender License. See http://www.blender.org/BL/ for information 00012 * about this. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software Foundation, 00021 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00022 * 00023 * The Original Code is Copyright (C) 2005 Blender Foundation. 00024 * All rights reserved. 00025 * 00026 * The Original Code is: all of this file. 00027 * 00028 * Contributor(s): Brecht Van Lommel. 00029 * 00030 * ***** END GPL LICENSE BLOCK ***** 00031 */ 00032 00038 #include "GL/glew.h" 00039 00040 #include "DNA_image_types.h" 00041 00042 #include "MEM_guardedalloc.h" 00043 00044 #include "BKE_global.h" 00045 00046 00047 #include "BLI_blenlib.h" 00048 #include "BLI_utildefines.h" 00049 00050 #include "GPU_draw.h" 00051 #include "GPU_extensions.h" 00052 00053 #include <stdlib.h> 00054 #include <stdio.h> 00055 #include <string.h> 00056 00057 #include "BLI_winstuff.h" 00058 00059 /* Extensions support */ 00060 00061 /* extensions used: 00062 - texture border clamp: 1.3 core 00063 - fragement shader: 2.0 core 00064 - framebuffer object: ext specification 00065 - multitexture 1.3 core 00066 - arb non power of two: 2.0 core 00067 - pixel buffer objects? 2.1 core 00068 - arb draw buffers? 2.0 core 00069 */ 00070 00071 static struct GPUGlobal { 00072 GLint maxtextures; 00073 GLuint currentfb; 00074 int glslsupport; 00075 int extdisabled; 00076 int colordepth; 00077 int npotdisabled; /* ATI 3xx-5xx (and more) chipsets support NPoT partially (== not enough) */ 00078 GPUDeviceType device; 00079 GPUOSType os; 00080 GPUDriverType driver; 00081 } GG = {1, 0, 0, 0, 0}; 00082 00083 /* GPU Types */ 00084 00085 int GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver) 00086 { 00087 return (GG.device & device) && (GG.os & os) && (GG.driver & driver); 00088 } 00089 00090 /* GPU Extensions */ 00091 00092 void GPU_extensions_disable(void) 00093 { 00094 GG.extdisabled = 1; 00095 } 00096 00097 void GPU_extensions_init(void) 00098 { 00099 GLint r, g, b; 00100 const char *vendor, *renderer; 00101 00102 /* can't avoid calling this multiple times, see wm_window_add_ghostwindow */ 00103 static char init= 0; 00104 if(init) return; 00105 init= 1; 00106 00107 glewInit(); 00108 00109 /* glewIsSupported("GL_VERSION_2_0") */ 00110 00111 if (GLEW_ARB_multitexture) 00112 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &GG.maxtextures); 00113 00114 GG.glslsupport = 1; 00115 if (!GLEW_ARB_multitexture) GG.glslsupport = 0; 00116 if (!GLEW_ARB_vertex_shader) GG.glslsupport = 0; 00117 if (!GLEW_ARB_fragment_shader) GG.glslsupport = 0; 00118 00119 glGetIntegerv(GL_RED_BITS, &r); 00120 glGetIntegerv(GL_GREEN_BITS, &g); 00121 glGetIntegerv(GL_BLUE_BITS, &b); 00122 GG.colordepth = r+g+b; /* assumes same depth for RGB */ 00123 00124 vendor = (const char*)glGetString(GL_VENDOR); 00125 renderer = (const char*)glGetString(GL_RENDERER); 00126 00127 if(strstr(vendor, "ATI")) { 00128 GG.device = GPU_DEVICE_ATI; 00129 GG.driver = GPU_DRIVER_OFFICIAL; 00130 00131 /* ATI X1xxx cards (R500 chipset) lack full support for npot textures 00132 * although they report the GLEW_ARB_texture_non_power_of_two extension. 00133 */ 00134 if(strstr(renderer, "X1")) 00135 GG.npotdisabled = 1; 00136 } 00137 else if(strstr(vendor, "NVIDIA")) { 00138 GG.device = GPU_DEVICE_NVIDIA; 00139 GG.driver = GPU_DRIVER_OFFICIAL; 00140 } 00141 else if(strstr(vendor, "Intel") || 00142 /* src/mesa/drivers/dri/intel/intel_context.c */ 00143 strstr(renderer, "Mesa DRI Intel") || 00144 strstr(renderer, "Mesa DRI Mobile Intel")) { 00145 GG.device = GPU_DEVICE_INTEL; 00146 GG.driver = GPU_DRIVER_OFFICIAL; 00147 } 00148 else if(strstr(renderer, "Mesa DRI R") || (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI "))) { 00149 GG.device = GPU_DEVICE_ATI; 00150 GG.driver = GPU_DRIVER_OPENSOURCE; 00151 /* ATI 9500 to X2300 cards support NPoT textures poorly 00152 * Incomplete list http://dri.freedesktop.org/wiki/ATIRadeon 00153 * New IDs from MESA's src/gallium/drivers/r300/r300_screen.c 00154 */ 00155 if(strstr(renderer, "R3") || strstr(renderer, "RV3") || 00156 strstr(renderer, "R4") || strstr(renderer, "RV4") || 00157 strstr(renderer, "RS4") || strstr(renderer, "RC4") || 00158 strstr(renderer, "R5") || strstr(renderer, "RV5") || 00159 strstr(renderer, "RS600") || strstr(renderer, "RS690") || 00160 strstr(renderer, "RS740")) 00161 GG.npotdisabled = 1; 00162 } 00163 else if(strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) { 00164 GG.device = GPU_DEVICE_NVIDIA; 00165 GG.driver = GPU_DRIVER_OPENSOURCE; 00166 } 00167 else if(strstr(vendor, "Mesa")) { 00168 GG.device = GPU_DEVICE_SOFTWARE; 00169 GG.driver = GPU_DRIVER_SOFTWARE; 00170 } 00171 else if(strstr(vendor, "Microsoft")) { 00172 GG.device = GPU_DEVICE_SOFTWARE; 00173 GG.driver = GPU_DRIVER_SOFTWARE; 00174 } 00175 else if(strstr(renderer, "Apple Software Renderer")) { 00176 GG.device = GPU_DEVICE_SOFTWARE; 00177 GG.driver = GPU_DRIVER_SOFTWARE; 00178 } 00179 else { 00180 GG.device = GPU_DEVICE_ANY; 00181 GG.driver = GPU_DRIVER_ANY; 00182 } 00183 00184 GG.os = GPU_OS_UNIX; 00185 #ifdef _WIN32 00186 GG.os = GPU_OS_WIN; 00187 #endif 00188 #ifdef __APPLE__ 00189 GG.os = GPU_OS_MAC; 00190 #endif 00191 } 00192 00193 int GPU_glsl_support(void) 00194 { 00195 return !GG.extdisabled && GG.glslsupport; 00196 } 00197 00198 int GPU_non_power_of_two_support(void) 00199 { 00200 /* Exception for buggy ATI/Apple driver in Mac OS X 10.5/10.6, 00201 * they claim to support this but can cause system freeze */ 00202 if(GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) 00203 return 0; 00204 00205 if(GG.npotdisabled) 00206 return 0; 00207 00208 return GLEW_ARB_texture_non_power_of_two; 00209 } 00210 00211 int GPU_color_depth(void) 00212 { 00213 return GG.colordepth; 00214 } 00215 00216 int GPU_print_error(const char *str) 00217 { 00218 GLenum errCode; 00219 00220 if (G.f & G_DEBUG) { 00221 if ((errCode = glGetError()) != GL_NO_ERROR) { 00222 fprintf(stderr, "%s opengl error: %s\n", str, gluErrorString(errCode)); 00223 return 1; 00224 } 00225 } 00226 00227 return 0; 00228 } 00229 00230 static void GPU_print_framebuffer_error(GLenum status, char err_out[256]) 00231 { 00232 const char *err= "unknown"; 00233 00234 switch(status) { 00235 case GL_FRAMEBUFFER_COMPLETE_EXT: 00236 break; 00237 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: 00238 err= "Incomplete attachment"; 00239 break; 00240 case GL_FRAMEBUFFER_UNSUPPORTED_EXT: 00241 err= "Unsupported framebuffer format"; 00242 break; 00243 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: 00244 err= "Missing attachment"; 00245 break; 00246 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: 00247 err= "Attached images must have same dimensions"; 00248 break; 00249 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: 00250 err= "Attached images must have same format"; 00251 break; 00252 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: 00253 err= "Missing draw buffer"; 00254 break; 00255 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: 00256 err= "Missing read buffer"; 00257 break; 00258 } 00259 00260 if(err_out) { 00261 BLI_snprintf(err_out, 256, "GPUFrameBuffer: framebuffer incomplete error %d '%s'", 00262 (int)status, err); 00263 } 00264 else { 00265 fprintf(stderr, "GPUFrameBuffer: framebuffer incomplete error %d '%s'\n", 00266 (int)status, err); 00267 } 00268 } 00269 00270 /* GPUTexture */ 00271 00272 struct GPUTexture { 00273 int w, h; /* width/height */ 00274 int number; /* number for multitexture binding */ 00275 int refcount; /* reference count */ 00276 GLenum target; /* GL_TEXTURE_* */ 00277 GLuint bindcode; /* opengl identifier for texture */ 00278 int fromblender; /* we got the texture from Blender */ 00279 00280 GPUFrameBuffer *fb; /* GPUFramebuffer this texture is attached to */ 00281 int depth; /* is a depth texture? */ 00282 }; 00283 00284 static unsigned char *GPU_texture_convert_pixels(int length, float *fpixels) 00285 { 00286 unsigned char *pixels, *p; 00287 float *fp; 00288 int a, len; 00289 00290 len = 4*length; 00291 fp = fpixels; 00292 p = pixels = MEM_callocN(sizeof(unsigned char)*len, "GPUTexturePixels"); 00293 00294 for (a=0; a<len; a++, p++, fp++) 00295 *p = FTOCHAR((*fp)); 00296 00297 return pixels; 00298 } 00299 00300 static int is_pow2(int n) 00301 { 00302 return ((n)&(n-1))==0; 00303 } 00304 00305 static int larger_pow2(int n) 00306 { 00307 if (is_pow2(n)) 00308 return n; 00309 00310 while(!is_pow2(n)) 00311 n= n&(n-1); 00312 00313 return n*2; 00314 } 00315 00316 static void GPU_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h) 00317 { 00318 void *pixels = MEM_callocN(sizeof(char)*4*w*h, "GPUTextureEmptyPixels"); 00319 00320 if (target == GL_TEXTURE_1D) 00321 glTexSubImage1D(target, 0, x, w, format, GL_UNSIGNED_BYTE, pixels); 00322 else 00323 glTexSubImage2D(target, 0, x, y, w, h, format, GL_UNSIGNED_BYTE, pixels); 00324 00325 MEM_freeN(pixels); 00326 } 00327 00328 static GPUTexture *GPU_texture_create_nD(int w, int h, int n, float *fpixels, int depth, char err_out[256]) 00329 { 00330 GPUTexture *tex; 00331 GLenum type, format, internalformat; 00332 void *pixels = NULL; 00333 00334 if(depth && !GLEW_ARB_depth_texture) 00335 return NULL; 00336 00337 tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); 00338 tex->w = w; 00339 tex->h = h; 00340 tex->number = -1; 00341 tex->refcount = 1; 00342 tex->target = (n == 1)? GL_TEXTURE_1D: GL_TEXTURE_2D; 00343 tex->depth = depth; 00344 00345 glGenTextures(1, &tex->bindcode); 00346 00347 if (!tex->bindcode) { 00348 if(err_out) { 00349 BLI_snprintf(err_out, 256, "GPUTexture: texture create failed: %d", 00350 (int)glGetError()); 00351 } 00352 else { 00353 fprintf(stderr, "GPUTexture: texture create failed: %d\n", 00354 (int)glGetError()); 00355 } 00356 GPU_texture_free(tex); 00357 return NULL; 00358 } 00359 00360 if (!GPU_non_power_of_two_support()) { 00361 tex->w = larger_pow2(tex->w); 00362 tex->h = larger_pow2(tex->h); 00363 } 00364 00365 tex->number = 0; 00366 glBindTexture(tex->target, tex->bindcode); 00367 00368 if(depth) { 00369 type = GL_UNSIGNED_BYTE; 00370 format = GL_DEPTH_COMPONENT; 00371 internalformat = GL_DEPTH_COMPONENT; 00372 } 00373 else { 00374 type = GL_UNSIGNED_BYTE; 00375 format = GL_RGBA; 00376 internalformat = GL_RGBA8; 00377 00378 if (fpixels) 00379 pixels = GPU_texture_convert_pixels(w*h, fpixels); 00380 } 00381 00382 if (tex->target == GL_TEXTURE_1D) { 00383 glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL); 00384 00385 if (fpixels) { 00386 glTexSubImage1D(tex->target, 0, 0, w, format, type, 00387 pixels? pixels: fpixels); 00388 00389 if (tex->w > w) 00390 GPU_glTexSubImageEmpty(tex->target, format, w, 0, 00391 tex->w-w, 1); 00392 } 00393 } 00394 else { 00395 glTexImage2D(tex->target, 0, internalformat, tex->w, tex->h, 0, 00396 format, type, NULL); 00397 00398 if (fpixels) { 00399 glTexSubImage2D(tex->target, 0, 0, 0, w, h, 00400 format, type, pixels? pixels: fpixels); 00401 00402 if (tex->w > w) 00403 GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w-w, tex->h); 00404 if (tex->h > h) 00405 GPU_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h-h); 00406 } 00407 } 00408 00409 if (pixels) 00410 MEM_freeN(pixels); 00411 00412 if(depth) { 00413 glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 00414 glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 00415 glTexParameteri(tex->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE); 00416 glTexParameteri(tex->target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); 00417 glTexParameteri(tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY); 00418 } 00419 else { 00420 glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 00421 glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 00422 } 00423 00424 if (tex->target != GL_TEXTURE_1D) { 00425 /* CLAMP_TO_BORDER is an OpenGL 1.3 core feature */ 00426 GLenum wrapmode = (depth)? GL_CLAMP_TO_EDGE: GL_CLAMP_TO_BORDER; 00427 glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, wrapmode); 00428 glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, wrapmode); 00429 00430 #if 0 00431 float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; 00432 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); 00433 #endif 00434 } 00435 else 00436 glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 00437 00438 return tex; 00439 } 00440 00441 00442 GPUTexture *GPU_texture_create_3D(int w, int h, int depth, float *fpixels) 00443 { 00444 GPUTexture *tex; 00445 GLenum type, format, internalformat; 00446 void *pixels = NULL; 00447 float vfBorderColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; 00448 00449 if(!GLEW_VERSION_1_2) 00450 return NULL; 00451 00452 tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); 00453 tex->w = w; 00454 tex->h = h; 00455 tex->depth = depth; 00456 tex->number = -1; 00457 tex->refcount = 1; 00458 tex->target = GL_TEXTURE_3D; 00459 00460 glGenTextures(1, &tex->bindcode); 00461 00462 if (!tex->bindcode) { 00463 fprintf(stderr, "GPUTexture: texture create failed: %d\n", 00464 (int)glGetError()); 00465 GPU_texture_free(tex); 00466 return NULL; 00467 } 00468 00469 if (!GPU_non_power_of_two_support()) { 00470 tex->w = larger_pow2(tex->w); 00471 tex->h = larger_pow2(tex->h); 00472 tex->depth = larger_pow2(tex->depth); 00473 } 00474 00475 tex->number = 0; 00476 glBindTexture(tex->target, tex->bindcode); 00477 00478 GPU_print_error("3D glBindTexture"); 00479 00480 type = GL_FLOAT; // GL_UNSIGNED_BYTE 00481 format = GL_RED; 00482 internalformat = GL_INTENSITY; 00483 00484 //if (fpixels) 00485 // pixels = GPU_texture_convert_pixels(w*h*depth, fpixels); 00486 00487 glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, 0); 00488 00489 GPU_print_error("3D glTexImage3D"); 00490 00491 if (fpixels) { 00492 if(!GPU_non_power_of_two_support() && (w != tex->w || h != tex->h || depth != tex->depth)) { 00493 /* clear first to avoid unitialized pixels */ 00494 float *zero= MEM_callocN(sizeof(float)*tex->w*tex->h*tex->depth, "zero"); 00495 glTexSubImage3D(tex->target, 0, 0, 0, 0, tex->w, tex->h, tex->depth, format, type, zero); 00496 MEM_freeN(zero); 00497 } 00498 00499 glTexSubImage3D(tex->target, 0, 0, 0, 0, w, h, depth, format, type, fpixels); 00500 GPU_print_error("3D glTexSubImage3D"); 00501 } 00502 00503 00504 glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, vfBorderColor); 00505 GPU_print_error("3D GL_TEXTURE_BORDER_COLOR"); 00506 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 00507 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 00508 GPU_print_error("3D GL_LINEAR"); 00509 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 00510 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 00511 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 00512 GPU_print_error("3D GL_CLAMP_TO_BORDER"); 00513 00514 if (pixels) 00515 MEM_freeN(pixels); 00516 00517 if (tex) 00518 GPU_texture_unbind(tex); 00519 00520 return tex; 00521 } 00522 00523 GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, double time, int mipmap) 00524 { 00525 GPUTexture *tex; 00526 GLint w, h, border, lastbindcode, bindcode; 00527 00528 glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastbindcode); 00529 00530 GPU_update_image_time(ima, time); 00531 bindcode = GPU_verify_image(ima, iuser, 0, 0, mipmap); 00532 00533 if(ima->gputexture) { 00534 ima->gputexture->bindcode = bindcode; 00535 glBindTexture(GL_TEXTURE_2D, lastbindcode); 00536 return ima->gputexture; 00537 } 00538 00539 if(!bindcode) { 00540 glBindTexture(GL_TEXTURE_2D, lastbindcode); 00541 return NULL; 00542 } 00543 00544 tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); 00545 tex->bindcode = bindcode; 00546 tex->number = -1; 00547 tex->refcount = 1; 00548 tex->target = GL_TEXTURE_2D; 00549 tex->fromblender = 1; 00550 00551 ima->gputexture= tex; 00552 00553 if (!glIsTexture(tex->bindcode)) { 00554 GPU_print_error("Blender Texture"); 00555 } 00556 else { 00557 glBindTexture(GL_TEXTURE_2D, tex->bindcode); 00558 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); 00559 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); 00560 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER, &border); 00561 00562 tex->w = w - border; 00563 tex->h = h - border; 00564 } 00565 00566 glBindTexture(GL_TEXTURE_2D, lastbindcode); 00567 00568 return tex; 00569 } 00570 00571 GPUTexture *GPU_texture_create_1D(int w, float *fpixels, char err_out[256]) 00572 { 00573 GPUTexture *tex = GPU_texture_create_nD(w, 1, 1, fpixels, 0, err_out); 00574 00575 if (tex) 00576 GPU_texture_unbind(tex); 00577 00578 return tex; 00579 } 00580 00581 GPUTexture *GPU_texture_create_2D(int w, int h, float *fpixels, char err_out[256]) 00582 { 00583 GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, err_out); 00584 00585 if (tex) 00586 GPU_texture_unbind(tex); 00587 00588 return tex; 00589 } 00590 00591 GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256]) 00592 { 00593 GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, err_out); 00594 00595 if (tex) 00596 GPU_texture_unbind(tex); 00597 00598 return tex; 00599 } 00600 00601 void GPU_texture_bind(GPUTexture *tex, int number) 00602 { 00603 GLenum arbnumber; 00604 00605 if (number >= GG.maxtextures) { 00606 GPU_print_error("Not enough texture slots."); 00607 return; 00608 } 00609 00610 if(number == -1) 00611 return; 00612 00613 GPU_print_error("Pre Texture Bind"); 00614 00615 arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + number); 00616 if (number != 0) glActiveTextureARB(arbnumber); 00617 glBindTexture(tex->target, tex->bindcode); 00618 glEnable(tex->target); 00619 if (number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); 00620 00621 tex->number = number; 00622 00623 GPU_print_error("Post Texture Bind"); 00624 } 00625 00626 void GPU_texture_unbind(GPUTexture *tex) 00627 { 00628 GLenum arbnumber; 00629 00630 if (tex->number >= GG.maxtextures) { 00631 GPU_print_error("Not enough texture slots."); 00632 return; 00633 } 00634 00635 if(tex->number == -1) 00636 return; 00637 00638 GPU_print_error("Pre Texture Unbind"); 00639 00640 arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); 00641 if (tex->number != 0) glActiveTextureARB(arbnumber); 00642 glBindTexture(tex->target, 0); 00643 glDisable(tex->target); 00644 if (tex->number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); 00645 00646 tex->number = -1; 00647 00648 GPU_print_error("Post Texture Unbind"); 00649 } 00650 00651 void GPU_texture_free(GPUTexture *tex) 00652 { 00653 tex->refcount--; 00654 00655 if (tex->refcount < 0) 00656 fprintf(stderr, "GPUTexture: negative refcount\n"); 00657 00658 if (tex->refcount == 0) { 00659 if (tex->fb) 00660 GPU_framebuffer_texture_detach(tex->fb, tex); 00661 if (tex->bindcode && !tex->fromblender) 00662 glDeleteTextures(1, &tex->bindcode); 00663 00664 MEM_freeN(tex); 00665 } 00666 } 00667 00668 void GPU_texture_ref(GPUTexture *tex) 00669 { 00670 tex->refcount++; 00671 } 00672 00673 int GPU_texture_target(GPUTexture *tex) 00674 { 00675 return tex->target; 00676 } 00677 00678 int GPU_texture_opengl_width(GPUTexture *tex) 00679 { 00680 return tex->w; 00681 } 00682 00683 int GPU_texture_opengl_height(GPUTexture *tex) 00684 { 00685 return tex->h; 00686 } 00687 00688 GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex) 00689 { 00690 return tex->fb; 00691 } 00692 00693 /* GPUFrameBuffer */ 00694 00695 struct GPUFrameBuffer { 00696 GLuint object; 00697 GPUTexture *colortex; 00698 GPUTexture *depthtex; 00699 }; 00700 00701 GPUFrameBuffer *GPU_framebuffer_create(void) 00702 { 00703 GPUFrameBuffer *fb; 00704 00705 if (!GLEW_EXT_framebuffer_object) 00706 return NULL; 00707 00708 fb= MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); 00709 glGenFramebuffersEXT(1, &fb->object); 00710 00711 if (!fb->object) { 00712 fprintf(stderr, "GPUFFrameBuffer: framebuffer gen failed. %d\n", 00713 (int)glGetError()); 00714 GPU_framebuffer_free(fb); 00715 return NULL; 00716 } 00717 00718 return fb; 00719 } 00720 00721 int GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, char err_out[256]) 00722 { 00723 GLenum status; 00724 GLenum attachment; 00725 00726 if(tex->depth) 00727 attachment = GL_DEPTH_ATTACHMENT_EXT; 00728 else 00729 attachment = GL_COLOR_ATTACHMENT0_EXT; 00730 00731 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->object); 00732 GG.currentfb = fb->object; 00733 00734 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, 00735 tex->target, tex->bindcode, 0); 00736 00737 if(tex->depth) { 00738 glDrawBuffer(GL_NONE); 00739 glReadBuffer(GL_NONE); 00740 } 00741 else { 00742 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); 00743 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); 00744 } 00745 00746 status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 00747 00748 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { 00749 GPU_framebuffer_restore(); 00750 GPU_print_framebuffer_error(status, err_out); 00751 return 0; 00752 } 00753 00754 if(tex->depth) 00755 fb->depthtex = tex; 00756 else 00757 fb->colortex = tex; 00758 00759 tex->fb= fb; 00760 00761 return 1; 00762 } 00763 00764 void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) 00765 { 00766 GLenum attachment; 00767 00768 if(!tex->fb) 00769 return; 00770 00771 if(GG.currentfb != tex->fb->object) { 00772 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tex->fb->object); 00773 GG.currentfb = tex->fb->object; 00774 } 00775 00776 if(tex->depth) { 00777 fb->depthtex = NULL; 00778 attachment = GL_DEPTH_ATTACHMENT_EXT; 00779 } 00780 else { 00781 fb->colortex = NULL; 00782 attachment = GL_COLOR_ATTACHMENT0_EXT; 00783 } 00784 00785 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, 00786 tex->target, 0, 0); 00787 00788 tex->fb = NULL; 00789 } 00790 00791 void GPU_framebuffer_texture_bind(GPUFrameBuffer *UNUSED(fb), GPUTexture *tex) 00792 { 00793 /* push attributes */ 00794 glPushAttrib(GL_ENABLE_BIT); 00795 glPushAttrib(GL_VIEWPORT_BIT); 00796 glDisable(GL_SCISSOR_TEST); 00797 00798 /* bind framebuffer */ 00799 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tex->fb->object); 00800 00801 /* push matrices and set default viewport and matrix */ 00802 glViewport(0, 0, tex->w, tex->h); 00803 GG.currentfb = tex->fb->object; 00804 00805 glMatrixMode(GL_PROJECTION); 00806 glPushMatrix(); 00807 glLoadIdentity(); 00808 glMatrixMode(GL_MODELVIEW); 00809 glPushMatrix(); 00810 glLoadIdentity(); 00811 } 00812 00813 void GPU_framebuffer_texture_unbind(GPUFrameBuffer *UNUSED(fb), GPUTexture *UNUSED(tex)) 00814 { 00815 /* restore matrix */ 00816 glMatrixMode(GL_PROJECTION); 00817 glPopMatrix(); 00818 glMatrixMode(GL_MODELVIEW); 00819 glPopMatrix(); 00820 00821 /* restore attributes */ 00822 glPopAttrib(); 00823 glPopAttrib(); 00824 glEnable(GL_SCISSOR_TEST); 00825 } 00826 00827 void GPU_framebuffer_free(GPUFrameBuffer *fb) 00828 { 00829 if(fb->depthtex) 00830 GPU_framebuffer_texture_detach(fb, fb->depthtex); 00831 if(fb->colortex) 00832 GPU_framebuffer_texture_detach(fb, fb->colortex); 00833 00834 if(fb->object) { 00835 glDeleteFramebuffersEXT(1, &fb->object); 00836 00837 if (GG.currentfb == fb->object) { 00838 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 00839 GG.currentfb = 0; 00840 } 00841 } 00842 00843 MEM_freeN(fb); 00844 } 00845 00846 void GPU_framebuffer_restore(void) 00847 { 00848 if (GG.currentfb != 0) { 00849 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 00850 GG.currentfb = 0; 00851 } 00852 } 00853 00854 /* GPUOffScreen */ 00855 00856 struct GPUOffScreen { 00857 GPUFrameBuffer *fb; 00858 GPUTexture *color; 00859 GPUTexture *depth; 00860 }; 00861 00862 GPUOffScreen *GPU_offscreen_create(int *width, int *height, char err_out[256]) 00863 { 00864 GPUOffScreen *ofs; 00865 00866 ofs= MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); 00867 00868 ofs->fb = GPU_framebuffer_create(); 00869 if(!ofs->fb) { 00870 GPU_offscreen_free(ofs); 00871 return NULL; 00872 } 00873 00874 ofs->depth = GPU_texture_create_depth(*width, *height, err_out); 00875 if(!ofs->depth) { 00876 GPU_offscreen_free(ofs); 00877 return NULL; 00878 } 00879 00880 if(*width!=ofs->depth->w || *height!=ofs->depth->h) { 00881 *width= ofs->depth->w; 00882 *height= ofs->depth->h; 00883 printf("Offscreen size differs from given size!\n"); 00884 } 00885 00886 if(!GPU_framebuffer_texture_attach(ofs->fb, ofs->depth, err_out)) { 00887 GPU_offscreen_free(ofs); 00888 return NULL; 00889 } 00890 00891 ofs->color = GPU_texture_create_2D(*width, *height, NULL, err_out); 00892 if(!ofs->color) { 00893 GPU_offscreen_free(ofs); 00894 return NULL; 00895 } 00896 00897 if(!GPU_framebuffer_texture_attach(ofs->fb, ofs->color, err_out)) { 00898 GPU_offscreen_free(ofs); 00899 return NULL; 00900 } 00901 00902 GPU_framebuffer_restore(); 00903 00904 return ofs; 00905 } 00906 00907 void GPU_offscreen_free(GPUOffScreen *ofs) 00908 { 00909 if(ofs->fb) 00910 GPU_framebuffer_free(ofs->fb); 00911 if(ofs->color) 00912 GPU_texture_free(ofs->color); 00913 if(ofs->depth) 00914 GPU_texture_free(ofs->depth); 00915 00916 MEM_freeN(ofs); 00917 } 00918 00919 void GPU_offscreen_bind(GPUOffScreen *ofs) 00920 { 00921 glDisable(GL_SCISSOR_TEST); 00922 GPU_framebuffer_texture_bind(ofs->fb, ofs->color); 00923 } 00924 00925 void GPU_offscreen_unbind(GPUOffScreen *ofs) 00926 { 00927 GPU_framebuffer_texture_unbind(ofs->fb, ofs->color); 00928 GPU_framebuffer_restore(); 00929 glEnable(GL_SCISSOR_TEST); 00930 } 00931 00932 /* GPUShader */ 00933 00934 struct GPUShader { 00935 GLhandleARB object; /* handle for full shader */ 00936 GLhandleARB vertex; /* handle for vertex shader */ 00937 GLhandleARB fragment; /* handle for fragment shader */ 00938 GLhandleARB lib; /* handle for libment shader */ 00939 int totattrib; /* total number of attributes */ 00940 }; 00941 00942 static void shader_print_errors(const char *task, char *log, const char *code) 00943 { 00944 const char *c, *pos, *end = code + strlen(code); 00945 int line = 1; 00946 00947 fprintf(stderr, "GPUShader: %s error:\n", task); 00948 00949 if(G.f & G_DEBUG) { 00950 c = code; 00951 while ((c < end) && (pos = strchr(c, '\n'))) { 00952 fprintf(stderr, "%2d ", line); 00953 fwrite(c, (pos+1)-c, 1, stderr); 00954 c = pos+1; 00955 line++; 00956 } 00957 00958 fprintf(stderr, "%s", c); 00959 } 00960 00961 fprintf(stderr, "%s\n", log); 00962 } 00963 00964 GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, /*GPUShader *lib,*/ const char *libcode) 00965 { 00966 GLint status; 00967 GLcharARB log[5000]; 00968 const char *fragsource[2]; 00969 GLsizei length = 0; 00970 GLint count; 00971 GPUShader *shader; 00972 00973 if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader) 00974 return NULL; 00975 00976 shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); 00977 00978 if(vertexcode) 00979 shader->vertex = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 00980 if(fragcode) 00981 shader->fragment = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 00982 shader->object = glCreateProgramObjectARB(); 00983 00984 if (!shader->object || 00985 (vertexcode && !shader->vertex) || 00986 (fragcode && !shader->fragment)) { 00987 fprintf(stderr, "GPUShader, object creation failed.\n"); 00988 GPU_shader_free(shader); 00989 return NULL; 00990 } 00991 00992 if(vertexcode) { 00993 glAttachObjectARB(shader->object, shader->vertex); 00994 glShaderSourceARB(shader->vertex, 1, (const char**)&vertexcode, NULL); 00995 00996 glCompileShaderARB(shader->vertex); 00997 glGetObjectParameterivARB(shader->vertex, GL_OBJECT_COMPILE_STATUS_ARB, &status); 00998 00999 if (!status) { 01000 glGetInfoLogARB(shader->vertex, sizeof(log), &length, log); 01001 shader_print_errors("compile", log, vertexcode); 01002 01003 GPU_shader_free(shader); 01004 return NULL; 01005 } 01006 } 01007 01008 if(fragcode) { 01009 count = 0; 01010 if(libcode) fragsource[count++] = libcode; 01011 if(fragcode) fragsource[count++] = fragcode; 01012 01013 glAttachObjectARB(shader->object, shader->fragment); 01014 glShaderSourceARB(shader->fragment, count, fragsource, NULL); 01015 01016 glCompileShaderARB(shader->fragment); 01017 glGetObjectParameterivARB(shader->fragment, GL_OBJECT_COMPILE_STATUS_ARB, &status); 01018 01019 if (!status) { 01020 glGetInfoLogARB(shader->fragment, sizeof(log), &length, log); 01021 shader_print_errors("compile", log, fragcode); 01022 01023 GPU_shader_free(shader); 01024 return NULL; 01025 } 01026 } 01027 01028 /*if(lib && lib->lib) 01029 glAttachObjectARB(shader->object, lib->lib);*/ 01030 01031 glLinkProgramARB(shader->object); 01032 glGetObjectParameterivARB(shader->object, GL_OBJECT_LINK_STATUS_ARB, &status); 01033 if (!status) { 01034 glGetInfoLogARB(shader->object, sizeof(log), &length, log); 01035 if (fragcode) shader_print_errors("linking", log, fragcode); 01036 else if (vertexcode) shader_print_errors("linking", log, vertexcode); 01037 else if (libcode) shader_print_errors("linking", log, libcode); 01038 01039 GPU_shader_free(shader); 01040 return NULL; 01041 } 01042 01043 return shader; 01044 } 01045 01046 #if 0 01047 GPUShader *GPU_shader_create_lib(const char *code) 01048 { 01049 GLint status; 01050 GLcharARB log[5000]; 01051 GLsizei length = 0; 01052 GPUShader *shader; 01053 01054 if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader) 01055 return NULL; 01056 01057 shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); 01058 01059 shader->lib = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 01060 01061 if (!shader->lib) { 01062 fprintf(stderr, "GPUShader, object creation failed.\n"); 01063 GPU_shader_free(shader); 01064 return NULL; 01065 } 01066 01067 glShaderSourceARB(shader->lib, 1, (const char**)&code, NULL); 01068 01069 glCompileShaderARB(shader->lib); 01070 glGetObjectParameterivARB(shader->lib, GL_OBJECT_COMPILE_STATUS_ARB, &status); 01071 01072 if (!status) { 01073 glGetInfoLogARB(shader->lib, sizeof(log), &length, log); 01074 shader_print_errors("compile", log, code); 01075 01076 GPU_shader_free(shader); 01077 return NULL; 01078 } 01079 01080 return shader; 01081 } 01082 #endif 01083 01084 void GPU_shader_bind(GPUShader *shader) 01085 { 01086 GPU_print_error("Pre Shader Bind"); 01087 glUseProgramObjectARB(shader->object); 01088 GPU_print_error("Post Shader Bind"); 01089 } 01090 01091 void GPU_shader_unbind(GPUShader *UNUSED(shader)) 01092 { 01093 GPU_print_error("Pre Shader Unbind"); 01094 glUseProgramObjectARB(0); 01095 GPU_print_error("Post Shader Unbind"); 01096 } 01097 01098 void GPU_shader_free(GPUShader *shader) 01099 { 01100 if (shader->lib) 01101 glDeleteObjectARB(shader->lib); 01102 if (shader->vertex) 01103 glDeleteObjectARB(shader->vertex); 01104 if (shader->fragment) 01105 glDeleteObjectARB(shader->fragment); 01106 if (shader->object) 01107 glDeleteObjectARB(shader->object); 01108 MEM_freeN(shader); 01109 } 01110 01111 int GPU_shader_get_uniform(GPUShader *shader, const char *name) 01112 { 01113 return glGetUniformLocationARB(shader->object, name); 01114 } 01115 01116 void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int length, int arraysize, float *value) 01117 { 01118 if(location == -1) 01119 return; 01120 01121 GPU_print_error("Pre Uniform Vector"); 01122 01123 if (length == 1) glUniform1fvARB(location, arraysize, value); 01124 else if (length == 2) glUniform2fvARB(location, arraysize, value); 01125 else if (length == 3) glUniform3fvARB(location, arraysize, value); 01126 else if (length == 4) glUniform4fvARB(location, arraysize, value); 01127 else if (length == 9) glUniformMatrix3fvARB(location, arraysize, 0, value); 01128 else if (length == 16) glUniformMatrix4fvARB(location, arraysize, 0, value); 01129 01130 GPU_print_error("Post Uniform Vector"); 01131 } 01132 01133 void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) 01134 { 01135 GLenum arbnumber; 01136 01137 if (tex->number >= GG.maxtextures) { 01138 GPU_print_error("Not enough texture slots."); 01139 return; 01140 } 01141 01142 if(tex->number == -1) 01143 return; 01144 01145 if(location == -1) 01146 return; 01147 01148 GPU_print_error("Pre Uniform Texture"); 01149 01150 arbnumber = (GLenum)((GLuint)GL_TEXTURE0_ARB + tex->number); 01151 01152 if (tex->number != 0) glActiveTextureARB(arbnumber); 01153 glBindTexture(tex->target, tex->bindcode); 01154 glUniform1iARB(location, tex->number); 01155 glEnable(tex->target); 01156 if (tex->number != 0) glActiveTextureARB(GL_TEXTURE0_ARB); 01157 01158 GPU_print_error("Post Uniform Texture"); 01159 } 01160 01161 int GPU_shader_get_attribute(GPUShader *shader, char *name) 01162 { 01163 int index; 01164 01165 GPU_print_error("Pre Get Attribute"); 01166 01167 index = glGetAttribLocationARB(shader->object, name); 01168 01169 GPU_print_error("Post Get Attribute"); 01170 01171 return index; 01172 } 01173 01174 #if 0 01175 /* GPUPixelBuffer */ 01176 01177 typedef struct GPUPixelBuffer { 01178 GLuint bindcode[2]; 01179 GLuint current; 01180 int datasize; 01181 int numbuffers; 01182 int halffloat; 01183 } GPUPixelBuffer; 01184 01185 void GPU_pixelbuffer_free(GPUPixelBuffer *pb) 01186 { 01187 if (pb->bindcode[0]) 01188 glDeleteBuffersARB(pb->numbuffers, pb->bindcode); 01189 MEM_freeN(pb); 01190 } 01191 01192 GPUPixelBuffer *gpu_pixelbuffer_create(int x, int y, int halffloat, int numbuffers) 01193 { 01194 GPUPixelBuffer *pb; 01195 01196 if (!GLEW_ARB_multitexture || !GLEW_EXT_pixel_buffer_object) 01197 return NULL; 01198 01199 pb = MEM_callocN(sizeof(GPUPixelBuffer), "GPUPBO"); 01200 pb->datasize = x*y*4*((halffloat)? 16: 8); 01201 pb->numbuffers = numbuffers; 01202 pb->halffloat = halffloat; 01203 01204 glGenBuffersARB(pb->numbuffers, pb->bindcode); 01205 01206 if (!pb->bindcode[0]) { 01207 fprintf(stderr, "GPUPixelBuffer allocation failed\n"); 01208 GPU_pixelbuffer_free(pb); 01209 return NULL; 01210 } 01211 01212 return pb; 01213 } 01214 01215 void GPU_pixelbuffer_texture(GPUTexture *tex, GPUPixelBuffer *pb) 01216 { 01217 void *pixels; 01218 int i; 01219 01220 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, tex->bindcode); 01221 01222 for (i = 0; i < pb->numbuffers; i++) { 01223 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb->bindcode[pb->current]); 01224 glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb->datasize, NULL, 01225 GL_STREAM_DRAW_ARB); 01226 01227 pixels = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); 01228 /*memcpy(pixels, _oImage.data(), pb->datasize);*/ 01229 01230 if (!glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT)) { 01231 fprintf(stderr, "Could not unmap opengl PBO\n"); 01232 break; 01233 } 01234 } 01235 01236 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0); 01237 } 01238 01239 static int pixelbuffer_map_into_gpu(GLuint bindcode) 01240 { 01241 void *pixels; 01242 01243 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, bindcode); 01244 pixels = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY); 01245 01246 /* do stuff in pixels */ 01247 01248 if (!glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT)) { 01249 fprintf(stderr, "Could not unmap opengl PBO\n"); 01250 return 0; 01251 } 01252 01253 return 1; 01254 } 01255 01256 static void pixelbuffer_copy_to_texture(GPUTexture *tex, GPUPixelBuffer *pb, GLuint bindcode) 01257 { 01258 GLenum type = (pb->halffloat)? GL_HALF_FLOAT_NV: GL_UNSIGNED_BYTE; 01259 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, tex->bindcode); 01260 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, bindcode); 01261 01262 glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, tex->w, tex->h, 01263 GL_RGBA, type, NULL); 01264 01265 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0); 01266 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0); 01267 } 01268 01269 void GPU_pixelbuffer_async_to_gpu(GPUTexture *tex, GPUPixelBuffer *pb) 01270 { 01271 int newbuffer; 01272 01273 if (pb->numbuffers == 1) { 01274 pixelbuffer_copy_to_texture(tex, pb, pb->bindcode[0]); 01275 pixelbuffer_map_into_gpu(pb->bindcode[0]); 01276 } 01277 else { 01278 pb->current = (pb->current+1)%pb->numbuffers; 01279 newbuffer = (pb->current+1)%pb->numbuffers; 01280 01281 pixelbuffer_map_into_gpu(pb->bindcode[newbuffer]); 01282 pixelbuffer_copy_to_texture(tex, pb, pb->bindcode[pb->current]); 01283 } 01284 } 01285 #endif 01286