diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2020-07-29 05:55:21 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2020-07-29 16:03:02 +0300 |
commit | e749643793809248dfc6ffd078be04aec3eeab82 (patch) | |
tree | e9b12ab51205aeb344c17ebd0d38d21c76e7cd69 | |
parent | e4ee9de63899e2da58cdfdd9cbf2ac5bb2e3755a (diff) |
GPU: Refactor gpu_texture_image to not use GL calls
This is also a bit of code cleanup, reorganisation.
Tried to be DRYed but avoid too much code change to (hopefully) minimize
breakage.
- GPU: remove TEXTARGET_CUBE_MAP, this is no longer used in the codebase.
- GPUTexture: Move compressed texture upload to gpu_texture.cc
- GPUTexture: Add per texture Anisotropic filtering switch
-rw-r--r-- | source/blender/gpu/GPU_draw.h | 10 | ||||
-rw-r--r-- | source/blender/gpu/GPU_texture.h | 16 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture.cc | 209 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture_image.cc | 1321 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture_smoke.cc | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_image_types.h | 4 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_movieclip_types.h | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_userdef.c | 4 | ||||
-rw-r--r-- | source/blender/nodes/shader/nodes/node_shader_tex_environment.c | 3 | ||||
-rw-r--r-- | source/blender/nodes/shader/nodes/node_shader_tex_image.c | 3 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_init_exit.c | 5 |
11 files changed, 559 insertions, 1021 deletions
diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index 01af654b52f..8110920527d 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -42,23 +42,13 @@ struct Main; /* Mipmap settings * - these will free textures on changes */ -void GPU_set_mipmap(struct Main *bmain, bool mipmap); -bool GPU_get_mipmap(void); -void GPU_set_linear_mipmap(bool linear); -bool GPU_get_linear_mipmap(void); void GPU_paint_set_mipmap(struct Main *bmain, bool mipmap); -/* Anisotropic filtering settings - * - these will free textures on changes */ -void GPU_set_anisotropic(float value); -float GPU_get_anisotropic(void); - /* Image updates and free * - these deal with images bound as opengl textures */ void GPU_paint_update_image( struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); -bool GPU_upload_dxt_texture(struct ImBuf *ibuf, bool use_srgb, uint *bindcode); void GPU_free_image(struct Image *ima); void GPU_free_images(struct Main *bmain); void GPU_free_images_anim(struct Main *bmain); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 6685e2a2171..42afe2b63bf 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -41,7 +41,6 @@ typedef struct GPUTexture GPUTexture; /* Used to get the correct gpu texture from an Image datablock. */ typedef enum eGPUTextureTarget { TEXTARGET_2D = 0, - TEXTARGET_CUBE_MAP, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING, TEXTARGET_COUNT, @@ -131,7 +130,6 @@ typedef enum eGPUTextureFormat { #if 0 GPU_RGB10_A2, GPU_RGB10_A2UI, - GPU_SRGB8_A8, #endif GPU_R11F_G11F_B10F, GPU_DEPTH32F_STENCIL8, @@ -160,7 +158,13 @@ typedef enum eGPUTextureFormat { GPU_R8_SNORM, #endif -/* Special formats texture only */ + /* Special formats texture only */ + GPU_SRGB8_A8_DXT1, + GPU_SRGB8_A8_DXT3, + GPU_SRGB8_A8_DXT5, + GPU_RGBA8_DXT1, + GPU_RGBA8_DXT3, + GPU_RGBA8_DXT5, #if 0 GPU_SRGB8, GPU_RGB9_E5, @@ -233,7 +237,10 @@ GPUTexture *GPU_texture_create_cube_array( GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert); GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer); -GPUTexture *GPU_texture_from_bindcode(eGPUTextureTarget target, int bindcode); +GPUTexture *GPU_texture_create_error(eGPUTextureTarget target); +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat format, const void *data); + GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, struct ImBuf *ibuf, @@ -279,6 +286,7 @@ void GPU_texture_unbind_all(void); void GPU_texture_copy(GPUTexture *dst, GPUTexture *src); void GPU_texture_generate_mipmap(GPUTexture *tex); +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso); void GPU_texture_compare_mode(GPUTexture *tex, bool use_compare); void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter); void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 2afba122cfd..db9d961e2e3 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "DNA_image_types.h" +#include "DNA_userdef_types.h" #include "BLI_blenlib.h" #include "BLI_math_base.h" @@ -439,6 +440,13 @@ static uint gpu_get_bytesize(eGPUTextureFormat data_type) case GPU_R8: case GPU_R8UI: return 1; + case GPU_SRGB8_A8_DXT1: + case GPU_SRGB8_A8_DXT3: + case GPU_SRGB8_A8_DXT5: + case GPU_RGBA8_DXT1: + case GPU_RGBA8_DXT3: + case GPU_RGBA8_DXT5: + return 1; /* Incorrect but actual size is fractional. */ default: BLI_assert(!"Texture format incorrect or unsupported\n"); return 0; @@ -524,7 +532,18 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) case GPU_RGB16F: return GL_RGB16F; /* Special formats texture only */ - /* ** Add Format here */ + case GPU_SRGB8_A8_DXT1: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + case GPU_SRGB8_A8_DXT3: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + case GPU_SRGB8_A8_DXT5: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case GPU_RGBA8_DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case GPU_RGBA8_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case GPU_RGBA8_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; /* Depth Formats */ case GPU_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F; @@ -538,99 +557,6 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) } } -static eGPUTextureFormat gl_internalformat_to_gpu_format(const GLint glformat) -{ - /* You can add any of the available type to this list - * For available types see GPU_texture.h */ - switch (glformat) { - /* Formats texture & renderbuffer */ - case GL_RGBA8UI: - return GPU_RGBA8UI; - case GL_RGBA8I: - return GPU_RGBA8I; - case GL_RGBA8: - return GPU_RGBA8; - case GL_RGBA32UI: - return GPU_RGBA32UI; - case GL_RGBA32I: - return GPU_RGBA32I; - case GL_RGBA32F: - return GPU_RGBA32F; - case GL_RGBA16UI: - return GPU_RGBA16UI; - case GL_RGBA16I: - return GPU_RGBA16I; - case GL_RGBA16F: - return GPU_RGBA16F; - case GL_RGBA16: - return GPU_RGBA16; - case GL_RG8UI: - return GPU_RG8UI; - case GL_RG8I: - return GPU_RG8I; - case GL_RG8: - return GPU_RG8; - case GL_RG32UI: - return GPU_RG32UI; - case GL_RG32I: - return GPU_RG32I; - case GL_RG32F: - return GPU_RG32F; - case GL_RG16UI: - return GPU_RG16UI; - case GL_RG16I: - return GPU_RG16I; - case GL_RG16F: - return GPU_RGBA32F; - case GL_RG16: - return GPU_RG16; - case GL_R8UI: - return GPU_R8UI; - case GL_R8I: - return GPU_R8I; - case GL_R8: - return GPU_R8; - case GL_R32UI: - return GPU_R32UI; - case GL_R32I: - return GPU_R32I; - case GL_R32F: - return GPU_R32F; - case GL_R16UI: - return GPU_R16UI; - case GL_R16I: - return GPU_R16I; - case GL_R16F: - return GPU_R16F; - case GL_R16: - return GPU_R16; - /* Special formats texture & renderbuffer */ - case GL_R11F_G11F_B10F: - return GPU_R11F_G11F_B10F; - case GL_DEPTH32F_STENCIL8: - return GPU_DEPTH32F_STENCIL8; - case GL_DEPTH24_STENCIL8: - return GPU_DEPTH24_STENCIL8; - case GL_SRGB8_ALPHA8: - return GPU_SRGB8_A8; - /* Texture only format */ - case GL_RGB16F: - return GPU_RGB16F; - /* Special formats texture only */ - /* ** Add Format here */ - /* Depth Formats */ - case GL_DEPTH_COMPONENT32F: - return GPU_DEPTH_COMPONENT32F; - case GL_DEPTH_COMPONENT24: - return GPU_DEPTH_COMPONENT24; - case GL_DEPTH_COMPONENT16: - return GPU_DEPTH_COMPONENT16; - default: - BLI_assert(!"Internal format incorrect or unsupported\n"); - return GPU_RGBA8; - } -} - static GLenum gpu_get_gl_datatype(eGPUDataFormat format) { switch (format) { @@ -1197,8 +1123,6 @@ static GLenum convert_target_to_gl(eGPUTextureTarget target) switch (target) { case TEXTARGET_2D: return GL_TEXTURE_2D; - case TEXTARGET_CUBE_MAP: - return GL_TEXTURE_CUBE_MAP; case TEXTARGET_2D_ARRAY: return GL_TEXTURE_2D_ARRAY; case TEXTARGET_TILE_MAPPING: @@ -1209,48 +1133,72 @@ static GLenum convert_target_to_gl(eGPUTextureTarget target) } } -/* TODO(fclem) This function should be remove and gpu_texture_image rewritten to not use any GL - * commands. */ -GPUTexture *GPU_texture_from_bindcode(eGPUTextureTarget target, int bindcode) +/* Create an error texture that will bind an invalid texture (pink) at draw time. */ +GPUTexture *GPU_texture_create_error(eGPUTextureTarget target) { GLenum textarget = convert_target_to_gl(target); GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); - tex->bindcode = bindcode; + tex->bindcode = 0; tex->refcount = 1; tex->target = textarget; tex->target_base = textarget; tex->samples = 0; - tex->sampler_state = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO; - if (GPU_get_mipmap()) { - tex->sampler_state |= (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER); - } + tex->sampler_state = GPU_SAMPLER_DEFAULT; tex->number = -1; - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); + GPU_print_error_debug("Blender Texture Not Loaded"); + return tex; +} + +/* DDS texture loading. Return NULL if support is not available. */ +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) +{ + if (!GLEW_EXT_texture_compression_s3tc) { + return NULL; } - else { - GLint w, h, gl_format; - GLenum gettarget; - gettarget = (textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : textarget; - - glBindTexture(textarget, tex->bindcode); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_format); - tex->w = w; - tex->h = h; - tex->format = gl_internalformat_to_gpu_format(gl_format); - tex->components = gpu_get_component_count(tex->format); - glBindTexture(textarget, 0); - - /* Depending on how this bindcode was obtained, the memory used here could - * already have been computed. - * But that is not the case currently. */ - gpu_texture_memory_footprint_add(tex); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->w = w; + tex->h = h; + tex->refcount = 1; + tex->target = tex->target_base = GL_TEXTURE_2D; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); + tex->components = gpu_get_component_count(tex_format); + tex->mipmaps = miplen - 1; + tex->sampler_state = GPU_SAMPLER_DEFAULT; + tex->number = -1; + + GLenum internalformat = gpu_format_to_gl_internalformat(tex_format); + + glGenTextures(1, &tex->bindcode); + glBindTexture(tex->target, tex->bindcode); + + /* Reset to opengl Defaults. (Untested, might not be needed) */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int blocksize = (ELEM(tex_format, GPU_RGBA8_DXT1, GPU_SRGB8_A8_DXT1)) ? 8 : 16; + + size_t ofs = 0; + for (int mip = 0; mip < miplen && (w || h); mip++, w >>= 1, h >>= 1) { + w = max_ii(1, w); + h = max_ii(1, h); + size_t size = ((w + 3) / 4) * ((h + 3) / 4) * blocksize; + + glCompressedTexImage2D(tex->target, mip, internalformat, w, h, 0, size, (uchar *)data + ofs); + + ofs += size; } + /* Restore Blender default. */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, tex->mipmaps); + glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBindTexture(tex->target, 0); + return tex; } @@ -1970,6 +1918,14 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter) SET_FLAG_FROM_TEST(tex->sampler_state, use_filter, GPU_SAMPLER_FILTER); } +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso) +{ + /* Stencil and integer format does not support filtering. */ + BLI_assert(!(use_aniso) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex))); + + SET_FLAG_FROM_TEST(tex->sampler_state, use_aniso, GPU_SAMPLER_ANISO); +} + void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp) { SET_FLAG_FROM_TEST(tex->sampler_state, use_repeat, GPU_SAMPLER_REPEAT); @@ -2194,8 +2150,9 @@ void GPU_samplers_init(void) ((state & GPU_SAMPLER_MIPMAP) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) : ((state & GPU_SAMPLER_MIPMAP) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST); GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; + /* TODO(fclem) Anisotropic level should be a render engine parameter. */ float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ? - GPU_get_anisotropic() : + U.anisotropic_filter : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); diff --git a/source/blender/gpu/intern/gpu_texture_image.cc b/source/blender/gpu/intern/gpu_texture_image.cc index 5b53ba0544b..cd422c6ee04 100644 --- a/source/blender/gpu/intern/gpu_texture_image.cc +++ b/source/blender/gpu/intern/gpu_texture_image.cc @@ -54,7 +54,6 @@ #include "GPU_draw.h" #include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_platform.h" #include "GPU_texture.h" @@ -63,22 +62,14 @@ static void gpu_free_image(Image *ima, const bool immediate); static void gpu_free_unused_buffers(void); -static void gpu_create_gl_tex_compressed(unsigned int *bind, - eGPUTextureTarget textarget, - Image *ima, - ImBuf *ibuf); -static void gpu_create_gl_tex(uint *bind, - uint *rect, - float *frect, - int rectw, - int recth, - eGPUTextureTarget textarget, - bool mipmap, - bool half_float, - bool use_srgb, - Image *ima); - -//* Checking powers of two for images since OpenGL ES requires it */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utility functions + * \{ */ + +/** Checking powers of two for images since OpenGL ES requires it */ #ifdef WITH_DDS static bool is_power_of_2_resolution(int w, int h) { @@ -86,9 +77,9 @@ static bool is_power_of_2_resolution(int w, int h) } #endif -static bool is_over_resolution_limit(GLenum textarget, int w, int h) +static bool is_over_resolution_limit(int w, int h) { - int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size(); + int size = GPU_max_texture_size(); int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; return (w > reslimit || h > reslimit); @@ -106,108 +97,177 @@ static int smaller_power_of_2_limit(int num) return power_of_2_min_i(num); } -/* Current OpenGL state caching for GPU_set_tpage */ - -static struct GPUTextureState { - /* also controls min/mag filtering */ - bool domipmap; - /* only use when 'domipmap' is set */ - bool linearmipmap; - /* store this so that new images created while texture painting won't be set to mipmapped */ - bool texpaint; - - float anisotropic; -} GTS = {1, 0, 0, 1.0f}; - -/* Mipmap settings */ - -void GPU_set_mipmap(Main *bmain, bool mipmap) +static GPUTexture **gpu_get_image_gputexture(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) { - if (GTS.domipmap != mipmap) { - GPU_free_images(bmain); - GTS.domipmap = mipmap; + const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); + BLI_assert(in_range); + + if (in_range) { + return &(ima->gputexture[textarget][multiview_eye]); } + return NULL; } -void GPU_set_linear_mipmap(bool linear) +static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, + MovieClipUser *cuser, + eGPUTextureTarget textarget) { - if (GTS.linearmipmap != linear) { - GTS.linearmipmap = linear; + LISTBASE_FOREACH (MovieClip_RuntimeGPUTexture *, tex, &clip->runtime.gputextures) { + if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { + if (tex == NULL) { + tex = (MovieClip_RuntimeGPUTexture *)MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), + __func__); + + for (int i = 0; i < TEXTARGET_COUNT; i++) { + tex->gputexture[i] = NULL; + } + + memcpy(&tex->user, cuser, sizeof(MovieClipUser)); + BLI_addtail(&clip->runtime.gputextures, tex); + } + + return &tex->gputexture[textarget]; + } } + return NULL; } -bool GPU_get_mipmap(void) +/* Apply colormanagement and scale buffer if needed. */ +static void *get_ibuf_data(const Image *ima, + const ImBuf *ibuf, + const bool do_rescale, + const int rescale_size[2], + const bool compress_as_srgb, + bool *r_freebuf) { - return GTS.domipmap && !GTS.texpaint; -} + const bool is_float_rect = (ibuf->rect_float != NULL); + void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect; -bool GPU_get_linear_mipmap(void) -{ - return GTS.linearmipmap; -} + if (is_float_rect) { + /* Float image is already in scene linear colorspace or non-color data by + * convention, no colorspace conversion needed. But we do require 4 channels + * currently. */ + const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false; -static GLenum gpu_get_mipmap_filter(bool mag) -{ - /* linearmipmap is off by default *when mipmapping is off, - * use unfiltered display */ - if (mag) { - if (GTS.domipmap) { - return GL_LINEAR; - } - else { - return GL_NEAREST; + if (ibuf->channels != 4 || !store_premultiplied) { + data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + *r_freebuf = true; + + if (data_rect == NULL) { + return NULL; + } + + IMB_colormanagement_imbuf_to_float_texture( + (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); } } else { - if (GTS.domipmap) { - if (GTS.linearmipmap) { - return GL_LINEAR_MIPMAP_LINEAR; - } - else { - return GL_LINEAR_MIPMAP_NEAREST; + /* Byte image is in original colorspace from the file. If the file is sRGB + * scene linear, or non-color data no conversion is needed. Otherwise we + * compress as scene linear + sRGB transfer function to avoid precision loss + * in common cases. + * + * We must also convert to premultiplied for correct texture interpolation + * and consistency with float images. */ + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + data_rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + *r_freebuf = true; + + if (data_rect == NULL) { + return NULL; } - } - else { - return GL_NEAREST; + + /* Texture storage of images is defined by the alpha mode of the image. The + * downside of this is that there can be artifacts near alpha edges. However, + * this allows us to use sRGB texture formats and preserves color values in + * zero alpha areas, and appears generally closer to what game engines that we + * want to be compatible with do. */ + const bool store_premultiplied = ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true; + IMB_colormanagement_imbuf_to_byte_texture( + (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); } } -} -/* Anisotropic filtering settings */ -void GPU_set_anisotropic(float value) -{ - if (GTS.anisotropic != value) { - GPU_samplers_free(); + if (do_rescale) { + uint *rect = (is_float_rect) ? NULL : (uint *)data_rect; + float *rect_float = (is_float_rect) ? (float *)data_rect : NULL; - /* Clamp value to the maximum value the graphics card supports */ - const float max = GPU_max_texture_anisotropy(); - if (value > max) { - value = max; - } + ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4); + IMB_scaleImBuf(scale_ibuf, UNPACK2(rescale_size)); - GTS.anisotropic = value; - - GPU_samplers_init(); + data_rect = (is_float_rect) ? (void *)scale_ibuf->rect_float : (void *)scale_ibuf->rect; + *r_freebuf = true; + /* Steal the rescaled buffer to avoid double free. */ + scale_ibuf->rect_float = NULL; + scale_ibuf->rect = NULL; + IMB_freeImBuf(scale_ibuf); } + return data_rect; } -float GPU_get_anisotropic(void) +static void get_texture_format_from_ibuf(const Image *ima, + const ImBuf *ibuf, + eGPUDataFormat *r_data_format, + eGPUTextureFormat *r_texture_format) { - return GTS.anisotropic; + const bool float_rect = (ibuf->rect_float != NULL); + const bool high_bitdepth = (!(ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)); + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + + *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + if (float_rect) { + *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F; + } + else { + *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8; + } } -static GPUTexture **gpu_get_image_gputexture(Image *ima, int textarget, const int multiview_eye) +/* Return false if no suitable format was found. */ +static bool get_texture_compressed_format_from_ibuf(const ImBuf *ibuf, + eGPUTextureFormat *r_data_format) { - const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); - BLI_assert(in_range); +#ifdef WITH_DDS + /* For DDS we only support data, scene linear and sRGB. Converting to + * different colorspace would break the compression. */ + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); - if (in_range) { - return &(ima->gputexture[textarget][multiview_eye]); + if (ibuf->dds_data.fourcc == FOURCC_DXT1) { + *r_data_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1; } - return NULL; + else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { + *r_data_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { + *r_data_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5; + } + else { + return false; + } + return true; +#else + return false; +#endif } -static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) +static bool mipmap_enabled(void) +{ + /* This used to be a userpref option. Maybe it will be re-introduce late. */ + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UDIM gpu texture + * \{ */ + +static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) { GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; @@ -223,10 +283,6 @@ static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) int max_tile = last_tile->tile_number - 1001; /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode); - int width = max_tile + 1; float *data = (float *)MEM_callocN(width * 8 * sizeof(float), __func__); for (int i = 0; i < width; i++) { @@ -243,15 +299,12 @@ static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) tile_info[3] = tile->runtime.tilearray_size[1] / array_h; } - glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, width, 2, 0, GL_RGBA, GL_FLOAT, data); - MEM_freeN(data); + GPUTexture *tex = GPU_texture_create_1d_array(width, 2, GPU_RGBA32F, data, NULL); + GPU_texture_mipmap_mode(tex, false, false); - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glBindTexture(GL_TEXTURE_1D_ARRAY, 0); + MEM_freeN(data); - return bindcode; + return tex; } typedef struct PackTile { @@ -268,10 +321,9 @@ static int compare_packtile(const void *a, const void *b) return tile_a->pack_score < tile_b->pack_score; } -static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) { int arraywidth = 0, arrayheight = 0; - ListBase boxes = {NULL}; LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { @@ -286,8 +338,7 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) packtile->boxpack.w = ibuf->x; packtile->boxpack.h = ibuf->y; - if (is_over_resolution_limit( - GL_TEXTURE_2D_ARRAY, packtile->boxpack.w, packtile->boxpack.h)) { + if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) { packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); } @@ -330,38 +381,17 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) arraylayers++; } - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode); + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + get_texture_format_from_ibuf(ima, main_ibuf, &data_format, &tex_format); - GLenum data_type, internal_format; - if (main_ibuf->rect_float) { - data_type = GL_FLOAT; - internal_format = (!(main_ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)) ? - GL_RGBA32F : - GL_RGBA16F; - } - else { - data_type = GL_UNSIGNED_BYTE; - internal_format = GL_RGBA8; - if (!IMB_colormanagement_space_is_data(main_ibuf->rect_colorspace) && - !IMB_colormanagement_space_is_scene_linear(main_ibuf->rect_colorspace)) { - internal_format = GL_SRGB8_ALPHA8; - } - } + /* Create Texture. */ + GPUTexture *tex = GPU_texture_create_nD( + arraywidth, arrayheight, arraylayers, 2, NULL, tex_format, data_format, 0, false, NULL); - glTexImage3D(GL_TEXTURE_2D_ARRAY, - 0, - internal_format, - arraywidth, - arrayheight, - arraylayers, - 0, - GL_RGBA, - data_type, - NULL); + GPU_texture_bind(tex, 0); + /* Upload each tile one by one. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { int tilelayer = tile->runtime.tilearray_layer; int *tileoffset = tile->runtime.tilearray_offset; @@ -377,209 +407,226 @@ static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); if (ibuf) { - bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); - - ImBuf *scale_ibuf = NULL; - if (ibuf->rect_float) { - float *rect_float = ibuf->rect_float; - - const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - - float *pixeldata = rect_float; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(NULL, rect_float, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect_float; - } + const bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_FLOAT, - pixeldata); - - if (rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - } - else { - unsigned int *rect = ibuf->rect; - - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - rect = (uint *)MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect, - 0, - 0, - ibuf->x, - ibuf->y, - ibuf, - internal_format == GL_SRGB8_ALPHA8, - ima->alpha_mode == IMA_ALPHA_PREMUL); - } + void *pixeldata = get_ibuf_data( + ima, ibuf, needs_scale, tilesize, compress_as_srgb, &freebuf); + GPU_texture_update_sub( + tex, data_format, pixeldata, UNPACK2(tileoffset), tilelayer, UNPACK2(tilesize), 1); - unsigned int *pixeldata = rect; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(rect, NULL, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect; - } - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - pixeldata); - - if (rect != ibuf->rect) { - MEM_freeN(rect); - } - } - if (scale_ibuf != NULL) { - IMB_freeImBuf(scale_ibuf); + if (freebuf) { + MEM_SAFE_FREE(pixeldata); } } BKE_image_release_ibuf(ima, ibuf, NULL); } - if (GPU_get_mipmap()) { - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + if (mipmap_enabled()) { + GPU_texture_generate_mipmap(tex); if (ima) { ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; } } - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + GPU_texture_unbind(tex); - return bindcode; + return tex; } -static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, eGPUTextureTarget textarget) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular gpu texture + * \{ */ + +static GPUTexture *gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf) { - uint bindcode = 0; - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; + GPUTexture *tex = NULL; + bool do_rescale = is_over_resolution_limit(ibuf->x, ibuf->y); #ifdef WITH_DDS if (ibuf->ftype == IMB_FTYPE_DDS) { - /* DDS is loaded directly in compressed form. */ - gpu_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf); - return bindcode; + eGPUTextureFormat compressed_format; + if (!get_texture_compressed_format_from_ibuf(ibuf, &compressed_format)) { + fprintf(stderr, "Unable to find a suitable DXT compression,"); + } + else if (do_rescale) { + fprintf(stderr, "Unable to load DXT image resolution,"); + } + else if (!is_power_of_2_resolution(ibuf->x, ibuf->y)) { + fprintf(stderr, "Unable to load non-power-of-two DXT image resolution,"); + } + else { + tex = GPU_texture_create_compressed( + ibuf->x, ibuf->y, ibuf->dds_data.nummipmaps, compressed_format, ibuf->dds_data.data); + + if (tex != NULL) { + return tex; + } + else { + fprintf(stderr, "ST3C support not found,"); + } + } + /* Fallback to uncompressed texture. */ + fprintf(stderr, " falling back to uncompressed.\n"); } #else - (void)gpu_create_gl_tex_compressed; + (void)get_texture_compressed_format_from_ibuf; #endif - /* Regular uncompressed texture. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - bool compress_as_srgb = false; + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + get_texture_format_from_ibuf(ima, ibuf, &data_format, &tex_format); - if (rect_float == NULL) { - /* Byte image is in original colorspace from the file. If the file is sRGB - * scene linear, or non-color data no conversion is needed. Otherwise we - * compress as scene linear + sRGB transfer function to avoid precision loss - * in common cases. - * - * We must also convert to premultiplied for correct texture interpolation - * and consistency with float images. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace); + int size[2] = {ibuf->x, ibuf->y}; + if (do_rescale) { + size[0] = smaller_power_of_2_limit(size[0]); + size[1] = smaller_power_of_2_limit(size[1]); + } - rect = (uchar *)MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - if (rect == NULL) { - return bindcode; - } + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; - /* Texture storage of images is defined by the alpha mode of the image. The - * downside of this is that there can be artifacts near alpha edges. However, - * this allows us to use sRGB texture formats and preserves color values in - * zero alpha areas, and appears generally closer to what game engines that we - * want to be compatible with do. */ - const bool store_premultiplied = ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true; - IMB_colormanagement_imbuf_to_byte_texture( - rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); + void *data = get_ibuf_data(ima, ibuf, do_rescale, size, compress_as_srgb, &freebuf); + + /* Create Texture. */ + tex = GPU_texture_create_nD(UNPACK2(size), 0, 2, data, tex_format, data_format, 0, false, NULL); + + GPU_texture_anisotropic_filter(tex, true); + + if (mipmap_enabled()) { + GPU_texture_bind(tex, 0); + GPU_texture_generate_mipmap(tex); + GPU_texture_unbind(tex); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; } + GPU_texture_mipmap_mode(tex, true, true); } else { - /* Float image is already in scene linear colorspace or non-color data by - * convention, no colorspace conversion needed. But we do require 4 channels - * currently. */ - const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false; + GPU_texture_mipmap_mode(tex, false, true); + } - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - if (rect_float == NULL) { - return bindcode; - } - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } + if (freebuf) { + MEM_SAFE_FREE(data); } - /* Create OpenGL texture. */ - gpu_create_gl_tex(&bindcode, - (uint *)rect, - rect_float, - ibuf->x, - ibuf->y, - textarget, - mipmap, - half_float, - compress_as_srgb, - ima); + return tex; +} - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); +/* Get the GPUTexture for a given `Image`. + * + * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already + * available. It is also required when requesting the GPUTexture for a render result. */ +GPUTexture *GPU_texture_from_blender(Image *ima, + ImageUser *iuser, + ImBuf *ibuf, + eGPUTextureTarget textarget) +{ +#ifndef GPU_STANDALONE + if (ima == NULL) { + return NULL; } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); + + /* Free any unused GPU textures, since we know we are in a thread with OpenGL + * context and might as well ensure we have as much space free as possible. */ + gpu_free_unused_buffers(); + + /* currently, gpu refresh tagging is used by ima sequences */ + if (ima->gpuflag & IMA_GPU_REFRESH) { + gpu_free_image(ima, true); + ima->gpuflag &= ~IMA_GPU_REFRESH; + } + + /* Tag as in active use for garbage collector. */ + BKE_image_tag_time(ima); + + /* Test if we already have a texture. */ + GPUTexture **tex = gpu_get_image_gputexture(ima, textarget, iuser ? iuser->multiview_eye : 0); + if (*tex) { + return *tex; + } + + /* Check if we have a valid image. If not, we return a dummy + * texture with zero bindcode so we don't keep trying. */ + ImageTile *tile = BKE_image_get_tile(ima, 0); + if (tile == NULL || tile->ok == 0) { + *tex = GPU_texture_create_error(textarget); + return *tex; + } + + /* check if we have a valid image buffer */ + ImBuf *ibuf_intern = ibuf; + if (ibuf_intern == NULL) { + ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (ibuf_intern == NULL) { + *tex = GPU_texture_create_error(textarget); + return *tex; + } + } + + if (textarget == TEXTARGET_2D_ARRAY) { + *tex = gpu_texture_create_tile_array(ima, ibuf_intern); + } + else if (textarget == TEXTARGET_TILE_MAPPING) { + *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); + } + else { + *tex = gpu_texture_create_from_ibuf(ima, ibuf_intern); + } + + /* if `ibuf` was given, we don't own the `ibuf_intern` */ + if (ibuf == NULL) { + BKE_image_release_ibuf(ima, ibuf_intern, NULL); } - return bindcode; + GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); + + return *tex; +#endif + return NULL; } -static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, - MovieClipUser *cuser, - eGPUTextureTarget textarget) +GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, + MovieClipUser *cuser, + eGPUTextureTarget textarget) { - LISTBASE_FOREACH (MovieClip_RuntimeGPUTexture *, tex, &clip->runtime.gputextures) { - if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { - if (tex == NULL) { - tex = (MovieClip_RuntimeGPUTexture *)MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), - __func__); - - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tex->gputexture[i] = NULL; - } +#ifndef GPU_STANDALONE + if (clip == NULL) { + return NULL; + } - memcpy(&tex->user, cuser, sizeof(MovieClipUser)); - BLI_addtail(&clip->runtime.gputextures, tex); - } + GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, textarget); + if (*tex) { + return *tex; + } - return &tex->gputexture[textarget]; - } + /* check if we have a valid image buffer */ + ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); + if (ibuf == NULL) { + *tex = GPU_texture_create_error(textarget); + return *tex; } + + *tex = gpu_texture_create_from_ibuf(NULL, ibuf); + + IMB_freeImBuf(ibuf); + + return *tex; +#else return NULL; +#endif } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paint Update + * \{ */ + static ImBuf *update_do_scale(uchar *rect, float *rect_float, int *x, @@ -619,108 +666,70 @@ static ImBuf *update_do_scale(uchar *rect, return ibuf; } -static void gpu_texture_update_scaled_array(uchar *rect, - float *rect_float, - int full_w, - int full_h, - int x, - int y, - int layer, - const int *tile_offset, - const int *tile_size, - int w, - int h) +static void gpu_texture_update_scaled(GPUTexture *tex, + uchar *rect, + float *rect_float, + int full_w, + int full_h, + int x, + int y, + int layer, + const int *tile_offset, + const int *tile_size, + int w, + int h) { - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); - - /* Shift to account for tile packing. */ - x += tile_offset[0]; - y += tile_offset[1]; + ImBuf *ibuf; + if (layer > -1) { + ibuf = update_do_scale( + rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); - if (ibuf->rect_float) { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float); + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; } else { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} + /* Partial update with scaling. */ + int limit_w = smaller_power_of_2_limit(full_w); + int limit_h = smaller_power_of_2_limit(full_h); -static void gpu_texture_update_scaled( - uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h) -{ - /* Partial update with scaling. */ - int limit_w = smaller_power_of_2_limit(full_w); - int limit_h = smaller_power_of_2_limit(full_h); + ibuf = update_do_scale(rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); + } - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); + void *data = (ibuf->rect_float) ? (void *)(ibuf->rect_float) : (void *)(ibuf->rect); + eGPUDataFormat data_format = (ibuf->rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; - if (ibuf->rect_float) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } + GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1); IMB_freeImBuf(ibuf); } -static void gpu_texture_update_unscaled(uchar *rect, +static void gpu_texture_update_unscaled(GPUTexture *tex, + uchar *rect, float *rect_float, int x, int y, int layer, + const int tile_offset[2], int w, int h, - GLint tex_stride, - GLint tex_offset) + int tex_stride, + int tex_offset) { + if (layer > -1) { + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; + } + + void *data = (rect_float) ? (void *)(rect_float + tex_offset) : (void *)(rect + tex_offset); + eGPUDataFormat data_format = (rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + /* Partial update without scaling. Stride and offset are used to copy only a * subset of a possible larger buffer than what we are updating. */ GPU_unpack_row_length_set(tex_stride); - if (layer >= 0) { - if (rect_float == NULL) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - rect + tex_offset); - } - else { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_FLOAT, - rect_float + tex_offset); - } - } - else { - if (rect_float == NULL) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); - } - } - + GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1); /* Restore default. */ GPU_unpack_row_length_set(0); } @@ -738,7 +747,7 @@ static void gpu_texture_update_from_ibuf( scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); } else { - scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); + scaled = is_over_resolution_limit(ibuf->x, ibuf->y); } if (scaled) { @@ -755,8 +764,8 @@ static void gpu_texture_update_from_ibuf( /* Get texture data pointers. */ float *rect_float = ibuf->rect_float; uchar *rect = (uchar *)ibuf->rect; - GLint tex_stride = ibuf->x; - GLint tex_offset = ibuf->channels * (y * ibuf->x + x); + int tex_stride = ibuf->x; + int tex_offset = ibuf->channels * (y * ibuf->x + x); if (rect_float == NULL) { /* Byte pixels. */ @@ -803,11 +812,12 @@ static void gpu_texture_update_from_ibuf( int *tileoffset = tile->runtime.tilearray_offset; int *tilesize = tile->runtime.tilearray_size; int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_scaled_array( - rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); + gpu_texture_update_scaled( + tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); } else { - gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); + gpu_texture_update_scaled( + tex, rect, rect_float, ibuf->x, ibuf->y, x, y, -1, NULL, NULL, w, h); } } else { @@ -815,18 +825,12 @@ static void gpu_texture_update_from_ibuf( if (tile != NULL) { int *tileoffset = tile->runtime.tilearray_offset; int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_unscaled(rect, - rect_float, - x + tileoffset[0], - y + tileoffset[1], - tilelayer, - w, - h, - tex_stride, - tex_offset); + gpu_texture_update_unscaled( + tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset); } else { - gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset); + gpu_texture_update_unscaled( + tex, rect, rect_float, x, y, -1, NULL, w, h, tex_stride, tex_offset); } } @@ -838,8 +842,8 @@ static void gpu_texture_update_from_ibuf( MEM_freeN(rect_float); } - if (GPU_get_mipmap()) { - glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); + if (mipmap_enabled()) { + GPU_texture_generate_mipmap(tex); } else { ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; @@ -848,425 +852,30 @@ static void gpu_texture_update_from_ibuf( GPU_texture_unbind(tex); } -/* Get the GPUTexture for a given `Image`. - * - * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already - * available. It is also required when requesting the GPUTexture for a render result. */ -GPUTexture *GPU_texture_from_blender(Image *ima, - ImageUser *iuser, - ImBuf *ibuf, - eGPUTextureTarget textarget) -{ -#ifndef GPU_STANDALONE - if (ima == NULL) { - return NULL; - } - - /* Free any unused GPU textures, since we know we are in a thread with OpenGL - * context and might as well ensure we have as much space free as possible. */ - gpu_free_unused_buffers(); - - /* currently, gpu refresh tagging is used by ima sequences */ - if (ima->gpuflag & IMA_GPU_REFRESH) { - gpu_free_image(ima, true); - ima->gpuflag &= ~IMA_GPU_REFRESH; - } - - /* Tag as in active use for garbage collector. */ - BKE_image_tag_time(ima); - - /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_image_gputexture(ima, textarget, iuser ? iuser->multiview_eye : 0); - if (*tex) { - return *tex; - } - - /* Check if we have a valid image. If not, we return a dummy - * texture with zero bindcode so we don't keep trying. */ - uint bindcode = 0; - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (tile == NULL || tile->ok == 0) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - /* check if we have a valid image buffer */ - ImBuf *ibuf_intern = ibuf; - if (ibuf_intern == NULL) { - ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf_intern == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - } - - if (textarget == TEXTARGET_2D_ARRAY) { - bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); - } - else if (textarget == TEXTARGET_TILE_MAPPING) { - bindcode = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); - } - else { - bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget); - } - - /* if `ibuf` was given, we don't own the `ibuf_intern` */ - if (ibuf == NULL) { - BKE_image_release_ibuf(ima, ibuf_intern, NULL); - } - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - - GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); - - if (textarget == TEXTARGET_TILE_MAPPING) { - /* Special for tile mapping. */ - GPU_texture_mipmap_mode(*tex, false, false); - } - - return *tex; -#endif - return NULL; -} - -GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, - MovieClipUser *cuser, - eGPUTextureTarget textarget) +void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) { #ifndef GPU_STANDALONE - if (clip == NULL) { - return NULL; - } - - GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, textarget); - if (*tex) { - return *tex; - } - - /* check if we have a valid image buffer */ - uint bindcode = 0; - ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); - if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - bindcode = gpu_texture_create_from_ibuf(NULL, ibuf, textarget); - IMB_freeImBuf(ibuf); - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; -#else - return NULL; -#endif -} - -void GPU_free_texture_movieclip(struct MovieClip *clip) -{ - /* number of gpu textures to keep around as cache - * We don't want to keep too many GPU textures for - * movie clips around, as they can be large.*/ - const int MOVIECLIP_NUM_GPUTEXTURES = 1; - - while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { - MovieClip_RuntimeGPUTexture *tex = (MovieClip_RuntimeGPUTexture *)BLI_pophead( - &clip->runtime.gputextures); - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (tex->gputexture[i]) { - GPU_texture_free(tex->gputexture[i]); - tex->gputexture[i] = NULL; - } - } - MEM_freeN(tex); - } -} - -static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth) -{ - size_t block_size = frect ? sizeof(float[4]) : sizeof(uchar[4]); - void **sides = NULL; - int h = recth / 2; - int w = rectw / 3; - - if (w != h) { - return sides; - } - - /* PosX, NegX, PosY, NegY, PosZ, NegZ */ - sides = (void **)MEM_mallocN(sizeof(void *) * 6, ""); - for (int i = 0; i < 6; i++) { - sides[i] = MEM_mallocN(block_size * w * h, ""); - } - - /* divide image into six parts */ - /* ______________________ - * | | | | - * | NegX | NegY | PosX | - * |______|______|______| - * | | | | - * | NegZ | PosZ | PosY | - * |______|______|______| - */ - if (frect) { - float(*frectb)[4] = (float(*)[4])frect; - float(**fsides)[4] = (float(**)[4])sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - memcpy(&fsides[0][x * h + y], &frectb[(recth - y - 1) * rectw + 2 * w + x], block_size); - memcpy(&fsides[1][x * h + y], &frectb[(y + h) * rectw + w - 1 - x], block_size); - memcpy( - &fsides[3][y * w + x], &frectb[(recth - y - 1) * rectw + 2 * w - 1 - x], block_size); - memcpy(&fsides[5][y * w + x], &frectb[(h - y - 1) * rectw + w - 1 - x], block_size); - } - memcpy(&fsides[2][y * w], frectb[y * rectw + 2 * w], block_size * w); - memcpy(&fsides[4][y * w], frectb[y * rectw + w], block_size * w); - } - } - else { - uint **isides = (uint **)sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - isides[0][x * h + y] = rect[(recth - y - 1) * rectw + 2 * w + x]; - isides[1][x * h + y] = rect[(y + h) * rectw + w - 1 - x]; - isides[3][y * w + x] = rect[(recth - y - 1) * rectw + 2 * w - 1 - x]; - isides[5][y * w + x] = rect[(h - y - 1) * rectw + w - 1 - x]; - } - memcpy(&isides[2][y * w], &rect[y * rectw + 2 * w], block_size * w); - memcpy(&isides[4][y * w], &rect[y * rectw + w], block_size * w); - } - } - - return sides; -} - -static void gpu_del_cube_map(void **cube_map) -{ - int i; - if (cube_map == NULL) { - return; - } - for (i = 0; i < 6; i++) { - MEM_freeN(cube_map[i]); - } - MEM_freeN(cube_map); -} - -/* Image *ima can be NULL */ -static void gpu_create_gl_tex(uint *bind, - uint *rect, - float *frect, - int rectw, - int recth, - eGPUTextureTarget target, - bool mipmap, - bool half_float, - bool use_srgb, - Image *ima) -{ - GLenum textarget = (target == TEXTARGET_2D) ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP; - - ImBuf *ibuf = NULL; - - if (textarget == GL_TEXTURE_2D && is_over_resolution_limit(textarget, rectw, recth)) { - int tpx = rectw; - int tpy = recth; - rectw = smaller_power_of_2_limit(rectw); - recth = smaller_power_of_2_limit(recth); - - if (frect) { - ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - frect = ibuf->rect_float; - } - else { - ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - rect = ibuf->rect; - } - } - - /* create image */ - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - GLenum float_format = (!half_float && (ima && (ima->flag & IMA_HIGH_BITDEPTH))) ? GL_RGBA32F : - GL_RGBA16F; - GLenum internal_format = (frect) ? float_format : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8; - - if (textarget == GL_TEXTURE_2D) { - if (frect) { - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); - } - else { - glTexImage2D( - GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect); - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_2D); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - int w = rectw / 3, h = recth / 2; - - if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) { - void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth); - GLenum type = frect ? GL_FLOAT : GL_UNSIGNED_BYTE; - - if (cube_map) { - for (int i = 0; i < 6; i++) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, - 0, - internal_format, - w, - h, - 0, - GL_RGBA, - type, - cube_map[i]); - } - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - gpu_del_cube_map(cube_map); - } - else { - printf("Incorrect envmap size\n"); - } - } - - glBindTexture(textarget, 0); - - if (ibuf) { - IMB_freeImBuf(ibuf); - } -} - -/** - * Returns false if the provided ImBuf doesn't have a supported DXT compression format - */ -bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb, uint *bindcode) -{ -#ifdef WITH_DDS - glGenTextures(1, (GLuint *)bindcode); - glBindTexture(GL_TEXTURE_2D, *bindcode); - - GLint format = 0; - int blocksize, height, width, i, size, offset = 0; - - width = ibuf->x; - height = ibuf->y; - - if (GLEW_EXT_texture_compression_s3tc) { - if (ibuf->dds_data.fourcc == FOURCC_DXT1) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } - } + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - if (format == 0) { - fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n"); - glDeleteTextures(1, (GLuint *)bindcode); - return false; + if ((ibuf == NULL) || (w == 0) || (h == 0)) { + /* Full reload of texture. */ + GPU_free_image(ima); } - if (!is_power_of_2_resolution(width, height)) { - fprintf( - stderr, - "Unable to load non-power-of-two DXT image resolution, falling back to uncompressed\n"); - glDeleteTextures(1, (GLuint *)bindcode); - return false; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - - /* Reset to opengl Defaults. (Untested, might not be needed) */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - - blocksize = (ibuf->dds_data.fourcc == FOURCC_DXT1) ? 8 : 16; - for (i = 0; i < ibuf->dds_data.nummipmaps && (width || height); i++) { - if (width == 0) { - width = 1; - } - if (height == 0) { - height = 1; - } - - size = ((width + 3) / 4) * ((height + 3) / 4) * blocksize; - - glCompressedTexImage2D( - GL_TEXTURE_2D, i, format, width, height, 0, size, ibuf->dds_data.data + offset); - - offset += size; - width >>= 1; - height >>= 1; + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } - /* Restore Blender default. */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - /* set number of mipmap levels we have, needed in case they don't go down to 1x1 */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1); - glBindTexture(GL_TEXTURE_2D, 0); - return true; -#else - UNUSED_VARS(ibuf, use_srgb, bindcode); - return false; -#endif -} - -static void gpu_create_gl_tex_compressed(unsigned int *bind, - eGPUTextureTarget textarget, - Image *ima, - ImBuf *ibuf) -{ - /* For DDS we only support data, scene linear and sRGB. Converting to - * different colorspace would break the compression. */ - const bool use_srgb = !(IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifndef WITH_DDS - (void)ibuf; - /* Fall back to uncompressed if DDS isn't enabled */ - gpu_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); -#else - - if (textarget == TEXTARGET_2D) { - if (GPU_upload_dxt_texture(ibuf, use_srgb, bind)) { - /* All went fine! */ - return; - } - } - /* Fallback. */ - gpu_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); + BKE_image_release_ibuf(ima, ibuf, NULL); #endif } @@ -1277,95 +886,40 @@ static void gpu_create_gl_tex_compressed(unsigned int *bind, void GPU_paint_set_mipmap(Main *bmain, bool mipmap) { #ifndef GPU_STANDALONE - if (!GTS.domipmap) { - return; - } - - GTS.texpaint = !mipmap; - - if (mipmap) { - - LISTBASE_FOREACH (Image *, ima, &bmain->images) { - if (BKE_image_has_opengl_texture(ima)) { - if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - GPU_free_image(ima); - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else { - LISTBASE_FOREACH (Image *, ima, &bmain->images) { - if (BKE_image_has_opengl_texture(ima)) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if (BKE_image_has_opengl_texture(ima)) { + if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { GPUTexture *tex = ima->gputexture[a][eye]; if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); + GPU_texture_mipmap_mode(tex, mipmap, true); } } } } } else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + GPU_free_image(ima); } } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } } #endif /* GPU_STANDALONE */ } -void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) -{ -#ifndef GPU_STANDALONE - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); +/** \} */ - if ((ibuf == NULL) || (w == 0) || (h == 0)) { - /* Full reload of texture. */ - GPU_free_image(ima); - } - - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; - /* Check if we need to update the main gputexture. */ - if (tex != NULL && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); - } - - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; - if (tex != NULL) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); - } - - BKE_image_release_ibuf(ima, ibuf, NULL); -#endif -} +/* -------------------------------------------------------------------- */ +/** \name Delayed GPU texture free + * + * Image datablocks can be deleted by any thread, but there may not be any active OpenGL context. + * In that case we push them into a queue and free the buffers later. + * \{ */ -/* Delayed GPU texture free. Image datablocks can be deleted by any thread, - * but there may not be any active OpenGL context. In that case we push them - * into a queue and free the buffers later. */ static LinkNode *gpu_texture_free_queue = NULL; static ThreadMutex gpu_texture_queue_mutex = BLI_MUTEX_INITIALIZER; @@ -1388,6 +942,12 @@ static void gpu_free_unused_buffers() BLI_mutex_unlock(&gpu_texture_queue_mutex); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deletion + * \{ */ + static void gpu_free_image(Image *ima, const bool immediate) { for (int eye = 0; eye < 2; eye++) { @@ -1422,6 +982,27 @@ void GPU_free_image(Image *ima) gpu_free_image(ima, BLI_thread_is_main()); } +void GPU_free_texture_movieclip(struct MovieClip *clip) +{ + /* number of gpu textures to keep around as cache + * We don't want to keep too many GPU textures for + * movie clips around, as they can be large.*/ + const int MOVIECLIP_NUM_GPUTEXTURES = 1; + + while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { + MovieClip_RuntimeGPUTexture *tex = (MovieClip_RuntimeGPUTexture *)BLI_pophead( + &clip->runtime.gputextures); + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* free glsl image binding */ + if (tex->gputexture[i]) { + GPU_texture_free(tex->gputexture[i]); + tex->gputexture[i] = NULL; + } + } + MEM_freeN(tex); + } +} + void GPU_free_images(Main *bmain) { if (bmain) { @@ -1478,3 +1059,5 @@ void GPU_free_images_old(Main *bmain) } } } + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_texture_smoke.cc b/source/blender/gpu/intern/gpu_texture_smoke.cc index 0f5aa361fa7..13273dba444 100644 --- a/source/blender/gpu/intern/gpu_texture_smoke.cc +++ b/source/blender/gpu/intern/gpu_texture_smoke.cc @@ -36,7 +36,6 @@ #include "BKE_colorband.h" #include "GPU_draw.h" -#include "GPU_glew.h" #include "GPU_texture.h" #ifdef WITH_FLUID diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 20357fc1099..7be244d7ddc 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -125,8 +125,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 4 = TEXTARGET_COUNT, 2 = stereo eyes. */ - struct GPUTexture *gputexture[4][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ + struct GPUTexture *gputexture[3][2]; /* sources from: */ ListBase anims; diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index d750a7f3148..063e5d8af09 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -64,8 +64,8 @@ typedef struct MovieClipProxy { typedef struct MovieClip_RuntimeGPUTexture { void *next, *prev; MovieClipUser user; - /** Not written in file 4 = TEXTARGET_COUNT. */ - struct GPUTexture *gputexture[4]; + /** Not written in file 3 = TEXTARGET_COUNT. */ + struct GPUTexture *gputexture[3]; } MovieClip_RuntimeGPUTexture; typedef struct MovieClip_Runtime { diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index bfc020afb41..b13b9f5c11d 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -190,6 +190,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "GPU_draw.h" # include "GPU_extensions.h" # include "GPU_select.h" +# include "GPU_texture.h" # include "BLF_api.h" @@ -363,7 +364,8 @@ static void rna_userdef_load_ui_update(Main *UNUSED(bmain), Scene *UNUSED(scene) static void rna_userdef_anisotropic_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - GPU_set_anisotropic(U.anisotropic_filter); + GPU_samplers_free(); + GPU_samplers_init(); rna_userdef_update(bmain, scene, ptr); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c index 0cf4b51f307..771b7d5920a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -59,7 +59,8 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, NodeTexImage *tex_original = node_original->storage; ImageUser *iuser = &tex_original->iuser; eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - if (GPU_get_mipmap()) { + /* TODO(fclem) For now assume mipmap is always enabled. */ + if (true) { sampler |= GPU_SAMPLER_MIPMAP; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index cbda72cd228..ff2ecf785d7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -95,7 +95,8 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (tex->interpolation != SHD_INTERP_CLOSEST) { sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - sampler_state |= GPU_get_mipmap() ? GPU_SAMPLER_MIPMAP : 0; + /* TODO(fclem) For now assume mipmap is always enabled. */ + sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0; } const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 945d5fd42e4..a757b0fabd2 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -171,7 +171,7 @@ void WM_init_state_start_with_console_set(bool value) */ static bool opengl_is_init = false; -void WM_init_opengl(Main *bmain) +void WM_init_opengl(Main *UNUSED(bmain)) { /* must be called only once */ BLI_assert(opengl_is_init == false); @@ -185,9 +185,6 @@ void WM_init_opengl(Main *bmain) DRW_opengl_context_create(); GPU_init(); - GPU_set_mipmap(bmain, true); - GPU_set_linear_mipmap(true); - GPU_set_anisotropic(U.anisotropic_filter); GPU_pass_cache_init(); |