Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2020-09-05 18:29:51 +0300
committerClément Foucault <foucault.clem@gmail.com>2020-09-05 18:49:14 +0300
commitc766d9b9dc5661693a58e01a3637f15197c2fe59 (patch)
tree6905f0fc085af0eff5cfdf74d87c4ebed412e741 /source/blender/gpu/opengl
parentdb21c12abedd7606a3aaf50f70e506a24d9f0e7a (diff)
GPUTexture: GL Backend Isolation
This is a massive cleanup needed for vulkan support T68990. It provides: - More meaningful enums with conversion functions. - Less hacky supports of arrays and cubemaps (all considered layered). - More inline with the stateless design of vulkan and modern GL. - Methods Fallbacks are using framebuffer functions that are wrapped instead of implementing inside the texture module. What is not in there: - API change. - Samplers support (breaks a few effects). # Conflicts: # source/blender/gpu/GPU_texture.h
Diffstat (limited to 'source/blender/gpu/opengl')
-rw-r--r--source/blender/gpu/opengl/gl_context.hh8
-rw-r--r--source/blender/gpu/opengl/gl_debug.cc5
-rw-r--r--source/blender/gpu/opengl/gl_debug.hh1
-rw-r--r--source/blender/gpu/opengl/gl_state.cc87
-rw-r--r--source/blender/gpu/opengl/gl_state.hh22
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc357
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh218
7 files changed, 665 insertions, 33 deletions
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index bc7e2060804..9e6359fabad 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -30,6 +30,8 @@
#include "BLI_set.hh"
#include "BLI_vector.hh"
+#include "gl_state.hh"
+
#include "glew-mx.h"
#include <mutex>
@@ -83,6 +85,12 @@ class GLContext : public GPUContext {
void activate(void) override;
void deactivate(void) override;
+ static inline GLStateManager *state_manager_active_get()
+ {
+ GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get());
+ return static_cast<GLStateManager *>(ctx->state_manager);
+ };
+
/* TODO(fclem) these needs to become private. */
public:
void orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id);
diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc
index fc4dc3db2b6..da1f40e6417 100644
--- a/source/blender/gpu/opengl/gl_debug.cc
+++ b/source/blender/gpu/opengl/gl_debug.cc
@@ -202,6 +202,11 @@ void check_gl_resources(const char *info)
}
}
+void raise_gl_error(const char *msg)
+{
+ debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL);
+}
+
/** \} */
} // namespace blender::gpu::debug
diff --git a/source/blender/gpu/opengl/gl_debug.hh b/source/blender/gpu/opengl/gl_debug.hh
index dd98505ebc1..5537147d0fe 100644
--- a/source/blender/gpu/opengl/gl_debug.hh
+++ b/source/blender/gpu/opengl/gl_debug.hh
@@ -37,6 +37,7 @@ namespace debug {
# define GL_CHECK_RESOURCES(info)
#endif
+void raise_gl_error(const char *info);
void check_gl_error(const char *info);
void check_gl_resources(const char *info);
void init_gl_callbacks(void);
diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc
index 8f01ff13486..1fd39cf80eb 100644
--- a/source/blender/gpu/opengl/gl_state.cc
+++ b/source/blender/gpu/opengl/gl_state.cc
@@ -28,9 +28,11 @@
#include "gl_context.hh"
#include "gl_framebuffer.hh"
+#include "gl_texture.hh"
+
#include "gl_state.hh"
-using namespace blender::gpu;
+namespace blender::gpu {
/* -------------------------------------------------------------------- */
/** \name GLStateManager
@@ -69,6 +71,7 @@ void GLStateManager::apply_state(void)
{
this->set_state(this->state);
this->set_mutable_state(this->mutable_state);
+ this->texture_bind_apply();
active_fb->apply_state();
};
@@ -419,3 +422,85 @@ void GLStateManager::set_blend(const eGPUBlend value)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture state managment
+ * \{ */
+
+void GLStateManager::texture_bind(Texture *tex_, eGPUSamplerState sampler, int unit)
+{
+ BLI_assert(unit < GPU_max_textures());
+ GLTexture *tex = static_cast<GLTexture *>(tex_);
+ targets_[unit] = tex->target_;
+ textures_[unit] = tex->tex_id_;
+ samplers_[unit] = sampler;
+ tex->is_bound_ = true;
+ dirty_texture_binds_ |= 1 << unit;
+}
+
+/* Bind the texture to slot 0 for editing purpose. Used by legacy pipeline. */
+void GLStateManager::texture_bind_temp(GLTexture *tex)
+{
+ // BLI_assert(!GLEW_ARB_direct_state_access);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(tex->target_, tex->tex_id_);
+ /* Will reset the first texture that was originaly bound to slot 0 back before drawing. */
+ dirty_texture_binds_ |= 1;
+ /* NOTE: This might leave this texture attached to this target even after update.
+ * In practice it is not causing problems as we have incorrect binding detection
+ * at higher level. */
+}
+
+void GLStateManager::texture_unbind(Texture *tex_)
+{
+ GLTexture *tex = static_cast<GLTexture *>(tex_);
+ if (!tex->is_bound_) {
+ return;
+ }
+
+ GLuint tex_id = tex->tex_id_;
+ for (int i = 0; i < ARRAY_SIZE(textures_); i++) {
+ if (textures_[i] == tex_id) {
+ textures_[i] = 0;
+ dirty_texture_binds_ |= 1 << i;
+ }
+ }
+ tex->is_bound_ = false;
+}
+
+void GLStateManager::texture_unbind_all(void)
+{
+ for (int i = 0; i < ARRAY_SIZE(textures_); i++) {
+ if (textures_[i] != 0) {
+ textures_[i] = 0;
+ dirty_texture_binds_ |= 1 << i;
+ }
+ }
+ this->texture_bind_apply();
+}
+
+void GLStateManager::texture_bind_apply(void)
+{
+ if (dirty_texture_binds_ == 0) {
+ return;
+ }
+
+ if (false) {
+ /* TODO multibind */
+ }
+ else {
+ uint64_t dirty_bind = dirty_texture_binds_;
+ for (int unit = 0; dirty_bind != 0; dirty_bind >>= 1, unit++) {
+ if (dirty_bind & 1) {
+ glActiveTexture(GL_TEXTURE0 + unit);
+ glBindTexture(targets_[unit], textures_[unit]);
+ // glBindSampler(unit, samplers_[unit]);
+ }
+ }
+ dirty_texture_binds_ = 0;
+ }
+}
+
+/** \} */
+
+} // namespace blender::gpu \ No newline at end of file
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index c25e384fcd7..2befad690f0 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -20,6 +20,8 @@
* \ingroup gpu
*/
+#pragma once
+
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
@@ -32,6 +34,7 @@ namespace blender {
namespace gpu {
class GLFrameBuffer;
+class GLTexture;
/**
* State manager keeping track of the draw state and applying it before drawing.
@@ -49,11 +52,28 @@ class GLStateManager : public GPUStateManager {
/** Limits. */
float line_width_range_[2];
+ /** Texture state:
+ * We keep the full stack of textures and sampler bounds to use multi bind, and to be able to
+ * edit and restore texture binds on the fly without querying the context.
+ * Also this allows us to keep track of textures bounds to many texture units.
+ * Keep the targets to know what target to set to 0 for unbinding (legacy).
+ * Init first target to GL_TEXTURE_2D for texture_bind_temp to work.
+ */
+ GLuint targets_[64] = {GL_TEXTURE_2D};
+ GLuint textures_[64] = {0};
+ GLuint samplers_[64] = {0};
+ uint64_t dirty_texture_binds_ = 0;
+
public:
GLStateManager();
void apply_state(void) override;
+ void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) override;
+ void texture_bind_temp(GLTexture *tex);
+ void texture_unbind(Texture *tex) override;
+ void texture_unbind_all(void) override;
+
private:
static void set_write_mask(const eGPUWriteMask value);
static void set_depth_test(const eGPUDepthTest value);
@@ -70,6 +90,8 @@ class GLStateManager : public GPUStateManager {
void set_state(const GPUState &state);
void set_mutable_state(const GPUStateMutable &state);
+ void texture_bind_apply(void);
+
MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager")
};
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index aaad281ed4e..7c72dc6a5c3 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -23,7 +23,12 @@
#include "BKE_global.h"
+#include "GPU_extensions.h"
+#include "GPU_framebuffer.h"
+
#include "gl_backend.hh"
+#include "gl_debug.hh"
+#include "gl_state.hh"
#include "gl_texture.hh"
@@ -38,23 +43,153 @@ GLTexture::GLTexture(const char *name) : Texture(name)
BLI_assert(GPU_context_active_get() != NULL);
glGenTextures(1, &tex_id_);
+}
+
+GLTexture::~GLTexture()
+{
+ if (framebuffer_) {
+ GPU_framebuffer_free(framebuffer_);
+ }
+ GPUContext *ctx = GPU_context_active_get();
+ if (ctx != NULL && is_bound_) {
+ /* This avoid errors when the texture is still inside the bound texture array. */
+ ctx->state_manager->texture_unbind(this);
+ }
+ GLBackend::get()->tex_free(tex_id_);
+}
+
+/* Return true on success. */
+bool GLTexture::init_internal(void)
+{
+ if ((format_ == GPU_DEPTH24_STENCIL8) && GPU_depth_blitting_workaround()) {
+ /* MacOS + Radeon Pro fails to blit depth on GPU_DEPTH24_STENCIL8
+ * but works on GPU_DEPTH32F_STENCIL8. */
+ format_ = GPU_DEPTH32F_STENCIL8;
+ }
+
+ if ((type_ == GPU_TEXTURE_CUBE_ARRAY) && !GPU_arb_texture_cube_map_array_is_supported()) {
+ debug::raise_gl_error("Attempt to create a cubemap array without hardware support!");
+ return false;
+ }
+
+ target_ = to_gl_target(type_);
+
+ /* TODO(fclem) Proxy check. */
+
+ this->ensure_mipmaps(0);
+
+ /* Avoid issue with incomplete textures. */
+ if (false) {
+ /* TODO(fclem) Direct State Access. */
+ }
+ else {
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+ glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
#ifndef __APPLE__
if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
char sh_name[64];
- SNPRINTF(sh_name, "Texture-%s", name);
+ SNPRINTF(sh_name, "Texture-%s", name_);
+ /* Binding before setting the label is needed on some drivers. */
glObjectLabel(GL_TEXTURE, tex_id_, -1, sh_name);
}
#endif
+
+ GL_CHECK_ERROR("Post-texture creation");
+ return true;
}
-GLTexture::~GLTexture()
+/* Return true on success. */
+bool GLTexture::init_internal(GPUVertBuf *vbo)
{
- GLBackend::get()->tex_free(tex_id_);
+ target_ = to_gl_target(type_);
+
+ GLenum internal_format = to_gl_internal_format(format_);
+
+ if (false) {
+ /* TODO(fclem) Direct State Access. */
+ }
+ else {
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+ glTexBuffer(target_, internal_format, vbo->vbo_id);
+ }
+
+#ifndef __APPLE__
+ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ char sh_name[64];
+ SNPRINTF(sh_name, "Texture-%s", name_);
+ /* Binding before setting the label is needed on some drivers. */
+ glObjectLabel(GL_TEXTURE, tex_id_, -1, sh_name);
+ }
+#endif
+
+ GL_CHECK_ERROR("Post-texture buffer creation");
+ return true;
}
-void GLTexture::init(void)
+/* Will create enough mipmaps up to get to the given level. */
+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, NULL);
+ }
+ }
+ 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, NULL);
+ break;
+ case 2:
+ glCompressedTexImage2D(target_, mip, internal_format, w, h, 0, size, NULL);
+ break;
+ case 3:
+ glCompressedTexImage3D(target_, mip, internal_format, w, h, d, 0, size, NULL);
+ break;
+ }
+ }
+ else {
+ switch (dimensions) {
+ default:
+ case 1:
+ glTexImage1D(target_, mip, internal_format, w, 0, gl_format, gl_type, NULL);
+ break;
+ case 2:
+ glTexImage2D(target_, mip, internal_format, w, h, 0, gl_format, gl_type, NULL);
+ break;
+ case 3:
+ glTexImage3D(target_, mip, internal_format, w, h, d, 0, gl_format, gl_type, NULL);
+ break;
+ }
+ }
+
+ GL_CHECK_ERROR("Post-mipmap creation");
+ }
+
+ this->mip_range_set(0, mipmaps_);
}
/** \} */
@@ -63,34 +198,234 @@ void GLTexture::init(void)
/** \name Operations
* \{ */
-void GLTexture::bind(int /*slot*/)
+void GLTexture::update_sub(
+ int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data)
{
+ BLI_assert(validate_data_format(format_, type));
+ BLI_assert(data != NULL);
+
+ this->ensure_mipmaps(mip);
+
+ if (mip > mipmaps_) {
+ debug::raise_gl_error("Updating a miplvl on a texture too small to have this many levels.");
+ return;
+ }
+
+ const int dimensions = this->dimensions_count();
+ GLenum gl_format = to_gl_data_format(format_);
+ GLenum gl_type = to_gl(type);
+
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+
+ if (true && type_ == GPU_TEXTURE_CUBE) {
+ /* TODO(fclem) bypass if direct state access is available. */
+ /* Workaround when ARB_direct_state_access is not available. */
+ for (int i = 0; i < extent[2]; i++) {
+ GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + offset[2] + i;
+ glTexSubImage2D(target, mip, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, data);
+ }
+ }
+ else if (format_flag_ & GPU_FORMAT_COMPRESSED) {
+ size_t size = ((extent[0] + 3) / 4) * ((extent[1] + 3) / 4) * to_block_size(format_);
+ switch (dimensions) {
+ default:
+ case 1:
+ glCompressedTexSubImage1D(target_, mip, offset[0], extent[0], gl_format, size, data);
+ break;
+ case 2:
+ glCompressedTexSubImage2D(
+ target_, mip, UNPACK2(offset), UNPACK2(extent), gl_format, size, data);
+ break;
+ case 3:
+ glCompressedTexSubImage3D(
+ target_, mip, UNPACK3(offset), UNPACK3(extent), gl_format, size, data);
+ break;
+ }
+ }
+ else {
+ switch (dimensions) {
+ default:
+ case 1:
+ glTexSubImage1D(target_, mip, offset[0], extent[0], gl_format, gl_type, data);
+ break;
+ case 2:
+ glTexSubImage2D(target_, mip, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, data);
+ break;
+ case 3:
+ glTexSubImage3D(target_, mip, UNPACK3(offset), UNPACK3(extent), gl_format, gl_type, data);
+ break;
+ }
+ }
+
+ GL_CHECK_ERROR("Post-update_sub");
}
-void GLTexture::update(void * /*data*/)
+/** This will create the mipmap images and populate them with filtered data from base level.
+ * WARNING: Depth textures are not populated but they have their mips correctly defined.
+ * WARNING: This resets the mipmap range.
+ */
+void GLTexture::generate_mipmap(void)
{
+ 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
+ * GPU_framebuffer_recursive_downsample(). */
+ if (format_flag_ & GPU_FORMAT_DEPTH) {
+ return;
+ }
+
+ if (false) {
+ /* TODO(fclem) Direct State Access. */
+ }
+ else {
+ /* Downsample from mip 0 using implementation. */
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+ glGenerateMipmap(target_);
+ }
}
-void GLTexture::update_sub(void * /*data*/, int /*offset*/[3], int /*size*/[3])
+void GLTexture::clear(eGPUDataFormat data_format, const void *data)
{
+ BLI_assert(validate_data_format(format_, data_format));
+
+ if (GLEW_ARB_clear_texture && !(G.debug & G_DEBUG_GPU_FORCE_WORKAROUNDS)) {
+ int mip = 0;
+ GLenum gl_format = to_gl_data_format(format_);
+ GLenum gl_type = to_gl(data_format);
+ glClearTexImage(tex_id_, mip, gl_format, gl_type, data);
+ }
+ else {
+ /* Fallback for older GL. */
+ GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get();
+
+ FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(this->framebuffer_get());
+ fb->bind(true);
+ fb->clear_attachment(this->attachment_type(0), data_format, data);
+
+ GPU_framebuffer_bind(prev_fb);
+ }
}
-void GLTexture::generate_mipmap(void)
+void GLTexture::copy_to(Texture *dst_)
{
+ GLTexture *dst = static_cast<GLTexture *>(dst_);
+ GLTexture *src = this;
+
+ BLI_assert((dst->w_ == src->w_) && (dst->h_ == src->h_) && (dst->d_ == src->d_));
+ BLI_assert(dst->format_ == src->format_);
+ BLI_assert(dst->type_ == src->type_);
+ /* TODO support array / 3D textures. */
+ BLI_assert(dst->d_ == 0);
+
+ if (GLEW_ARB_copy_image && !GPU_texture_copy_workaround()) {
+ /* Opengl 4.3 */
+ int mip = 0;
+ /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
+ int extent[3] = {1, 1, 1};
+ this->mip_size_get(mip, extent);
+ glCopyImageSubData(
+ src->tex_id_, target_, mip, 0, 0, 0, dst->tex_id_, target_, mip, 0, 0, 0, UNPACK3(extent));
+ }
+ else {
+ /* Fallback for older GL. */
+ GPU_framebuffer_blit(
+ src->framebuffer_get(), 0, dst->framebuffer_get(), 0, to_framebuffer_bits(format_));
+ }
}
-void GLTexture::copy_to(Texture * /*tex*/)
+void *GLTexture::read(int mip, eGPUDataFormat type)
{
+ BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED));
+ BLI_assert(mip <= mipmaps_);
+ BLI_assert(validate_data_format(format_, type));
+
+ /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
+ int extent[3] = {1, 1, 1};
+ this->mip_size_get(mip, extent);
+
+ size_t sample_len = extent[0] * extent[1] * extent[2];
+ size_t sample_size = to_bytesize(format_, type);
+ size_t texture_size = sample_len * sample_size;
+
+ /* AMD Pro driver have a bug that write 8 bytes past buffer size
+ * if the texture is big. (see T66573) */
+ void *data = MEM_mallocN(texture_size + 8, "GPU_texture_read");
+
+ GLenum gl_format = to_gl_data_format(format_);
+ GLenum gl_type = to_gl(type);
+
+ if (false) {
+ /* TODO(fclem) Direct State Access. */
+ /* NOTE: DSA can read GL_TEXTURE_CUBE_MAP directly. */
+ }
+ else {
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+ if (type_ == GPU_TEXTURE_CUBE) {
+ size_t cube_face_size = texture_size / 6;
+ char *face_data = (char *)data;
+ for (int i = 0; i < 6; i++, face_data += cube_face_size) {
+ glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mip, gl_format, gl_type, face_data);
+ }
+ }
+ else {
+ glGetTexImage(target_, mip, gl_format, gl_type, data);
+ }
+ }
+ return data;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Getters & setters
+ * \{ */
+
+void GLTexture::swizzle_set(const char swizzle[4])
+{
+ GLint gl_swizzle[4] = {(GLint)swizzle_to_gl(swizzle[0]),
+ (GLint)swizzle_to_gl(swizzle[1]),
+ (GLint)swizzle_to_gl(swizzle[2]),
+ (GLint)swizzle_to_gl(swizzle[3])};
+ if (false) {
+ /* TODO(fclem) Direct State Access. */
+ }
+ else {
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+ glTexParameteriv(target_, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle);
+ }
}
-void GLTexture::swizzle_set(char /*swizzle_mask*/[4])
+void GLTexture::mip_range_set(int min, int max)
{
+ BLI_assert(min <= max && min >= 0 && max <= mipmaps_);
+ if (false) {
+ /* TODO(fclem) Direct State Access. */
+ }
+ else {
+ GLContext::state_manager_active_get()->texture_bind_temp(this);
+ glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, min);
+ glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, max);
+ }
+}
+
+struct GPUFrameBuffer *GLTexture::framebuffer_get(void)
+{
+ if (framebuffer_) {
+ return framebuffer_;
+ }
+ BLI_assert(!(type_ & (GPU_TEXTURE_ARRAY | GPU_TEXTURE_CUBE | GPU_TEXTURE_1D | GPU_TEXTURE_3D)));
+ /* TODO(fclem) cleanup this. Don't use GPU object but blender::gpu ones. */
+ GPUTexture *gputex = reinterpret_cast<GPUTexture *>(static_cast<Texture *>(this));
+ framebuffer_ = GPU_framebuffer_create(name_);
+ GPU_framebuffer_texture_attach(framebuffer_, gputex, 0, 0);
+ return framebuffer_;
}
/** \} */
/* TODO(fclem) Legacy. Should be removed at some point. */
-uint GLTexture::gl_bindcode_get(void)
+uint GLTexture::gl_bindcode_get(void) const
{
return tex_id_;
}
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
index 513cef59e85..0e054d7996a 100644
--- a/source/blender/gpu/opengl/gl_texture.hh
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -39,6 +39,8 @@
#include "glew-mx.h"
+struct GPUFrameBuffer;
+
namespace blender {
namespace gpu {
@@ -53,62 +55,177 @@ class GLContext {
#endif
class GLTexture : public Texture {
+ friend class GLStateManager;
+
private:
- /** Texture unit to which this texture is bound. */
- int slot = -1;
/** Target to bind the texture to (GL_TEXTURE_1D, GL_TEXTURE_2D, etc...)*/
GLenum target_ = -1;
/** opengl identifier for texture. */
GLuint tex_id_ = 0;
- /** Legacy workaround for texture copy. */
- GLuint copy_fb = 0;
- GPUContext *copy_fb_ctx = NULL;
+ /** Legacy workaround for texture copy. Created when using framebuffer_get(). */
+ struct GPUFrameBuffer *framebuffer_ = NULL;
+ /** True if this texture is bound to at least one texture unit. */
+ /* TODO(fclem) How do we ensure thread safety here? */
+ bool is_bound_;
public:
GLTexture(const char *name);
~GLTexture();
- void bind(int slot) override;
- void update(void *data) override;
- void update_sub(void *data, int offset[3], int size[3]) override;
+ void update_sub(
+ int mip, int offset[3], int extent[3], eGPUDataFormat format, const void *data) override;
+
void generate_mipmap(void) override;
void copy_to(Texture *tex) override;
-
- void swizzle_set(char swizzle_mask[4]) override;
+ void clear(eGPUDataFormat format, const void *data) override;
+ void swizzle_set(const char swizzle_mask[4]) override;
+ void mip_range_set(int min, int max) override;
+ void *read(int mip, eGPUDataFormat format) override;
/* TODO(fclem) Legacy. Should be removed at some point. */
- uint gl_bindcode_get(void) override;
+ uint gl_bindcode_get(void) const override;
+
+ protected:
+ bool init_internal(void) override;
+ bool init_internal(GPUVertBuf *vbo) override;
private:
- void init(void);
+ void ensure_mipmaps(int miplvl);
+ GPUFrameBuffer *framebuffer_get(void);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("GLTexture")
};
-static inline GLenum target_to_gl(eGPUTextureFlag target)
+inline GLenum to_gl_internal_format(eGPUTextureFormat format)
+{
+ /* You can add any of the available type to this list
+ * For available types see GPU_texture.h */
+ switch (format) {
+ /* Formats texture & renderbuffer */
+ case GPU_RGBA8UI:
+ return GL_RGBA8UI;
+ case GPU_RGBA8I:
+ return GL_RGBA8I;
+ case GPU_RGBA8:
+ return GL_RGBA8;
+ case GPU_RGBA32UI:
+ return GL_RGBA32UI;
+ case GPU_RGBA32I:
+ return GL_RGBA32I;
+ case GPU_RGBA32F:
+ return GL_RGBA32F;
+ case GPU_RGBA16UI:
+ return GL_RGBA16UI;
+ case GPU_RGBA16I:
+ return GL_RGBA16I;
+ case GPU_RGBA16F:
+ return GL_RGBA16F;
+ case GPU_RGBA16:
+ return GL_RGBA16;
+ case GPU_RG8UI:
+ return GL_RG8UI;
+ case GPU_RG8I:
+ return GL_RG8I;
+ case GPU_RG8:
+ return GL_RG8;
+ case GPU_RG32UI:
+ return GL_RG32UI;
+ case GPU_RG32I:
+ return GL_RG32I;
+ case GPU_RG32F:
+ return GL_RG32F;
+ case GPU_RG16UI:
+ return GL_RG16UI;
+ case GPU_RG16I:
+ return GL_RG16I;
+ case GPU_RG16F:
+ return GL_RG16F;
+ case GPU_RG16:
+ return GL_RG16;
+ case GPU_R8UI:
+ return GL_R8UI;
+ case GPU_R8I:
+ return GL_R8I;
+ case GPU_R8:
+ return GL_R8;
+ case GPU_R32UI:
+ return GL_R32UI;
+ case GPU_R32I:
+ return GL_R32I;
+ case GPU_R32F:
+ return GL_R32F;
+ case GPU_R16UI:
+ return GL_R16UI;
+ case GPU_R16I:
+ return GL_R16I;
+ case GPU_R16F:
+ return GL_R16F;
+ case GPU_R16:
+ return GL_R16;
+ /* Special formats texture & renderbuffer */
+ case GPU_R11F_G11F_B10F:
+ return GL_R11F_G11F_B10F;
+ case GPU_DEPTH32F_STENCIL8:
+ return GL_DEPTH32F_STENCIL8;
+ case GPU_DEPTH24_STENCIL8:
+ return GL_DEPTH24_STENCIL8;
+ case GPU_SRGB8_A8:
+ return GL_SRGB8_ALPHA8;
+ /* Texture only format */
+ case GPU_RGB16F:
+ return GL_RGB16F;
+ /* Special formats texture only */
+ 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;
+ case GPU_DEPTH_COMPONENT24:
+ return GL_DEPTH_COMPONENT24;
+ case GPU_DEPTH_COMPONENT16:
+ return GL_DEPTH_COMPONENT16;
+ default:
+ BLI_assert(!"Texture format incorrect or unsupported\n");
+ return 0;
+ }
+}
+
+inline GLenum to_gl_target(eGPUTextureType type)
{
- switch (target & GPU_TEXTURE_TARGET) {
+ switch (type) {
case GPU_TEXTURE_1D:
return GL_TEXTURE_1D;
- case GPU_TEXTURE_1D | GPU_TEXTURE_ARRAY:
+ case GPU_TEXTURE_1D_ARRAY:
return GL_TEXTURE_1D_ARRAY;
case GPU_TEXTURE_2D:
return GL_TEXTURE_2D;
- case GPU_TEXTURE_2D | GPU_TEXTURE_ARRAY:
+ case GPU_TEXTURE_2D_ARRAY:
return GL_TEXTURE_2D_ARRAY;
case GPU_TEXTURE_3D:
return GL_TEXTURE_3D;
case GPU_TEXTURE_CUBE:
return GL_TEXTURE_CUBE_MAP;
- case GPU_TEXTURE_CUBE | GPU_TEXTURE_ARRAY:
+ case GPU_TEXTURE_CUBE_ARRAY:
return GL_TEXTURE_CUBE_MAP_ARRAY_ARB;
case GPU_TEXTURE_BUFFER:
return GL_TEXTURE_BUFFER;
default:
BLI_assert(0);
- return GPU_TEXTURE_1D;
+ return GL_TEXTURE_1D;
}
}
-static inline GLenum swizzle_to_gl(const char swizzle)
+inline GLenum swizzle_to_gl(const char swizzle)
{
switch (swizzle) {
default:
@@ -131,7 +248,7 @@ static inline GLenum swizzle_to_gl(const char swizzle)
}
}
-static inline GLenum to_gl(eGPUDataFormat format)
+inline GLenum to_gl(eGPUDataFormat format)
{
switch (format) {
case GPU_DATA_FLOAT:
@@ -152,8 +269,67 @@ static inline GLenum to_gl(eGPUDataFormat format)
}
}
+/* Definitely not complete, edit according to the gl specification. */
+inline GLenum to_gl_data_format(eGPUTextureFormat format)
+{
+ /* You can add any of the available type to this list
+ * For available types see GPU_texture.h */
+ switch (format) {
+ case GPU_R8I:
+ case GPU_R8UI:
+ case GPU_R16I:
+ case GPU_R16UI:
+ case GPU_R32I:
+ case GPU_R32UI:
+ return GL_RED_INTEGER;
+ case GPU_RG8I:
+ case GPU_RG8UI:
+ case GPU_RG16I:
+ case GPU_RG16UI:
+ case GPU_RG32I:
+ case GPU_RG32UI:
+ return GL_RG_INTEGER;
+ case GPU_RGBA8I:
+ case GPU_RGBA8UI:
+ case GPU_RGBA16I:
+ case GPU_RGBA16UI:
+ case GPU_RGBA32I:
+ case GPU_RGBA32UI:
+ return GL_RGBA_INTEGER;
+ case GPU_R8:
+ case GPU_R16:
+ case GPU_R16F:
+ case GPU_R32F:
+ return GL_RED;
+ case GPU_RG8:
+ case GPU_RG16:
+ case GPU_RG16F:
+ case GPU_RG32F:
+ return GL_RG;
+ case GPU_R11F_G11F_B10F:
+ case GPU_RGB16F:
+ return GL_RGB;
+ case GPU_RGBA8:
+ case GPU_SRGB8_A8:
+ case GPU_RGBA16:
+ case GPU_RGBA16F:
+ case GPU_RGBA32F:
+ return GL_RGBA;
+ case GPU_DEPTH24_STENCIL8:
+ case GPU_DEPTH32F_STENCIL8:
+ return GL_DEPTH_STENCIL;
+ case GPU_DEPTH_COMPONENT16:
+ case GPU_DEPTH_COMPONENT24:
+ case GPU_DEPTH_COMPONENT32F:
+ return GL_DEPTH_COMPONENT;
+ default:
+ BLI_assert(!"Texture format incorrect or unsupported\n");
+ return 0;
+ }
+}
+
/* Assume Unorm / Float target. Used with glReadPixels. */
-static inline GLenum channel_len_to_gl(int channel_len)
+inline GLenum channel_len_to_gl(int channel_len)
{
switch (channel_len) {
case 1: