diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2022-02-24 23:40:16 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2022-02-25 00:51:19 +0300 |
commit | e59f754c169d855110296a365d93c33e82333385 (patch) | |
tree | e42f9ad35ae0fbd4edf87e8d5d5f1eac2b46a3af | |
parent | c23ee6d7b4d6665aed4d0440153f0b1e80b77c8b (diff) |
GPUTexture: Use immutable storage
This means textures need to have the number of mipmap levels specified
upfront. It does not mean the data is immutable.
There is fallback code for OpenGL < 4.2.
Immutable storage will enables texture views in the future.
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_depth_of_field.c | 5 | ||||
-rw-r--r-- | source/blender/draw/intern/DRW_gpu_wrapper.hh | 5 | ||||
-rw-r--r-- | source/blender/editors/screen/glutil.c | 3 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture.cc | 26 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture_private.hh | 8 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_backend.cc | 3 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_context.hh | 1 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_texture.cc | 140 | ||||
-rw-r--r-- | source/blender/gpu/opengl/gl_texture.hh | 2 |
9 files changed, 100 insertions, 93 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index 39cfbb40318..ef4d88bd521 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -626,11 +626,6 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl, "dof_reduced_color", UNPACK2(res), mip_count, GPU_RGBA16F, NULL); txl->dof_reduced_coc = GPU_texture_create_2d( "dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL); - - /* TODO(@fclem): Remove once we have immutable storage or when mips are generated on creation. - */ - GPU_texture_generate_mipmap(txl->dof_reduced_color); - GPU_texture_generate_mipmap(txl->dof_reduced_coc); } GPU_framebuffer_ensure_config(&fbl->dof_reduce_fb, diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index f387d5371b5..bce001659b2 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -641,11 +641,6 @@ class Texture : NonCopyable { } if (tx_ == nullptr) { tx_ = create(w, h, d, mips, format, data, layered, cubemap); - if (mips > 1) { - /* TODO(@fclem): Remove once we have immutable storage or when mips are - * generated on creation. */ - GPU_texture_generate_mipmap(tx_); - } return true; } return false; diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index f43bc08151a..8a84f4cf079 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -77,8 +77,9 @@ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state, * filtering results. Mipmaps can be used to get better results (i.e. #GL_LINEAR_MIPMAP_LINEAR), * so always use mipmaps when filtering. */ const bool use_mipmap = use_filter && ((draw_width < img_w) || (draw_height < img_h)); + const int mips = use_mipmap ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, 1, gpu_format, NULL); + GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, mips, gpu_format, NULL); const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F); eGPUDataFormat gpu_data_format = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE; diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index d84f420f35b..507ad47e36a 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -52,11 +52,13 @@ Texture::~Texture() #endif } -bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) +bool Texture::init_1D(int w, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = layers; d_ = 0; + int mips_max = 1 + floorf(log2f(w)); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_1D_ARRAY : GPU_TEXTURE_1D; @@ -66,11 +68,13 @@ bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format) +bool Texture::init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = h; d_ = layers; + int mips_max = 1 + floorf(log2f(max_ii(w, h))); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_2D_ARRAY : GPU_TEXTURE_2D; @@ -80,11 +84,13 @@ bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format) +bool Texture::init_3D(int w, int h, int d, int mips, eGPUTextureFormat format) { w_ = w; h_ = h; d_ = d; + int mips_max = 1 + floorf(log2f(max_iii(w, h, d))); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = GPU_TEXTURE_3D; @@ -94,11 +100,13 @@ bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_cubemap(int w, int layers, eGPUTextureFormat format) +bool Texture::init_cubemap(int w, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = w; d_ = max_ii(1, layers) * 6; + int mips_max = 1 + floorf(log2f(w)); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_CUBE_ARRAY : GPU_TEXTURE_CUBE; @@ -198,18 +206,18 @@ static inline GPUTexture *gpu_texture_create(const char *name, switch (type) { case GPU_TEXTURE_1D: case GPU_TEXTURE_1D_ARRAY: - success = tex->init_1D(w, h, tex_format); + success = tex->init_1D(w, h, mips, tex_format); break; case GPU_TEXTURE_2D: case GPU_TEXTURE_2D_ARRAY: - success = tex->init_2D(w, h, d, tex_format); + success = tex->init_2D(w, h, d, mips, tex_format); break; case GPU_TEXTURE_3D: - success = tex->init_3D(w, h, d, tex_format); + success = tex->init_3D(w, h, d, mips, tex_format); break; case GPU_TEXTURE_CUBE: case GPU_TEXTURE_CUBE_ARRAY: - success = tex->init_cubemap(w, d, tex_format); + success = tex->init_cubemap(w, d, mips, tex_format); break; default: break; @@ -287,7 +295,7 @@ GPUTexture *GPU_texture_create_compressed_2d( const char *name, int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) { Texture *tex = GPUBackend::get()->texture_alloc(name); - bool success = tex->init_2D(w, h, 0, tex_format); + bool success = tex->init_2D(w, h, 0, miplen, tex_format); if (!success) { delete tex; diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 2c57c467bcf..b019235e463 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -101,10 +101,10 @@ class Texture { virtual ~Texture(); /* Return true on success. */ - bool init_1D(int w, int layers, eGPUTextureFormat format); - bool init_2D(int w, int h, int layers, eGPUTextureFormat format); - bool init_3D(int w, int h, int d, eGPUTextureFormat format); - bool init_cubemap(int w, int layers, eGPUTextureFormat format); + bool init_1D(int w, int layers, int mips, eGPUTextureFormat format); + bool init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format); + bool init_3D(int w, int h, int d, int mips, eGPUTextureFormat format); + bool init_cubemap(int w, int layers, int mips, eGPUTextureFormat format); bool init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format); virtual void generate_mipmap() = 0; diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 7a1674a5f01..302d8249914 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -240,6 +240,7 @@ static void detect_workarounds() GLContext::texture_cube_map_array_support = false; GLContext::texture_filter_anisotropic_support = false; GLContext::texture_gather_support = false; + GLContext::texture_storage_support = false; GLContext::vertex_attrib_binding_support = false; return; } @@ -439,6 +440,7 @@ bool GLContext::shader_draw_parameters_support = false; bool GLContext::texture_cube_map_array_support = false; bool GLContext::texture_filter_anisotropic_support = false; bool GLContext::texture_gather_support = false; +bool GLContext::texture_storage_support = false; bool GLContext::vertex_attrib_binding_support = false; /** Workarounds. */ @@ -501,6 +503,7 @@ void GLBackend::capabilities_init() GLContext::texture_cube_map_array_support = GLEW_ARB_texture_cube_map_array; GLContext::texture_filter_anisotropic_support = GLEW_EXT_texture_filter_anisotropic; GLContext::texture_gather_support = GLEW_ARB_texture_gather; + GLContext::texture_storage_support = GLEW_VERSION_4_3; GLContext::vertex_attrib_binding_support = GLEW_ARB_vertex_attrib_binding; detect_workarounds(); diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index fe2ad0a4747..369b667cbcc 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -64,6 +64,7 @@ class GLContext : public Context { static bool texture_cube_map_array_support; static bool texture_filter_anisotropic_support; static bool texture_gather_support; + static bool texture_storage_support; static bool vertex_attrib_binding_support; /** Workarounds. */ diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 22af2dd463f..37bf2f6b8b3 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -69,9 +69,79 @@ bool GLTexture::init_internal() return false; } - this->ensure_mipmaps(0); + GLenum internal_format = to_gl_internal_format(format_); + const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE); + const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY); + const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY); + const int dimensions = (is_cubemap) ? 2 : this->dimensions_count(); + GLenum gl_format = to_gl_data_format(format_); + GLenum gl_type = to_gl(to_data_format(format_)); - /* Avoid issue with incomplete textures. */ + auto mip_size = [&](int h, int w = 1, int d = 1) -> size_t { + return divide_ceil_u(w, 4) * divide_ceil_u(h, 4) * divide_ceil_u(d, 4) * + to_block_size(format_); + }; + switch (dimensions) { + default: + case 1: + if (GLContext::texture_storage_support) { + glTexStorage1D(target_, mipmaps_, internal_format, w_); + } + else { + for (int i = 0, w = w_; i < mipmaps_; i++) { + if (is_compressed) { + glCompressedTexImage1D(target_, i, internal_format, w, 0, mip_size(w), nullptr); + } + else { + glTexImage1D(target_, i, internal_format, w, 0, gl_format, gl_type, nullptr); + } + w = max_ii(1, (w / 2)); + } + } + break; + case 2: + if (GLContext::texture_storage_support) { + glTexStorage2D(target_, mipmaps_, internal_format, w_, h_); + } + else { + for (int i = 0, w = w_, h = h_; i < mipmaps_; i++) { + for (int f = 0; f < (is_cubemap ? 6 : 1); f++) { + GLenum target = (is_cubemap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + f : target_; + if (is_compressed) { + glCompressedTexImage2D(target, i, internal_format, w, h, 0, mip_size(w, h), nullptr); + } + else { + glTexImage2D(target, i, internal_format, w, h, 0, gl_format, gl_type, nullptr); + } + } + w = max_ii(1, (w / 2)); + h = is_layered ? h_ : max_ii(1, (h / 2)); + } + } + break; + case 3: + if (GLContext::texture_storage_support) { + glTexStorage3D(target_, mipmaps_, internal_format, w_, h_, d_); + } + else { + for (int i = 0, w = w_, h = h_, d = d_; i < mipmaps_; i++) { + if (is_compressed) { + glCompressedTexImage3D( + target_, i, internal_format, w, h, d, 0, mip_size(w, h, d), nullptr); + } + else { + glTexImage3D(target_, i, internal_format, w, h, d, 0, gl_format, gl_type, nullptr); + } + w = max_ii(1, (w / 2)); + h = max_ii(1, (h / 2)); + d = is_layered ? d_ : max_ii(1, (d / 2)); + } + } + break; + } + this->mip_range_set(0, mipmaps_ - 1); + + /* Avoid issue with formats not supporting filtering. Nearest by default. */ if (GLContext::direct_state_access_support) { glTextureParameteri(tex_id_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } @@ -105,67 +175,6 @@ bool GLTexture::init_internal(GPUVertBuf *vbo) return true; } -void GLTexture::ensure_mipmaps(int miplvl) -{ - int effective_h = (type_ == GPU_TEXTURE_1D_ARRAY) ? 0 : h_; - int effective_d = (type_ != GPU_TEXTURE_3D) ? 0 : d_; - int max_dimension = max_iii(w_, effective_h, effective_d); - int max_miplvl = floor(log2(max_dimension)); - miplvl = min_ii(miplvl, max_miplvl); - - while (mipmaps_ < miplvl) { - int mip = ++mipmaps_; - const int dimensions = this->dimensions_count(); - - int w = mip_width_get(mip); - int h = mip_height_get(mip); - int d = mip_depth_get(mip); - GLenum internal_format = to_gl_internal_format(format_); - GLenum gl_format = to_gl_data_format(format_); - GLenum gl_type = to_gl(to_data_format(format_)); - - GLContext::state_manager_active_get()->texture_bind_temp(this); - - if (type_ == GPU_TEXTURE_CUBE) { - for (int i = 0; i < d; i++) { - GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; - glTexImage2D(target, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr); - } - } - else if (format_flag_ & GPU_FORMAT_COMPRESSED) { - size_t size = ((w + 3) / 4) * ((h + 3) / 4) * to_block_size(format_); - switch (dimensions) { - default: - case 1: - glCompressedTexImage1D(target_, mip, internal_format, w, 0, size, nullptr); - break; - case 2: - glCompressedTexImage2D(target_, mip, internal_format, w, h, 0, size, nullptr); - break; - case 3: - glCompressedTexImage3D(target_, mip, internal_format, w, h, d, 0, size, nullptr); - break; - } - } - else { - switch (dimensions) { - default: - case 1: - glTexImage1D(target_, mip, internal_format, w, 0, gl_format, gl_type, nullptr); - break; - case 2: - glTexImage2D(target_, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr); - break; - case 3: - glTexImage3D(target_, mip, internal_format, w, h, d, 0, gl_format, gl_type, nullptr); - break; - } - } - } - - this->mip_range_set(0, mipmaps_); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -216,9 +225,7 @@ void GLTexture::update_sub( BLI_assert(validate_data_format(format_, type)); BLI_assert(data != nullptr); - this->ensure_mipmaps(mip); - - if (mip > mipmaps_) { + if (mip >= mipmaps_) { debug::raise_gl_error("Updating a miplvl on a texture too small to have this many levels."); return; } @@ -283,7 +290,6 @@ void GLTexture::update_sub( */ void GLTexture::generate_mipmap() { - this->ensure_mipmaps(9999); /* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789). * In this case we just create a complete texture with mipmaps manually without * down-sampling. You must initialize the texture levels using other methods like diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 07d3bb8c946..9e128da90e8 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -77,8 +77,6 @@ class GLTexture : public Texture { private: bool proxy_check(int mip); - /** Will create enough mipmaps up to get to the given level. */ - void ensure_mipmaps(int mip); void update_sub_direct_state_access( int mip, int offset[3], int extent[3], GLenum gl_format, GLenum gl_type, const void *data); GPUFrameBuffer *framebuffer_get(); |