Errors are caught & reported by our GL debug callback. This gives us way more useful information than sporadic calls to glGetError. I removed almost all use of glGetError, including our own GPU_ASSERT_NO_GL_ERRORS and GPU_CHECK_ERRORS_AROUND macros. Still used in rna_Image_gl_load because it passes unvalidated input to OpenGL functions. Still used in gpu_state_print_fl_ex as an exception handling hack -- will rewrite this soon. The optimism embodied by this commit will not prevent OpenGL errors. We need to analyze what would cause GL to fail at certain points and proactively intercept these failures. Or guarantee they can't happen.
748 lines
19 KiB
C
748 lines
19 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2005 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Brecht Van Lommel.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_image_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_math_base.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "GPU_debug.h"
|
|
#include "GPU_draw.h"
|
|
#include "GPU_extensions.h"
|
|
#include "GPU_framebuffer.h"
|
|
#include "GPU_glew.h"
|
|
#include "GPU_texture.h"
|
|
|
|
static struct GPUTextureGlobal {
|
|
GPUTexture *invalid_tex_1D; /* texture used in place of invalid textures (not loaded correctly, missing) */
|
|
GPUTexture *invalid_tex_2D;
|
|
GPUTexture *invalid_tex_3D;
|
|
} GG = {NULL, NULL, NULL};
|
|
|
|
/* GPUTexture */
|
|
|
|
struct GPUTexture {
|
|
int w, h; /* width/height */
|
|
int number; /* number for multitexture binding */
|
|
int refcount; /* reference count */
|
|
GLenum target; /* GL_TEXTURE_* */
|
|
GLenum target_base; /* same as target, (but no multisample)
|
|
* use it for unbinding */
|
|
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;
|
|
|
|
p = pixels = MEM_callocN(sizeof(unsigned char) * len, "GPUTexturePixels");
|
|
|
|
for (int a = 0; a < len; a++, p++, fp++)
|
|
*p = FTOCHAR((*fp));
|
|
|
|
return pixels;
|
|
}
|
|
|
|
static void GPU_glTexSubImageEmpty(GLenum target, GLenum format, int x, int y, int w, int h)
|
|
{
|
|
void *pixels = MEM_callocN(sizeof(char) * 4 * w * h, "GPUTextureEmptyPixels");
|
|
|
|
if (target == GL_TEXTURE_1D)
|
|
glTexSubImage1D(target, 0, x, w, format, GL_UNSIGNED_BYTE, pixels);
|
|
else
|
|
glTexSubImage2D(target, 0, x, y, w, h, format, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
MEM_freeN(pixels);
|
|
}
|
|
|
|
static GPUTexture *GPU_texture_create_nD(
|
|
int w, int h, int n, const float *fpixels, int depth,
|
|
GPUHDRType hdr_type, int components, int samples,
|
|
char err_out[256])
|
|
{
|
|
GLenum type, format, internalformat;
|
|
void *pixels = NULL;
|
|
|
|
if (samples) {
|
|
CLAMP_MAX(samples, GPU_max_color_texture_samples());
|
|
}
|
|
|
|
GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
|
|
tex->w = w;
|
|
tex->h = h;
|
|
tex->number = -1;
|
|
tex->refcount = 1;
|
|
tex->target = (n == 1) ? GL_TEXTURE_1D : (samples ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
|
|
tex->target_base = (n == 1) ? GL_TEXTURE_1D : GL_TEXTURE_2D;
|
|
tex->depth = depth;
|
|
tex->fb_attachment = -1;
|
|
|
|
glGenTextures(1, &tex->bindcode);
|
|
|
|
if (!tex->bindcode) {
|
|
if (err_out) {
|
|
BLI_snprintf(err_out, 256, "GPUTexture: texture create failed");
|
|
}
|
|
else {
|
|
fprintf(stderr, "GPUTexture: texture create failed");
|
|
}
|
|
GPU_texture_free(tex);
|
|
return NULL;
|
|
}
|
|
|
|
if (!GPU_full_non_power_of_two_support()) {
|
|
tex->w = power_of_2_max_i(tex->w);
|
|
tex->h = power_of_2_max_i(tex->h);
|
|
}
|
|
|
|
tex->number = 0;
|
|
glBindTexture(tex->target, tex->bindcode);
|
|
|
|
if (depth) {
|
|
type = GL_UNSIGNED_BYTE;
|
|
format = GL_DEPTH_COMPONENT;
|
|
internalformat = GL_DEPTH_COMPONENT;
|
|
}
|
|
else {
|
|
type = GL_FLOAT;
|
|
|
|
if (components == 4) {
|
|
format = GL_RGBA;
|
|
switch (hdr_type) {
|
|
case GPU_HDR_NONE:
|
|
internalformat = GL_RGBA8;
|
|
break;
|
|
/* the following formats rely on ARB_texture_float or OpenGL 3.0 */
|
|
case GPU_HDR_HALF_FLOAT:
|
|
internalformat = GL_RGBA16F_ARB;
|
|
break;
|
|
case GPU_HDR_FULL_FLOAT:
|
|
internalformat = GL_RGBA32F_ARB;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (components == 2) {
|
|
/* these formats rely on ARB_texture_rg or OpenGL 3.0 */
|
|
format = GL_RG;
|
|
switch (hdr_type) {
|
|
case GPU_HDR_NONE:
|
|
internalformat = GL_RG8;
|
|
break;
|
|
case GPU_HDR_HALF_FLOAT:
|
|
internalformat = GL_RG16F;
|
|
break;
|
|
case GPU_HDR_FULL_FLOAT:
|
|
internalformat = GL_RG32F;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fpixels && hdr_type == GPU_HDR_NONE) {
|
|
type = GL_UNSIGNED_BYTE;
|
|
pixels = GPU_texture_convert_pixels(w * h, fpixels);
|
|
}
|
|
}
|
|
|
|
if (tex->target == GL_TEXTURE_1D) {
|
|
glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL);
|
|
|
|
if (fpixels) {
|
|
glTexSubImage1D(tex->target, 0, 0, w, format, type,
|
|
pixels ? pixels : fpixels);
|
|
|
|
if (tex->w > w) {
|
|
GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, 1);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (samples) {
|
|
glTexImage2DMultisample(tex->target, samples, internalformat, tex->w, tex->h, true);
|
|
}
|
|
else {
|
|
glTexImage2D(tex->target, 0, internalformat, tex->w, tex->h, 0,
|
|
format, type, NULL);
|
|
}
|
|
|
|
if (fpixels) {
|
|
glTexSubImage2D(tex->target, 0, 0, 0, w, h,
|
|
format, type, pixels ? pixels : fpixels);
|
|
|
|
if (tex->w > w)
|
|
GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, tex->h);
|
|
if (tex->h > h)
|
|
GPU_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h - h);
|
|
}
|
|
}
|
|
|
|
if (pixels)
|
|
MEM_freeN(pixels);
|
|
|
|
if (depth) {
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
glTexParameteri(tex->target_base, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
|
|
}
|
|
else {
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
|
|
if (tex->target_base != GL_TEXTURE_1D) {
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
else
|
|
glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
return tex;
|
|
}
|
|
|
|
|
|
GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels)
|
|
{
|
|
GLenum type, format, internalformat;
|
|
void *pixels = NULL;
|
|
|
|
GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
|
|
tex->w = w;
|
|
tex->h = h;
|
|
tex->depth = depth;
|
|
tex->number = -1;
|
|
tex->refcount = 1;
|
|
tex->target = GL_TEXTURE_3D;
|
|
tex->target_base = GL_TEXTURE_3D;
|
|
|
|
glGenTextures(1, &tex->bindcode);
|
|
|
|
if (!tex->bindcode) {
|
|
fprintf(stderr, "GPUTexture: texture create failed");
|
|
GPU_texture_free(tex);
|
|
return NULL;
|
|
}
|
|
|
|
tex->number = 0;
|
|
glBindTexture(tex->target, tex->bindcode);
|
|
|
|
type = GL_FLOAT;
|
|
if (channels == 4) {
|
|
format = GL_RGBA;
|
|
internalformat = GL_RGBA8;
|
|
}
|
|
else {
|
|
format = GL_RED;
|
|
internalformat = GL_INTENSITY8;
|
|
}
|
|
|
|
/* 3D textures are quite heavy, test if it's possible to create them first */
|
|
glTexImage3D(GL_PROXY_TEXTURE_3D, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL);
|
|
|
|
bool rescale = false;
|
|
int r_width;
|
|
|
|
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width);
|
|
|
|
while (r_width == 0) {
|
|
rescale = true;
|
|
tex->w /= 2;
|
|
tex->h /= 2;
|
|
tex->depth /= 2;
|
|
glTexImage3D(GL_PROXY_TEXTURE_3D, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL);
|
|
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width);
|
|
}
|
|
|
|
/* 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
|
|
|
|
/* hardcore stuff, 3D texture rescaling - warning, this is gonna hurt your performance a lot, but we need it
|
|
* for gooseberry */
|
|
if (rescale && fpixels) {
|
|
/* FIXME: should these be floating point? */
|
|
const unsigned int xf = w / tex->w, yf = h / tex->h, zf = depth / tex->depth;
|
|
float *tex3d = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->depth, "tex3d");
|
|
|
|
GPU_print_error_debug("You need to scale a 3D texture, feel the pain!");
|
|
|
|
for (unsigned k = 0; k < tex->depth; k++) {
|
|
for (unsigned j = 0; j < tex->h; j++) {
|
|
for (unsigned i = 0; i < tex->w; i++) {
|
|
/* obviously doing nearest filtering here,
|
|
* it's going to be slow in any case, let's not make it worse */
|
|
float xb = i * xf;
|
|
float yb = j * yf;
|
|
float zb = k * zf;
|
|
unsigned int offset = k * (tex->w * tex->h) + i * tex->h + j;
|
|
unsigned int offset_orig = (zb) * (w * h) + (xb) * h + (yb);
|
|
|
|
if (channels == 4) {
|
|
tex3d[offset * 4] = fpixels[offset_orig * 4];
|
|
tex3d[offset * 4 + 1] = fpixels[offset_orig * 4 + 1];
|
|
tex3d[offset * 4 + 2] = fpixels[offset_orig * 4 + 2];
|
|
tex3d[offset * 4 + 3] = fpixels[offset_orig * 4 + 3];
|
|
}
|
|
else
|
|
tex3d[offset] = fpixels[offset_orig];
|
|
}
|
|
}
|
|
}
|
|
|
|
glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, tex3d);
|
|
|
|
MEM_freeN(tex3d);
|
|
}
|
|
else if (fpixels) {
|
|
glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, fpixels);
|
|
}
|
|
|
|
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, int textarget, bool is_data, double time, int mipmap)
|
|
{
|
|
int gputt;
|
|
/* this binds a texture, so that's why to restore it to 0 */
|
|
GLint bindcode = GPU_verify_image(ima, iuser, textarget, 0, 0, mipmap, is_data);
|
|
GPU_update_image_time(ima, time);
|
|
|
|
/* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP
|
|
* these values are correct for glDisable, so textarget can be safely used in
|
|
* GPU_texture_bind/GPU_texture_unbind through tex->target_base */
|
|
if (textarget == GL_TEXTURE_2D)
|
|
gputt = TEXTARGET_TEXTURE_2D;
|
|
else
|
|
gputt = TEXTARGET_TEXTURE_CUBE_MAP;
|
|
|
|
if (ima->gputexture[gputt]) {
|
|
ima->gputexture[gputt]->bindcode = bindcode;
|
|
glBindTexture(textarget, 0);
|
|
return ima->gputexture[gputt];
|
|
}
|
|
|
|
GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
|
|
tex->bindcode = bindcode;
|
|
tex->number = -1;
|
|
tex->refcount = 1;
|
|
tex->target = textarget;
|
|
tex->target_base = textarget;
|
|
tex->fromblender = 1;
|
|
|
|
ima->gputexture[gputt] = tex;
|
|
|
|
if (!glIsTexture(tex->bindcode)) {
|
|
GPU_print_error_debug("Blender Texture Not Loaded");
|
|
}
|
|
else {
|
|
GLint w, h, border;
|
|
|
|
GLenum gettarget;
|
|
|
|
if (textarget == GL_TEXTURE_2D)
|
|
gettarget = GL_TEXTURE_2D;
|
|
else
|
|
gettarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
|
|
|
|
glBindTexture(textarget, tex->bindcode);
|
|
glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w);
|
|
glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h);
|
|
glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_BORDER, &border);
|
|
|
|
tex->w = w - border;
|
|
tex->h = h - border;
|
|
}
|
|
|
|
glBindTexture(textarget, 0);
|
|
|
|
return tex;
|
|
}
|
|
|
|
GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap)
|
|
{
|
|
GPUTexture *tex = prv->gputexture[0];
|
|
GLuint bindcode = 0;
|
|
|
|
if (tex)
|
|
bindcode = tex->bindcode;
|
|
|
|
/* this binds a texture, so that's why we restore it to 0 */
|
|
if (bindcode == 0) {
|
|
GPU_create_gl_tex(&bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], GL_TEXTURE_2D, mipmap, 0, NULL);
|
|
}
|
|
if (tex) {
|
|
tex->bindcode = bindcode;
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
return tex;
|
|
}
|
|
|
|
tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture");
|
|
tex->bindcode = bindcode;
|
|
tex->number = -1;
|
|
tex->refcount = 1;
|
|
tex->target = GL_TEXTURE_2D;
|
|
tex->target_base = GL_TEXTURE_2D;
|
|
|
|
prv->gputexture[0] = tex;
|
|
|
|
if (!glIsTexture(tex->bindcode)) {
|
|
GPU_print_error_debug("Blender Texture Not Loaded");
|
|
}
|
|
else {
|
|
GLint w, h;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex->bindcode);
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
|
|
|
|
tex->w = w;
|
|
tex->h = h;
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
GPUTexture *GPU_texture_create_1D(int w, const float *fpixels, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, 1, 1, fpixels, 0, GPU_HDR_NONE, 4, 0, err_out);
|
|
|
|
if (tex)
|
|
GPU_texture_unbind(tex);
|
|
|
|
return tex;
|
|
}
|
|
|
|
GPUTexture *GPU_texture_create_2D(int w, int h, const float *fpixels, GPUHDRType hdr, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, hdr, 4, 0, err_out);
|
|
|
|
if (tex)
|
|
GPU_texture_unbind(tex);
|
|
|
|
return tex;
|
|
}
|
|
GPUTexture *GPU_texture_create_2D_multisample(
|
|
int w, int h, const float *fpixels, GPUHDRType hdr, int samples, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, h, 2, fpixels, 0, hdr, 4, samples, err_out);
|
|
|
|
if (tex)
|
|
GPU_texture_unbind(tex);
|
|
|
|
return tex;
|
|
}
|
|
|
|
GPUTexture *GPU_texture_create_depth(int w, int h, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, GPU_HDR_NONE, 1, 0, err_out);
|
|
|
|
if (tex)
|
|
GPU_texture_unbind(tex);
|
|
|
|
return tex;
|
|
}
|
|
GPUTexture *GPU_texture_create_depth_multisample(int w, int h, int samples, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, h, 2, NULL, 1, GPU_HDR_NONE, 1, samples, err_out);
|
|
|
|
if (tex)
|
|
GPU_texture_unbind(tex);
|
|
|
|
return tex;
|
|
}
|
|
|
|
/**
|
|
* A shadow map for VSM needs two components (depth and depth^2)
|
|
*/
|
|
GPUTexture *GPU_texture_create_vsm_shadow_map(int size, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(size, size, 2, NULL, 0, GPU_HDR_FULL_FLOAT, 2, 0, err_out);
|
|
|
|
if (tex) {
|
|
/* Now we tweak some of the settings */
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
GPU_texture_unbind(tex);
|
|
}
|
|
|
|
return tex;
|
|
}
|
|
|
|
GPUTexture *GPU_texture_create_2D_procedural(int w, int h, const float *pixels, bool repeat, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, h, 2, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out);
|
|
|
|
if (tex) {
|
|
/* Now we tweak some of the settings */
|
|
if (repeat) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
GPU_texture_unbind(tex);
|
|
}
|
|
|
|
return tex;
|
|
}
|
|
|
|
GPUTexture *GPU_texture_create_1D_procedural(int w, const float *pixels, char err_out[256])
|
|
{
|
|
GPUTexture *tex = GPU_texture_create_nD(w, 0, 1, pixels, 0, GPU_HDR_HALF_FLOAT, 2, 0, err_out);
|
|
|
|
if (tex) {
|
|
/* Now we tweak some of the settings */
|
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
GPU_texture_unbind(tex);
|
|
}
|
|
|
|
return tex;
|
|
}
|
|
|
|
void GPU_invalid_tex_init(void)
|
|
{
|
|
const float color[4] = {1.0f, 0.0f, 1.0f, 1.0f};
|
|
GG.invalid_tex_1D = GPU_texture_create_1D(1, color, NULL);
|
|
GG.invalid_tex_2D = GPU_texture_create_2D(1, 1, color, GPU_HDR_NONE, NULL);
|
|
GG.invalid_tex_3D = GPU_texture_create_3D(1, 1, 1, 4, color);
|
|
}
|
|
|
|
void GPU_invalid_tex_bind(int mode)
|
|
{
|
|
switch (mode) {
|
|
case GL_TEXTURE_1D:
|
|
glBindTexture(GL_TEXTURE_1D, GG.invalid_tex_1D->bindcode);
|
|
break;
|
|
case GL_TEXTURE_2D:
|
|
glBindTexture(GL_TEXTURE_2D, GG.invalid_tex_2D->bindcode);
|
|
break;
|
|
case GL_TEXTURE_3D:
|
|
glBindTexture(GL_TEXTURE_3D, GG.invalid_tex_3D->bindcode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GPU_invalid_tex_free(void)
|
|
{
|
|
if (GG.invalid_tex_1D)
|
|
GPU_texture_free(GG.invalid_tex_1D);
|
|
if (GG.invalid_tex_2D)
|
|
GPU_texture_free(GG.invalid_tex_2D);
|
|
if (GG.invalid_tex_3D)
|
|
GPU_texture_free(GG.invalid_tex_3D);
|
|
}
|
|
|
|
|
|
void GPU_texture_bind(GPUTexture *tex, int number)
|
|
{
|
|
if (number >= GPU_max_textures()) {
|
|
fprintf(stderr, "Not enough texture slots.\n");
|
|
return;
|
|
}
|
|
|
|
if ((G.debug & G_DEBUG)) {
|
|
if (tex->fb && GPU_framebuffer_bound(tex->fb)) {
|
|
fprintf(stderr, "Feedback loop warning!: Attempting to bind texture attached to current framebuffer!\n");
|
|
}
|
|
}
|
|
|
|
if (number < 0)
|
|
return;
|
|
|
|
GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number);
|
|
if (number != 0) glActiveTexture(arbnumber);
|
|
if (tex->bindcode != 0) {
|
|
glBindTexture(tex->target_base, tex->bindcode);
|
|
}
|
|
else
|
|
GPU_invalid_tex_bind(tex->target_base);
|
|
glEnable(tex->target_base);
|
|
if (number != 0) glActiveTexture(GL_TEXTURE0);
|
|
|
|
tex->number = number;
|
|
}
|
|
|
|
void GPU_texture_unbind(GPUTexture *tex)
|
|
{
|
|
if (tex->number >= GPU_max_textures()) {
|
|
fprintf(stderr, "Not enough texture slots.\n");
|
|
return;
|
|
}
|
|
|
|
if (tex->number == -1)
|
|
return;
|
|
|
|
GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number);
|
|
if (tex->number != 0) glActiveTexture(arbnumber);
|
|
glBindTexture(tex->target_base, 0);
|
|
glDisable(tex->target_base);
|
|
if (tex->number != 0) glActiveTexture(GL_TEXTURE0);
|
|
|
|
tex->number = -1;
|
|
}
|
|
|
|
int GPU_texture_bound_number(GPUTexture *tex)
|
|
{
|
|
return tex->number;
|
|
}
|
|
|
|
void GPU_texture_filter_mode(GPUTexture *tex, bool compare, bool use_filter)
|
|
{
|
|
if (tex->number >= GPU_max_textures()) {
|
|
fprintf(stderr, "Not enough texture slots.\n");
|
|
return;
|
|
}
|
|
|
|
if (tex->number == -1)
|
|
return;
|
|
|
|
GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number);
|
|
if (tex->number != 0) glActiveTexture(arbnumber);
|
|
|
|
if (tex->depth) {
|
|
if (compare)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
else
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
}
|
|
|
|
if (use_filter) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
else {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
if (tex->number != 0) glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
void GPU_texture_free(GPUTexture *tex)
|
|
{
|
|
tex->refcount--;
|
|
|
|
if (tex->refcount < 0)
|
|
fprintf(stderr, "GPUTexture: negative refcount\n");
|
|
|
|
if (tex->refcount == 0) {
|
|
if (tex->fb)
|
|
GPU_framebuffer_texture_detach(tex);
|
|
if (tex->bindcode && !tex->fromblender)
|
|
glDeleteTextures(1, &tex->bindcode);
|
|
|
|
MEM_freeN(tex);
|
|
}
|
|
}
|
|
|
|
void GPU_texture_ref(GPUTexture *tex)
|
|
{
|
|
tex->refcount++;
|
|
}
|
|
|
|
int GPU_texture_target(const GPUTexture *tex)
|
|
{
|
|
return tex->target;
|
|
}
|
|
|
|
int GPU_texture_width(const GPUTexture *tex)
|
|
{
|
|
return tex->w;
|
|
}
|
|
|
|
int GPU_texture_height(const GPUTexture *tex)
|
|
{
|
|
return tex->h;
|
|
}
|
|
|
|
int GPU_texture_depth(const GPUTexture *tex)
|
|
{
|
|
return tex->depth;
|
|
}
|
|
|
|
int GPU_texture_opengl_bindcode(const GPUTexture *tex)
|
|
{
|
|
return tex->bindcode;
|
|
}
|
|
|
|
GPUFrameBuffer *GPU_texture_framebuffer(GPUTexture *tex)
|
|
{
|
|
return tex->fb;
|
|
}
|
|
|
|
int GPU_texture_framebuffer_attachment(GPUTexture *tex)
|
|
{
|
|
return tex->fb_attachment;
|
|
}
|
|
|
|
void GPU_texture_framebuffer_set(GPUTexture *tex, GPUFrameBuffer *fb, int attachment)
|
|
{
|
|
tex->fb = fb;
|
|
tex->fb_attachment = attachment;
|
|
}
|
|
|