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
This commit is contained in:
@@ -44,6 +44,7 @@ typedef struct GPUTexture GPUTexture;
|
||||
* - All states are created at startup to avoid runtime costs.
|
||||
*/
|
||||
typedef enum eGPUSamplerState {
|
||||
GPU_SAMPLER_DEFAULT = 0,
|
||||
GPU_SAMPLER_FILTER = (1 << 0),
|
||||
GPU_SAMPLER_MIPMAP = (1 << 1),
|
||||
GPU_SAMPLER_REPEAT_S = (1 << 2),
|
||||
@@ -52,8 +53,11 @@ typedef enum eGPUSamplerState {
|
||||
GPU_SAMPLER_CLAMP_BORDER = (1 << 5), /* Clamp to border color instead of border texel. */
|
||||
GPU_SAMPLER_COMPARE = (1 << 6),
|
||||
GPU_SAMPLER_ANISO = (1 << 7),
|
||||
GPU_SAMPLER_ICON = (1 << 8),
|
||||
|
||||
GPU_SAMPLER_REPEAT = (GPU_SAMPLER_REPEAT_S | GPU_SAMPLER_REPEAT_T | GPU_SAMPLER_REPEAT_R),
|
||||
/* Don't use that. */
|
||||
GPU_SAMPLER_MAX = (1 << 8),
|
||||
GPU_SAMPLER_MAX = (GPU_SAMPLER_ICON + 1),
|
||||
} eGPUSamplerState;
|
||||
|
||||
ENUM_OPERATORS(eGPUSamplerState)
|
||||
@@ -209,12 +213,6 @@ GPUTexture *GPU_texture_create_1d_array(
|
||||
int w, int h, eGPUTextureFormat tex_format, const float *pixels, char err_out[256]);
|
||||
GPUTexture *GPU_texture_create_2d(
|
||||
int w, int h, eGPUTextureFormat tex_format, const float *pixels, char err_out[256]);
|
||||
GPUTexture *GPU_texture_create_2d_multisample(int w,
|
||||
int h,
|
||||
eGPUTextureFormat tex_format,
|
||||
const float *pixels,
|
||||
int samples,
|
||||
char err_out[256]);
|
||||
GPUTexture *GPU_texture_create_2d_array(
|
||||
int w, int h, int d, eGPUTextureFormat tex_format, const float *pixels, char err_out[256]);
|
||||
GPUTexture *GPU_texture_create_3d(
|
||||
@@ -227,7 +225,6 @@ GPUTexture *GPU_texture_create_cube_array(
|
||||
int w, int d, eGPUTextureFormat tex_format, const float *pixels, char err_out[256]);
|
||||
|
||||
GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert);
|
||||
GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const uint buffer);
|
||||
|
||||
GPUTexture *GPU_texture_create_compressed(
|
||||
int w, int h, int miplen, eGPUTextureFormat format, const void *data);
|
||||
@@ -286,7 +283,6 @@ int GPU_texture_height(const GPUTexture *tex);
|
||||
int GPU_texture_orig_width(const GPUTexture *tex);
|
||||
int GPU_texture_orig_height(const GPUTexture *tex);
|
||||
void GPU_texture_orig_size_set(GPUTexture *tex, int w, int h);
|
||||
int GPU_texture_layers(const GPUTexture *tex);
|
||||
eGPUTextureFormat GPU_texture_format(const GPUTexture *tex);
|
||||
int GPU_texture_samples(const GPUTexture *tex);
|
||||
bool GPU_texture_array(const GPUTexture *tex);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include "GPU_state.h"
|
||||
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace blender {
|
||||
@@ -160,6 +162,10 @@ class GPUStateManager {
|
||||
virtual ~GPUStateManager(){};
|
||||
|
||||
virtual void apply_state(void) = 0;
|
||||
|
||||
virtual void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) = 0;
|
||||
virtual void texture_unbind(Texture *tex) = 0;
|
||||
virtual void texture_unbind_all(void) = 0;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,28 +30,32 @@
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
||||
typedef enum eGPUTextureFlag {
|
||||
GPU_TEXFORMAT_DEPTH = (1 << 0),
|
||||
GPU_TEXFORMAT_STENCIL = (1 << 1),
|
||||
GPU_TEXFORMAT_INTEGER = (1 << 2),
|
||||
GPU_TEXFORMAT_FLOAT = (1 << 3),
|
||||
typedef enum eGPUTextureFormatFlag {
|
||||
GPU_FORMAT_DEPTH = (1 << 0),
|
||||
GPU_FORMAT_STENCIL = (1 << 1),
|
||||
GPU_FORMAT_INTEGER = (1 << 2),
|
||||
GPU_FORMAT_FLOAT = (1 << 3),
|
||||
GPU_FORMAT_COMPRESSED = (1 << 4),
|
||||
|
||||
GPU_TEXTURE_1D = (1 << 10),
|
||||
GPU_TEXTURE_2D = (1 << 11),
|
||||
GPU_TEXTURE_3D = (1 << 12),
|
||||
GPU_TEXTURE_CUBE = (1 << 13),
|
||||
GPU_TEXTURE_ARRAY = (1 << 14),
|
||||
GPU_TEXTURE_BUFFER = (1 << 15),
|
||||
GPU_FORMAT_DEPTH_STENCIL = (GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL),
|
||||
} eGPUTextureFormatFlag;
|
||||
|
||||
ENUM_OPERATORS(eGPUTextureFormatFlag)
|
||||
|
||||
typedef enum eGPUTextureType {
|
||||
GPU_TEXTURE_1D = (1 << 0),
|
||||
GPU_TEXTURE_2D = (1 << 1),
|
||||
GPU_TEXTURE_3D = (1 << 2),
|
||||
GPU_TEXTURE_CUBE = (1 << 3),
|
||||
GPU_TEXTURE_ARRAY = (1 << 4),
|
||||
GPU_TEXTURE_BUFFER = (1 << 5),
|
||||
|
||||
GPU_TEXTURE_1D_ARRAY = (GPU_TEXTURE_1D | GPU_TEXTURE_ARRAY),
|
||||
GPU_TEXTURE_2D_ARRAY = (GPU_TEXTURE_2D | GPU_TEXTURE_ARRAY),
|
||||
GPU_TEXTURE_CUBE_ARRAY = (GPU_TEXTURE_CUBE | GPU_TEXTURE_ARRAY),
|
||||
} eGPUTextureType;
|
||||
|
||||
GPU_TEXTURE_TARGET = (GPU_TEXTURE_1D | GPU_TEXTURE_2D | GPU_TEXTURE_3D | GPU_TEXTURE_CUBE |
|
||||
GPU_TEXTURE_ARRAY),
|
||||
} eGPUTextureFlag;
|
||||
|
||||
ENUM_OPERATORS(eGPUTextureFlag)
|
||||
ENUM_OPERATORS(eGPUTextureType)
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_NAME_LEN 64
|
||||
@@ -60,23 +64,14 @@ ENUM_OPERATORS(eGPUTextureFlag)
|
||||
#endif
|
||||
|
||||
/* Maximum number of FBOs a texture can be attached to. */
|
||||
#define GPU_TEX_MAX_FBO_ATTACHED 12
|
||||
#define GPU_TEX_MAX_FBO_ATTACHED 13
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
/** Width & Height & Depth. */
|
||||
int w = 0, h = 0, d = 0;
|
||||
/** Number of color/alpha channels. */
|
||||
int components = 0;
|
||||
/** Internal data format and it's characteristics. */
|
||||
eGPUTextureFormat format;
|
||||
eGPUTextureFlag flag;
|
||||
/** Internal Sampler state. */
|
||||
eGPUSamplerState sampler_state;
|
||||
/** Number of mipmaps this texture has. */
|
||||
int mipmaps = 0;
|
||||
eGPUSamplerState sampler_state = GPU_SAMPLER_DEFAULT;
|
||||
/** Reference counter. */
|
||||
int refcount = 0;
|
||||
int refcount = 1;
|
||||
/** Width & Height (of source data), optional. */
|
||||
int src_w = 0, src_h = 0;
|
||||
/** Framebuffer references to update on deletion. */
|
||||
@@ -84,6 +79,20 @@ class Texture {
|
||||
FrameBuffer *fb[GPU_TEX_MAX_FBO_ATTACHED];
|
||||
|
||||
protected:
|
||||
/* ---- Texture format (immutable after init). ---- */
|
||||
/** Width & Height & Depth. For cubemap arrays, d is number of facelayers. */
|
||||
int w_, h_, d_;
|
||||
/** Internal data format. */
|
||||
eGPUTextureFormat format_;
|
||||
/** Format caracteristics. */
|
||||
eGPUTextureFormatFlag format_flag_;
|
||||
/** Texture type. */
|
||||
eGPUTextureType type_;
|
||||
|
||||
/** Number of mipmaps this texture has (Max miplvl). */
|
||||
/* TODO(fclem) Should become immutable and the need for mipmaps should be specified upfront. */
|
||||
int mipmaps_ = -1;
|
||||
|
||||
/** For debugging */
|
||||
char name_[DEBUG_NAME_LEN];
|
||||
|
||||
@@ -91,22 +100,110 @@ class Texture {
|
||||
Texture(const char *name);
|
||||
virtual ~Texture();
|
||||
|
||||
virtual void bind(int slot) = 0;
|
||||
virtual void update(void *data) = 0;
|
||||
virtual void update_sub(void *data, int offset[3], int size[3]) = 0;
|
||||
/* 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_buffer(GPUVertBuf *vbo, eGPUTextureFormat format);
|
||||
|
||||
virtual void generate_mipmap(void) = 0;
|
||||
virtual void copy_to(Texture *tex) = 0;
|
||||
|
||||
virtual void swizzle_set(char swizzle_mask[4]) = 0;
|
||||
|
||||
/* TODO(fclem) Legacy. Should be removed at some point. */
|
||||
virtual uint gl_bindcode_get(void) = 0;
|
||||
virtual void clear(eGPUDataFormat format, const void *data) = 0;
|
||||
virtual void swizzle_set(const char swizzle_mask[4]) = 0;
|
||||
virtual void mip_range_set(int min, int max) = 0;
|
||||
virtual void *read(int mip, eGPUDataFormat format) = 0;
|
||||
|
||||
void attach_to(FrameBuffer *fb);
|
||||
void update(eGPUDataFormat format, const void *data);
|
||||
void update_mip(int mip, eGPUDataFormat format, const void *data);
|
||||
|
||||
virtual void update_sub(
|
||||
int mip, int offset[3], int extent[3], eGPUDataFormat format, const void *data) = 0;
|
||||
|
||||
/* TODO(fclem) Legacy. Should be removed at some point. */
|
||||
virtual uint gl_bindcode_get(void) const = 0;
|
||||
|
||||
int width_get(void) const
|
||||
{
|
||||
return w_;
|
||||
}
|
||||
int height_get(void) const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
int depth_get(void) const
|
||||
{
|
||||
return d_;
|
||||
}
|
||||
|
||||
void mip_size_get(int mip, int r_size[3]) const
|
||||
{
|
||||
/* TODO assert if lvl is below the limit of 1px in each dimension. */
|
||||
int div = 1 << mip;
|
||||
r_size[0] = max_ii(1, w_ / div);
|
||||
|
||||
if (type_ == GPU_TEXTURE_1D_ARRAY) {
|
||||
r_size[1] = h_;
|
||||
}
|
||||
else if (h_ > 0) {
|
||||
r_size[1] = max_ii(1, h_ / div);
|
||||
}
|
||||
|
||||
if (type_ & (GPU_TEXTURE_ARRAY | GPU_TEXTURE_CUBE)) {
|
||||
r_size[2] = d_;
|
||||
}
|
||||
else if (d_ > 0) {
|
||||
r_size[2] = max_ii(1, d_ / div);
|
||||
}
|
||||
}
|
||||
|
||||
int mip_width_get(int mip) const
|
||||
{
|
||||
return max_ii(1, w_ / (1 << mip));
|
||||
}
|
||||
int mip_height_get(int mip) const
|
||||
{
|
||||
return (type_ == GPU_TEXTURE_1D_ARRAY) ? h_ : max_ii(1, h_ / (1 << mip));
|
||||
}
|
||||
int mip_depth_get(int mip) const
|
||||
{
|
||||
return (type_ & (GPU_TEXTURE_ARRAY | GPU_TEXTURE_CUBE)) ? d_ : max_ii(1, d_ / (1 << mip));
|
||||
}
|
||||
|
||||
/* Return number of dimension taking the array type into account. */
|
||||
int dimensions_count(void) const
|
||||
{
|
||||
const int array = (type_ & GPU_TEXTURE_ARRAY) ? 1 : 0;
|
||||
switch (type_ & ~GPU_TEXTURE_ARRAY) {
|
||||
case GPU_TEXTURE_BUFFER:
|
||||
return 1;
|
||||
case GPU_TEXTURE_1D:
|
||||
return 1 + array;
|
||||
case GPU_TEXTURE_2D:
|
||||
return 2 + array;
|
||||
case GPU_TEXTURE_CUBE:
|
||||
case GPU_TEXTURE_3D:
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
eGPUTextureFormat format_get(void) const
|
||||
{
|
||||
return format_;
|
||||
}
|
||||
eGPUTextureFormatFlag format_flag_get(void) const
|
||||
{
|
||||
return format_flag_;
|
||||
}
|
||||
eGPUTextureType type_get(void) const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
GPUAttachmentType attachment_type(int slot) const
|
||||
{
|
||||
switch (format) {
|
||||
switch (format_) {
|
||||
case GPU_DEPTH_COMPONENT32F:
|
||||
case GPU_DEPTH_COMPONENT24:
|
||||
case GPU_DEPTH_COMPONENT16:
|
||||
@@ -120,9 +217,233 @@ class Texture {
|
||||
return GPU_FB_COLOR_ATTACHMENT0 + slot;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool init_internal(void) = 0;
|
||||
virtual bool init_internal(GPUVertBuf *vbo) = 0;
|
||||
};
|
||||
|
||||
#undef DEBUG_NAME_LEN
|
||||
|
||||
inline size_t to_bytesize(eGPUTextureFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case GPU_RGBA32F:
|
||||
return 32;
|
||||
case GPU_RG32F:
|
||||
case GPU_RGBA16F:
|
||||
case GPU_RGBA16:
|
||||
return 16;
|
||||
case GPU_RGB16F:
|
||||
return 12;
|
||||
case GPU_DEPTH32F_STENCIL8: /* 32-bit depth, 8 bits stencil, and 24 unused bits. */
|
||||
return 8;
|
||||
case GPU_RG16F:
|
||||
case GPU_RG16I:
|
||||
case GPU_RG16UI:
|
||||
case GPU_RG16:
|
||||
case GPU_DEPTH24_STENCIL8:
|
||||
case GPU_DEPTH_COMPONENT32F:
|
||||
case GPU_RGBA8UI:
|
||||
case GPU_RGBA8:
|
||||
case GPU_SRGB8_A8:
|
||||
case GPU_R11F_G11F_B10F:
|
||||
case GPU_R32F:
|
||||
case GPU_R32UI:
|
||||
case GPU_R32I:
|
||||
return 4;
|
||||
case GPU_DEPTH_COMPONENT24:
|
||||
return 3;
|
||||
case GPU_DEPTH_COMPONENT16:
|
||||
case GPU_R16F:
|
||||
case GPU_R16UI:
|
||||
case GPU_R16I:
|
||||
case GPU_RG8:
|
||||
case GPU_R16:
|
||||
return 2;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t to_block_size(eGPUTextureFormat data_type)
|
||||
{
|
||||
switch (data_type) {
|
||||
case GPU_SRGB8_A8_DXT1:
|
||||
case GPU_RGBA8_DXT1:
|
||||
return 8;
|
||||
case GPU_SRGB8_A8_DXT3:
|
||||
case GPU_SRGB8_A8_DXT5:
|
||||
case GPU_RGBA8_DXT3:
|
||||
case GPU_RGBA8_DXT5:
|
||||
return 16;
|
||||
default:
|
||||
BLI_assert(!"Texture format is not a compressed format\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline eGPUTextureFormatFlag to_format_flag(eGPUTextureFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case GPU_DEPTH_COMPONENT24:
|
||||
case GPU_DEPTH_COMPONENT16:
|
||||
case GPU_DEPTH_COMPONENT32F:
|
||||
return GPU_FORMAT_DEPTH;
|
||||
case GPU_DEPTH24_STENCIL8:
|
||||
case GPU_DEPTH32F_STENCIL8:
|
||||
return GPU_FORMAT_DEPTH_STENCIL;
|
||||
case GPU_R8UI:
|
||||
case GPU_RG16I:
|
||||
case GPU_R16I:
|
||||
case GPU_RG16UI:
|
||||
case GPU_R16UI:
|
||||
case GPU_R32UI:
|
||||
return GPU_FORMAT_INTEGER;
|
||||
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 GPU_FORMAT_COMPRESSED;
|
||||
default:
|
||||
return GPU_FORMAT_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
inline int to_component_len(eGPUTextureFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case GPU_RGBA8:
|
||||
case GPU_RGBA8UI:
|
||||
case GPU_RGBA16F:
|
||||
case GPU_RGBA16:
|
||||
case GPU_RGBA32F:
|
||||
case GPU_SRGB8_A8:
|
||||
return 4;
|
||||
case GPU_RGB16F:
|
||||
case GPU_R11F_G11F_B10F:
|
||||
return 3;
|
||||
case GPU_RG8:
|
||||
case GPU_RG16:
|
||||
case GPU_RG16F:
|
||||
case GPU_RG16I:
|
||||
case GPU_RG16UI:
|
||||
case GPU_RG32F:
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t to_bytesize(eGPUTextureFormat tex_format, eGPUDataFormat data_format)
|
||||
{
|
||||
switch (data_format) {
|
||||
case GPU_DATA_UNSIGNED_BYTE:
|
||||
return 1 * to_component_len(tex_format);
|
||||
case GPU_DATA_FLOAT:
|
||||
case GPU_DATA_INT:
|
||||
case GPU_DATA_UNSIGNED_INT:
|
||||
return 4 * to_component_len(tex_format);
|
||||
case GPU_DATA_UNSIGNED_INT_24_8:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return 4;
|
||||
default:
|
||||
BLI_assert(!"Data format incorrect or unsupported\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Definitely not complete, edit according to the gl specification. */
|
||||
inline bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat data_format)
|
||||
{
|
||||
switch (tex_format) {
|
||||
case GPU_DEPTH_COMPONENT24:
|
||||
case GPU_DEPTH_COMPONENT16:
|
||||
case GPU_DEPTH_COMPONENT32F:
|
||||
return data_format == GPU_DATA_FLOAT;
|
||||
case GPU_DEPTH24_STENCIL8:
|
||||
case GPU_DEPTH32F_STENCIL8:
|
||||
return data_format == GPU_DATA_UNSIGNED_INT_24_8;
|
||||
case GPU_R8UI:
|
||||
case GPU_R16UI:
|
||||
case GPU_RG16UI:
|
||||
case GPU_R32UI:
|
||||
return data_format == GPU_DATA_UNSIGNED_INT;
|
||||
case GPU_RG16I:
|
||||
case GPU_R16I:
|
||||
return data_format == GPU_DATA_INT;
|
||||
case GPU_R8:
|
||||
case GPU_RG8:
|
||||
case GPU_RGBA8:
|
||||
case GPU_RGBA8UI:
|
||||
case GPU_SRGB8_A8:
|
||||
return ELEM(data_format, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT);
|
||||
case GPU_R11F_G11F_B10F:
|
||||
return ELEM(data_format, GPU_DATA_10_11_11_REV, GPU_DATA_FLOAT);
|
||||
default:
|
||||
return data_format == GPU_DATA_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Definitely not complete, edit according to the gl specification. */
|
||||
inline eGPUDataFormat to_data_format(eGPUTextureFormat tex_format)
|
||||
{
|
||||
switch (tex_format) {
|
||||
case GPU_DEPTH_COMPONENT24:
|
||||
case GPU_DEPTH_COMPONENT16:
|
||||
case GPU_DEPTH_COMPONENT32F:
|
||||
return GPU_DATA_FLOAT;
|
||||
case GPU_DEPTH24_STENCIL8:
|
||||
case GPU_DEPTH32F_STENCIL8:
|
||||
return GPU_DATA_UNSIGNED_INT_24_8;
|
||||
case GPU_R8UI:
|
||||
case GPU_R16UI:
|
||||
case GPU_RG16UI:
|
||||
case GPU_R32UI:
|
||||
return GPU_DATA_UNSIGNED_INT;
|
||||
case GPU_RG16I:
|
||||
case GPU_R16I:
|
||||
return GPU_DATA_INT;
|
||||
case GPU_R8:
|
||||
case GPU_RG8:
|
||||
case GPU_RGBA8:
|
||||
case GPU_RGBA8UI:
|
||||
case GPU_SRGB8_A8:
|
||||
return GPU_DATA_UNSIGNED_BYTE;
|
||||
case GPU_R11F_G11F_B10F:
|
||||
return GPU_DATA_10_11_11_REV;
|
||||
default:
|
||||
return GPU_DATA_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
inline eGPUFrameBufferBits to_framebuffer_bits(eGPUTextureFormat tex_format)
|
||||
{
|
||||
switch (tex_format) {
|
||||
case GPU_DEPTH_COMPONENT24:
|
||||
case GPU_DEPTH_COMPONENT16:
|
||||
case GPU_DEPTH_COMPONENT32F:
|
||||
return GPU_DEPTH_BIT;
|
||||
case GPU_DEPTH24_STENCIL8:
|
||||
case GPU_DEPTH32F_STENCIL8:
|
||||
return GPU_DEPTH_BIT | GPU_STENCIL_BIT;
|
||||
default:
|
||||
return GPU_COLOR_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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")
|
||||
};
|
||||
|
||||
|
||||
@@ -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_);
|
||||
|
||||
#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);
|
||||
glObjectLabel(GL_TEXTURE, tex_id_, -1, sh_name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
void GLTexture::init(void)
|
||||
/* 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_);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Return true on success. */
|
||||
bool GLTexture::init_internal(GPUVertBuf *vbo)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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(void * /*data*/)
|
||||
{
|
||||
}
|
||||
|
||||
void GLTexture::update_sub(void * /*data*/, int /*offset*/[3], int /*size*/[3])
|
||||
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");
|
||||
}
|
||||
|
||||
/** 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::copy_to(Texture * /*tex*/)
|
||||
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::swizzle_set(char /*swizzle_mask*/[4])
|
||||
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::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::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_;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
switch (target & GPU_TEXTURE_TARGET) {
|
||||
/* 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 (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:
|
||||
|
||||
Reference in New Issue
Block a user