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
This commit is contained in:
Clément Foucault
2020-07-29 04:55:21 +02:00
parent e4ee9de638
commit e749643793
11 changed files with 593 additions and 1055 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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");
}
else {
GLint w, h, gl_format;
GLenum gettarget;
gettarget = (textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : textarget;
GPU_print_error_debug("Blender Texture Not Loaded");
return tex;
}
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);
/* 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;
}
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);

View File

@@ -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,97 +97,9 @@ 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)
{
if (GTS.domipmap != mipmap) {
GPU_free_images(bmain);
GTS.domipmap = mipmap;
}
}
void GPU_set_linear_mipmap(bool linear)
{
if (GTS.linearmipmap != linear) {
GTS.linearmipmap = linear;
}
}
bool GPU_get_mipmap(void)
{
return GTS.domipmap && !GTS.texpaint;
}
bool GPU_get_linear_mipmap(void)
{
return GTS.linearmipmap;
}
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;
}
}
else {
if (GTS.domipmap) {
if (GTS.linearmipmap) {
return GL_LINEAR_MIPMAP_LINEAR;
}
else {
return GL_LINEAR_MIPMAP_NEAREST;
}
}
else {
return GL_NEAREST;
}
}
}
/* Anisotropic filtering settings */
void GPU_set_anisotropic(float value)
{
if (GTS.anisotropic != value) {
GPU_samplers_free();
/* Clamp value to the maximum value the graphics card supports */
const float max = GPU_max_texture_anisotropy();
if (value > max) {
value = max;
}
GTS.anisotropic = value;
GPU_samplers_init();
}
}
float GPU_get_anisotropic(void)
{
return GTS.anisotropic;
}
static GPUTexture **gpu_get_image_gputexture(Image *ima, int textarget, const int multiview_eye)
static GPUTexture **gpu_get_image_gputexture(Image *ima,
eGPUTextureTarget textarget,
const int multiview_eye)
{
const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT);
BLI_assert(in_range);
@@ -207,7 +110,164 @@ static GPUTexture **gpu_get_image_gputexture(Image *ima, int textarget, const in
return NULL;
}
static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye)
static GPUTexture **gpu_get_movieclip_gputexture(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;
}
memcpy(&tex->user, cuser, sizeof(MovieClipUser));
BLI_addtail(&clip->runtime.gputextures, tex);
}
return &tex->gputexture[textarget];
}
}
return NULL;
}
/* 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)
{
const bool is_float_rect = (ibuf->rect_float != NULL);
void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect;
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;
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 {
/* 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;
}
/* 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);
}
}
if (do_rescale) {
uint *rect = (is_float_rect) ? NULL : (uint *)data_rect;
float *rect_float = (is_float_rect) ? (float *)data_rect : NULL;
ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4);
IMB_scaleImBuf(scale_ibuf, UNPACK2(rescale_size));
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;
}
static void get_texture_format_from_ibuf(const Image *ima,
const ImBuf *ibuf,
eGPUDataFormat *r_data_format,
eGPUTextureFormat *r_texture_format)
{
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;
}
}
/* Return false if no suitable format was found. */
static bool get_texture_compressed_format_from_ibuf(const ImBuf *ibuf,
eGPUTextureFormat *r_data_format)
{
#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 (ibuf->dds_data.fourcc == FOURCC_DXT1) {
*r_data_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1;
}
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 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);
GPUTexture *tex = GPU_texture_create_1d_array(width, 2, GPU_RGBA32F, data, NULL);
GPU_texture_mipmap_mode(tex, false, false);
MEM_freeN(data);
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);
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]);
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;
ImBuf *scale_ibuf = NULL;
if (ibuf->rect_float) {
float *rect_float = ibuf->rect_float;
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);
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;
}
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);
}
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;
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);
}
GPU_texture_mipmap_mode(tex, false, true);
}
/* Create OpenGL texture. */
gpu_create_gl_tex(&bindcode,
(uint *)rect,
rect_float,
ibuf->x,
ibuf->y,
textarget,
mipmap,
half_float,
compress_as_srgb,
ima);
/* Free buffers if needed. */
if (rect && rect != (uchar *)ibuf->rect) {
MEM_freeN(rect);
}
if (rect_float && rect_float != ibuf->rect_float) {
MEM_freeN(rect_float);
if (freebuf) {
MEM_SAFE_FREE(data);
}
return bindcode;
return tex;
}
static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip,
MovieClipUser *cuser,
eGPUTextureTarget textarget)
/* 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)
{
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__);
#ifndef GPU_STANDALONE
if (ima == NULL) {
return NULL;
}
for (int i = 0; i < TEXTARGET_COUNT; i++) {
tex->gputexture[i] = 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();
memcpy(&tex->user, cuser, sizeof(MovieClipUser));
BLI_addtail(&clip->runtime.gputextures, tex);
}
/* 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;
}
return &tex->gputexture[textarget];
/* 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);
}
GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
return *tex;
#endif
return NULL;
}
GPUTexture *GPU_texture_from_movieclip(MovieClip *clip,
MovieClipUser *cuser,
eGPUTextureTarget textarget)
{
#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 */
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);
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);
/* Shift to account for tile packing. */
x += tile_offset[0];
y += tile_offset[1];
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);
/* 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);
}
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;
GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1);
IMB_freeImBuf(ibuf);
}
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);
ImBuf *ibuf = update_do_scale(
rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h);
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);
}
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,494 +852,6 @@ 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)
{
#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;
}
}
if (format == 0) {
fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n");
glDeleteTextures(1, (GLuint *)bindcode);
return false;
}
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;
}
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;
}
/* 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);
#endif
}
/* these two functions are called on entering and exiting texture paint mode,
* temporary disabling/enabling mipmapping on all images for quick texture
* updates with glTexSubImage2D. images that didn't change don't have to be
* re-uploaded to OpenGL */
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)) {
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);
}
}
}
}
}
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
@@ -1363,9 +879,47 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
#endif
}
/* 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. */
/* these two functions are called on entering and exiting texture paint mode,
* temporary disabling/enabling mipmapping on all images for quick texture
* updates with glTexSubImage2D. images that didn't change don't have to be
* re-uploaded to OpenGL */
void GPU_paint_set_mipmap(Main *bmain, bool mipmap)
{
#ifndef GPU_STANDALONE
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_mipmap_mode(tex, mipmap, true);
}
}
}
}
}
else {
GPU_free_image(ima);
}
}
else {
ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
}
}
#endif /* GPU_STANDALONE */
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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.
* \{ */
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)
}
}
}
/** \} */

View File

@@ -36,7 +36,6 @@
#include "BKE_colorband.h"
#include "GPU_draw.h"
#include "GPU_glew.h"
#include "GPU_texture.h"
#ifdef WITH_FLUID

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();