Cleanup: Split gpu_texture_image.c into BKE and IMB modules

This is in order to disolve GPU_draw.h into more meaningful code blocks.

All the Image related function are in `image_gpu.c`.
All the MovieClip related function are in `movieclip.c`.

The IMB module now has a connection with GPU. This is not strickly
necessary and the code could be move to `image_gpu.c` if needed.

The Image garbage collection is also ported to `image_gpu.c`.
This commit is contained in:
Clément Foucault 2020-07-29 18:13:19 +02:00
parent 7e8d493730
commit 5f6fb5bb41
37 changed files with 632 additions and 572 deletions

View File

@ -55,6 +55,7 @@ void BKE_image_free_packedfiles(struct Image *image);
void BKE_image_free_views(struct Image *image);
void BKE_image_free_buffers(struct Image *image);
void BKE_image_free_buffers_ex(struct Image *image, bool do_lock);
void BKE_image_free_gputextures(struct Image *ima);
/* call from library */
void BKE_image_free(struct Image *image);
@ -274,6 +275,10 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame);
/* does all images with type MOVIE or SEQUENCE */
void BKE_image_all_free_anim_ibufs(struct Main *bmain, int except_frame);
void BKE_image_free_all_gputextures(struct Main *bmain);
void BKE_image_free_anim_gputextures(struct Main *bmain);
void BKE_image_free_old_gputextures(struct Main *bmain);
bool BKE_image_memorypack(struct Image *ima);
void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath);
void BKE_image_packfiles_from_mem(struct ReportList *reports,
@ -362,6 +367,30 @@ bool BKE_image_has_loaded_ibuf(struct Image *image);
struct ImBuf *BKE_image_get_ibuf_with_name(struct Image *image, const char *name);
struct ImBuf *BKE_image_get_first_ibuf(struct Image *image);
/* Not to be use directly. */
struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf);
/* 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. */
struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image,
struct ImageUser *iuser,
struct ImBuf *ibuf);
struct GPUTexture *BKE_image_get_gpu_tiles(struct Image *image,
struct ImageUser *iuser,
struct ImBuf *ibuf);
struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image,
struct ImageUser *iuser,
struct ImBuf *ibuf);
void BKE_image_update_gputexture(
struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h);
void BKE_image_paint_set_mipmap(struct Main *bmain, bool mipmap);
/* Delayed free of OpenGL buffers by main thread */
void BKE_image_free_unused_gpu_textures(void);
struct RenderSlot *BKE_image_add_renderslot(struct Image *ima, const char *name);
bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int slot);
struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int slot);

View File

@ -113,6 +113,11 @@ bool BKE_movieclip_put_frame_if_possible(struct MovieClip *clip,
struct MovieClipUser *user,
struct ImBuf *ibuf);
struct GPUTexture *BKE_movieclip_get_gpu_texture(struct MovieClip *clip,
struct MovieClipUser *cuser);
void BKE_movieclip_free_gputexture(struct MovieClip *clip);
/* Dependency graph evaluation. */
void BKE_movieclip_eval_update(struct Depsgraph *depsgraph,

View File

@ -134,6 +134,7 @@ set(SRC
intern/idtype.c
intern/image.c
intern/image_gen.c
intern/image_gpu.c
intern/image_save.c
intern/ipo.c
intern/kelvinlet.c

View File

@ -90,7 +90,6 @@
#include "RE_pipeline.h"
#include "GPU_draw.h"
#include "GPU_texture.h"
#include "BLI_sys_types.h" // for intptr_t support
@ -393,7 +392,7 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock)
ima->rr = NULL;
}
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
tile->ok = IMA_OK;

View File

@ -13,36 +13,24 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2005 Blender Foundation.
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup gpu
*
* Utility functions for dealing with OpenGL texture & material context,
* mipmap generation and light objects.
*
* These are some obscure rendering functions shared between the game engine (not anymore)
* and the blender, in this module to avoid duplication
* and abstract them away from the rest a bit.
* \ingroup bke
*/
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_boxpack_2d.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "DNA_image_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_userdef_types.h"
#include "MEM_guardedalloc.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@ -50,218 +38,31 @@
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_movieclip.h"
#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_state.h"
#include "GPU_texture.h"
#include "PIL_time.h"
static void gpu_free_image(Image *ima, const bool immediate);
/* Prototypes. */
static void gpu_free_unused_buffers(void);
/** \} */
/* -------------------------------------------------------------------- */
/** \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)
{
return is_power_of_2_i(w) && is_power_of_2_i(h);
}
#endif
static bool is_over_resolution_limit(int w, int h)
{
int size = GPU_max_texture_size();
int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size;
return (w > reslimit || h > reslimit);
}
static int smaller_power_of_2_limit(int num)
{
int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, GPU_max_texture_size()) :
GPU_max_texture_size();
/* take texture clamping into account */
if (num > reslimit) {
return reslimit;
}
return power_of_2_min_i(num);
}
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);
if (in_range) {
return &(ima->gputexture[textarget][multiview_eye]);
}
return NULL;
}
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.
* *r_freedata is set to true if the returned buffer need to be manually freed.
**/
static void *IMB_gpu_get_data(const ImBuf *ibuf,
const bool do_rescale,
const int rescale_size[2],
const bool compress_as_srgb,
const bool store_premultiplied,
bool *r_freedata)
{
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. */
if (ibuf->channels != 4 || !store_premultiplied) {
data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
*r_freedata = 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_freedata = 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. */
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_freedata = 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 IMB_gpu_get_format(const ImBuf *ibuf,
bool high_bitdepth,
eGPUDataFormat *r_data_format,
eGPUTextureFormat *r_texture_format)
{
const bool float_rect = (ibuf->rect_float != NULL);
const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) &&
!IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
*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;
}
}
#ifdef WITH_DDS
/* Return false if no suitable format was found. */
static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
{
/* 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_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1;
}
else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
*r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3;
}
else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
*r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5;
}
else {
return false;
}
return true;
}
#endif
static bool mipmap_enabled(void)
{
/* This used to be a userpref option. Maybe it will be re-introduce late. */
return true;
}
/** \} */
static void image_free_gpu(Image *ima, const bool immediate);
/* -------------------------------------------------------------------- */
/** \name UDIM gpu texture
* \{ */
static bool is_over_resolution_limit(int w, int h)
{
return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h));
}
static int smaller_power_of_2_limit(int num)
{
return power_of_2_min_i(GPU_texture_size_with_limit(num));
}
static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye)
{
GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye];
@ -421,7 +222,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
BKE_image_release_ibuf(ima, ibuf, NULL);
}
if (mipmap_enabled()) {
if (GPU_mipmap_enabled()) {
GPU_texture_generate_mipmap(tex);
GPU_texture_mipmap_mode(tex, true, true);
if (ima) {
@ -443,76 +244,37 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
/** \name Regular gpu texture
* \{ */
static GPUTexture *IMB_create_gpu_texture(ImBuf *ibuf, bool use_high_bitdepth, bool use_premult)
static GPUTexture **get_image_gpu_texture_ptr(Image *ima,
eGPUTextureTarget textarget,
const int multiview_eye)
{
GPUTexture *tex = NULL;
bool do_rescale = is_over_resolution_limit(ibuf->x, ibuf->y);
const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT);
BLI_assert(in_range);
#ifdef WITH_DDS
if (ibuf->ftype == IMB_FTYPE_DDS) {
eGPUTextureFormat compressed_format;
if (!IMB_gpu_get_compressed_format(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");
if (in_range) {
return &(ima->gputexture[textarget][multiview_eye]);
}
#endif
eGPUDataFormat data_format;
eGPUTextureFormat tex_format;
IMB_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
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]);
}
const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8);
bool freebuf = false;
void *data = IMB_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &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 (freebuf) {
MEM_SAFE_FREE(data);
}
return tex;
return NULL;
}
/* 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)
static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget)
{
switch (textarget) {
case TEXTARGET_2D_ARRAY:
return GPU_texture_create_error(2, true);
case TEXTARGET_TILE_MAPPING:
return GPU_texture_create_error(1, true);
case TEXTARGET_2D:
default:
return GPU_texture_create_error(2, false);
}
}
static GPUTexture *image_get_gpu_texture(Image *ima,
ImageUser *iuser,
ImBuf *ibuf,
eGPUTextureTarget textarget)
{
#ifndef GPU_STANDALONE
if (ima == NULL) {
return NULL;
}
@ -523,7 +285,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima,
/* currently, gpu refresh tagging is used by ima sequences */
if (ima->gpuflag & IMA_GPU_REFRESH) {
gpu_free_image(ima, true);
image_free_gpu(ima, true);
ima->gpuflag &= ~IMA_GPU_REFRESH;
}
@ -531,7 +293,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima,
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);
GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, iuser ? iuser->multiview_eye : 0);
if (*tex) {
return *tex;
}
@ -540,7 +302,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima,
* 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);
*tex = image_gpu_texture_error_create(textarget);
return *tex;
}
@ -549,7 +311,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima,
if (ibuf_intern == NULL) {
ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (ibuf_intern == NULL) {
*tex = GPU_texture_create_error(textarget);
*tex = image_gpu_texture_error_create(textarget);
return *tex;
}
}
@ -568,7 +330,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima,
*tex = IMB_create_gpu_texture(ibuf_intern, use_high_bitdepth, store_premultiplied);
if (mipmap_enabled()) {
if (GPU_mipmap_enabled()) {
GPU_texture_bind(*tex, 0);
GPU_texture_generate_mipmap(*tex);
GPU_texture_unbind(*tex);
@ -590,45 +352,150 @@ GPUTexture *GPU_texture_from_blender(Image *ima,
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)
GPUTexture *BKE_image_get_gpu_texture(Image *image, ImageUser *iuser, ImBuf *ibuf)
{
#ifndef GPU_STANDALONE
if (clip == NULL) {
return NULL;
}
GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, TEXTARGET_2D);
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_2D);
return *tex;
}
/* This only means RGBA16F instead of RGBA32F. */
const bool high_bitdepth = false;
const bool store_premultiplied = ibuf->rect_float ? false : true;
*tex = IMB_create_gpu_texture(ibuf, high_bitdepth, store_premultiplied);
/* Do not generate mips for movieclips... too slow. */
GPU_texture_mipmap_mode(*tex, false, true);
IMB_freeImBuf(ibuf);
return *tex;
#else
return NULL;
#endif
return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D);
}
GPUTexture *BKE_image_get_gpu_tiles(Image *image, ImageUser *iuser, ImBuf *ibuf)
{
return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D_ARRAY);
}
GPUTexture *BKE_image_get_gpu_tilemap(Image *image, ImageUser *iuser, ImBuf *ibuf)
{
return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_TILE_MAPPING);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \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;
static void gpu_free_unused_buffers(void)
{
if (gpu_texture_free_queue == NULL) {
return;
}
BLI_mutex_lock(&gpu_texture_queue_mutex);
if (gpu_texture_free_queue != NULL) {
GPUTexture *tex;
while ((tex = (GPUTexture *)BLI_linklist_pop(&gpu_texture_free_queue))) {
GPU_texture_free(tex);
}
gpu_texture_free_queue = NULL;
}
BLI_mutex_unlock(&gpu_texture_queue_mutex);
}
void BKE_image_free_unused_gpu_textures()
{
if (BLI_thread_is_main()) {
gpu_free_unused_buffers();
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Deletion
* \{ */
static void image_free_gpu(Image *ima, const bool immediate)
{
for (int eye = 0; eye < 2; eye++) {
for (int i = 0; i < TEXTARGET_COUNT; i++) {
if (ima->gputexture[i][eye] != NULL) {
if (immediate) {
GPU_texture_free(ima->gputexture[i][eye]);
}
else {
BLI_mutex_lock(&gpu_texture_queue_mutex);
BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]);
BLI_mutex_unlock(&gpu_texture_queue_mutex);
}
ima->gputexture[i][eye] = NULL;
}
}
}
ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
}
void BKE_image_free_gputextures(Image *ima)
{
image_free_gpu(ima, BLI_thread_is_main());
}
void BKE_image_free_all_gputextures(Main *bmain)
{
if (bmain) {
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
BKE_image_free_gputextures(ima);
}
}
}
/* same as above but only free animated images */
void BKE_image_free_anim_gputextures(Main *bmain)
{
if (bmain) {
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
if (BKE_image_is_animated(ima)) {
BKE_image_free_gputextures(ima);
}
}
}
}
void BKE_image_free_old_gputextures(Main *bmain)
{
static int lasttime = 0;
int ctime = (int)PIL_check_seconds_timer();
/*
* Run garbage collector once for every collecting period of time
* if textimeout is 0, that's the option to NOT run the collector
*/
if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) {
return;
}
/* of course not! */
if (G.is_rendering) {
return;
}
lasttime = ctime;
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) {
/* If it's in GL memory, deallocate and set time tag to current time
* This gives textures a "second chance" to be used before dying. */
if (BKE_image_has_opengl_texture(ima)) {
BKE_image_free_gputextures(ima);
ima->lastused = ctime;
}
/* Otherwise, just kill the buffers */
else {
BKE_image_free_buffers(ima);
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
@ -745,10 +612,6 @@ static void gpu_texture_update_unscaled(GPUTexture *tex,
static void gpu_texture_update_from_ibuf(
GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h)
{
/* Partial update of texture for texture painting. This is often much
* quicker than fully updating the texture for high resolution images. */
GPU_texture_bind(tex, 0);
bool scaled;
if (tile != NULL) {
int *tilesize = tile->runtime.tilearray_size;
@ -814,6 +677,8 @@ static void gpu_texture_update_from_ibuf(
}
}
GPU_texture_bind(tex, 0);
if (scaled) {
/* Slower update where we first have to scale the input pixels. */
if (tile != NULL) {
@ -850,7 +715,7 @@ static void gpu_texture_update_from_ibuf(
MEM_freeN(rect_float);
}
if (mipmap_enabled()) {
if (GPU_mipmap_enabled()) {
GPU_texture_generate_mipmap(tex);
}
else {
@ -860,7 +725,9 @@ static void gpu_texture_update_from_ibuf(
GPU_texture_unbind(tex);
}
void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
/* Partial update of texture for texture painting. This is often much
* quicker than fully updating the texture for high resolution images. */
void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
{
#ifndef GPU_STANDALONE
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
@ -868,7 +735,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
if ((ibuf == NULL) || (w == 0) || (h == 0)) {
/* Full reload of texture. */
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
}
GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0];
@ -891,9 +758,8 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i
* 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)
void BKE_image_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) {
@ -909,163 +775,13 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap)
}
}
else {
GPU_free_image(ima);
BKE_image_free_gputextures(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;
static void gpu_free_unused_buffers()
{
if (gpu_texture_free_queue == NULL) {
return;
}
BLI_mutex_lock(&gpu_texture_queue_mutex);
if (gpu_texture_free_queue != NULL) {
GPUTexture *tex;
while ((tex = (GPUTexture *)BLI_linklist_pop(&gpu_texture_free_queue))) {
GPU_texture_free(tex);
}
gpu_texture_free_queue = NULL;
}
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++) {
for (int i = 0; i < TEXTARGET_COUNT; i++) {
if (ima->gputexture[i][eye] != NULL) {
if (immediate) {
GPU_texture_free(ima->gputexture[i][eye]);
}
else {
BLI_mutex_lock(&gpu_texture_queue_mutex);
BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]);
BLI_mutex_unlock(&gpu_texture_queue_mutex);
}
ima->gputexture[i][eye] = NULL;
}
}
}
ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
}
void GPU_free_unused_buffers()
{
if (BLI_thread_is_main()) {
gpu_free_unused_buffers();
}
}
void GPU_free_image(Image *ima)
{
gpu_free_image(ima, BLI_thread_is_main());
}
void GPU_free_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) {
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
GPU_free_image(ima);
}
}
}
/* same as above but only free animated images */
void GPU_free_images_anim(Main *bmain)
{
if (bmain) {
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
if (BKE_image_is_animated(ima)) {
GPU_free_image(ima);
}
}
}
}
void GPU_free_images_old(Main *bmain)
{
static int lasttime = 0;
int ctime = (int)PIL_check_seconds_timer();
/*
* Run garbage collector once for every collecting period of time
* if textimeout is 0, that's the option to NOT run the collector
*/
if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) {
return;
}
/* of course not! */
if (G.is_rendering) {
return;
}
lasttime = ctime;
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) {
/* If it's in GL memory, deallocate and set time tag to current time
* This gives textures a "second chance" to be used before dying. */
if (BKE_image_has_opengl_texture(ima)) {
GPU_free_image(ima);
ima->lastused = ctime;
}
/* Otherwise, just kill the buffers */
else {
BKE_image_free_buffers(ima);
}
}
}
}
/** \} */
/** \} */

View File

@ -1867,3 +1867,84 @@ void BKE_movieclip_eval_selection_update(struct Depsgraph *depsgraph, MovieClip
DEG_debug_print_eval(depsgraph, __func__, clip->id.name, clip);
movieclip_selection_sync(clip, (MovieClip *)clip->id.orig_id);
}
/* -------------------------------------------------------------------- */
/** \name GPU textures
* \{ */
static GPUTexture **movieclip_get_gputexture_ptr(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;
}
GPUTexture *BKE_movieclip_get_gpu_texture(MovieClip *clip, MovieClipUser *cuser)
{
if (clip == NULL) {
return NULL;
}
GPUTexture **tex = movieclip_get_gputexture_ptr(clip, cuser, TEXTARGET_2D);
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(2, false);
return *tex;
}
/* This only means RGBA16F instead of RGBA32F. */
const bool high_bitdepth = false;
const bool store_premultiplied = ibuf->rect_float ? false : true;
*tex = IMB_create_gpu_texture(ibuf, high_bitdepth, store_premultiplied);
/* Do not generate mips for movieclips... too slow. */
GPU_texture_mipmap_mode(*tex, false, true);
IMB_freeImBuf(ibuf);
return *tex;
}
void BKE_movieclip_free_gputexture(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);
}
}
/** \} */

View File

@ -32,6 +32,8 @@
#include "DNA_world_types.h"
#include "IMB_imbuf.h"
#include "eevee_private.h"
#include "eevee_engine.h" /* own include */

View File

@ -63,7 +63,7 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
if (ibuf != NULL && ibuf->rect != NULL) {
gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, TEXTARGET_2D);
gpu_tex = BKE_image_get_gpu_texture(image, &iuser, ibuf);
*r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL);
}
BKE_image_release_ibuf(image, ibuf, lock);

View File

@ -175,7 +175,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
}
width = ibuf->x;
height = ibuf->y;
tex = GPU_texture_from_blender(image, iuser, ibuf, TEXTARGET_2D);
tex = BKE_image_get_gpu_texture(image, iuser, ibuf);
BKE_image_release_ibuf(image, ibuf, lock);
iuser->scene = NULL;
@ -203,7 +203,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
}
BKE_movieclip_user_set_frame(&bgpic->cuser, ctime);
tex = GPU_texture_from_movieclip(clip, &bgpic->cuser);
tex = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser);
if (tex == NULL) {
return NULL;
}
@ -232,7 +232,7 @@ static void OVERLAY_image_free_movieclips_textures(OVERLAY_Data *data)
LinkData *link;
while ((link = BLI_pophead(&data->stl->pd->bg_movie_clips))) {
MovieClip *clip = (MovieClip *)link->data;
GPU_free_movieclip(clip);
BKE_movieclip_free_gputexture(clip);
MEM_freeN(link);
}
}
@ -383,7 +383,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (ima != NULL) {
ImageUser iuser = *ob->iuser;
camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser);
tex = GPU_texture_from_blender(ima, &iuser, NULL, TEXTARGET_2D);
tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
if (tex) {
size[0] = GPU_texture_orig_width(tex);
size[1] = GPU_texture_orig_height(tex);

View File

@ -22,6 +22,8 @@
#include "DRW_render.h"
#include "BKE_image.h"
#include "DNA_mesh_types.h"
#include "DEG_depsgraph_query.h"
@ -136,7 +138,7 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state);
GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, TEXTARGET_2D);
GPUTexture *tex = BKE_image_get_gpu_texture(imapaint->stencil, NULL, NULL);
const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL);
const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0;

View File

@ -262,11 +262,11 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd,
if (ima) {
if (ima->source == IMA_SRC_TILED) {
tex = GPU_texture_from_blender(ima, iuser, NULL, TEXTARGET_2D_ARRAY);
tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, TEXTARGET_TILE_MAPPING);
tex = BKE_image_get_gpu_tiles(ima, iuser, NULL);
tex_tile_data = BKE_image_get_gpu_tilemap(ima, iuser, NULL);
}
else {
tex = GPU_texture_from_blender(ima, iuser, NULL, TEXTARGET_2D);
tex = BKE_image_get_gpu_texture(ima, iuser, NULL);
}
}

View File

@ -1293,13 +1293,10 @@ static DRWShadingGroup *drw_shgroup_material_create_ex(GPUPass *gpupass, DRWPass
}
static void drw_shgroup_material_texture(DRWShadingGroup *grp,
GPUMaterialTexture *tex,
GPUTexture *gputex,
const char *name,
eGPUSamplerState state,
eGPUTextureTarget textarget)
eGPUSamplerState state)
{
GPUTexture *gputex = GPU_texture_from_blender(tex->ima, tex->iuser, NULL, textarget);
DRW_shgroup_uniform_texture_ex(grp, name, gputex, state);
GPUTexture **gputex_ref = BLI_memblock_alloc(DST.vmempool->images);
@ -1315,15 +1312,16 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial
LISTBASE_FOREACH (GPUMaterialTexture *, tex, &textures) {
if (tex->ima) {
/* Image */
GPUTexture *gputex;
if (tex->tiled_mapping_name[0]) {
drw_shgroup_material_texture(
grp, tex, tex->sampler_name, tex->sampler_state, TEXTARGET_2D_ARRAY);
drw_shgroup_material_texture(
grp, tex, tex->tiled_mapping_name, tex->sampler_state, TEXTARGET_TILE_MAPPING);
gputex = BKE_image_get_gpu_tiles(tex->ima, tex->iuser, NULL);
drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state);
gputex = BKE_image_get_gpu_tilemap(tex->ima, tex->iuser, NULL);
drw_shgroup_material_texture(grp, gputex, tex->tiled_mapping_name, tex->sampler_state);
}
else {
drw_shgroup_material_texture(
grp, tex, tex->sampler_name, tex->sampler_state, TEXTARGET_2D);
gputex = BKE_image_get_gpu_texture(tex->ima, tex->iuser, NULL);
drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state);
}
}
else if (tex->colorband) {

View File

@ -60,7 +60,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "GPU_draw.h" /* GPU_free_image */
#include "GPU_draw.h" /* BKE_image_free_gputextures */
#include "WM_api.h"
#include "WM_types.h"
@ -530,7 +530,7 @@ static void multiresbake_freejob(void *bkv)
/* delete here, since this delete will be called from main thread */
for (link = data->images.first; link; link = link->next) {
Image *ima = (Image *)link->data;
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
}
MEM_freeN(data->ob_image.array);

View File

@ -308,7 +308,7 @@ static void refresh_images(BakeImages *bake_images)
Image *ima = bake_images->data[i].image;
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
if (tile->ok == IMA_OK_LOADED) {
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
break;
}

View File

@ -182,7 +182,7 @@ void imapaint_image_update(
int h = imapaintpartial.y2 - imapaintpartial.y1;
if (w && h) {
/* Testing with partial update in uv editor too */
GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h);
BKE_image_update_gputexture(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h);
}
}
}
@ -1164,9 +1164,9 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint);
if (U.glreslimit != 0) {
GPU_free_images(bmain);
BKE_image_free_all_gputextures(bmain);
}
GPU_paint_set_mipmap(bmain, 0);
BKE_image_paint_set_mipmap(bmain, 0);
toggle_paint_cursor(scene, true);
@ -1189,9 +1189,9 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob)
ob->mode &= ~OB_MODE_TEXTURE_PAINT;
if (U.glreslimit != 0) {
GPU_free_images(bmain);
BKE_image_free_all_gputextures(bmain);
}
GPU_paint_set_mipmap(bmain, 1);
BKE_image_paint_set_mipmap(bmain, 1);
toggle_paint_cursor(scene, false);
Mesh *me = BKE_mesh_from_object(ob);

View File

@ -1784,7 +1784,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final)
if (final) {
if (s->image && !(s->sima && s->sima->lock)) {
GPU_free_image(s->image);
BKE_image_free_gputextures(s->image);
}
/* compositor listener deals with updating */

View File

@ -6134,7 +6134,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
project_image_refresh_tagged(&ps);
for (a = 0; a < ps.image_tot; a++) {
GPU_free_image(ps.projImages[a].ima);
BKE_image_free_gputextures(ps.projImages[a].ima);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima);
}

View File

@ -2769,7 +2769,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
/* force GPU reupload, all image is invalid */
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@ -2860,7 +2860,7 @@ static int image_scale_exec(bContext *C, wmOperator *op)
ED_image_undo_push_end();
/* force GPU reupload, all image is invalid */
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
DEG_id_tag_update(&ima->id, 0);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);

View File

@ -295,7 +295,8 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
SWAP(uint *, ptile->rect.uint, tmpibuf->rect);
}
GPU_free_image(image); /* force OpenGL reload (maybe partial update will operate better?) */
BKE_image_free_gputextures(
image); /* force OpenGL reload (maybe partial update will operate better?) */
if (ibuf->rect_float) {
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
}
@ -570,7 +571,7 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
if (changed) {
BKE_image_mark_dirty(image, ibuf);
GPU_free_image(image); /* force OpenGL reload */
BKE_image_free_gputextures(image); /* force OpenGL reload */
if (ibuf->rect_float) {
ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */

View File

@ -36,6 +36,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_main.h"
@ -1617,7 +1618,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
view3d_draw_view(C, region);
DRW_cache_free_old_batches(bmain);
GPU_free_images_old(bmain);
BKE_image_free_old_gputextures(bmain);
GPU_pass_cache_garbage_collect();
/* XXX This is in order to draw UI batches with the DRW
@ -1707,7 +1708,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
{
/* free images which can have changed on frame-change
* warning! can be slow so only free animated images - campbell */
GPU_free_images_anim(G.main); /* XXX :((( */
BKE_image_free_anim_gputextures(G.main); /* XXX :((( */
}
GPU_matrix_push_projection();

View File

@ -80,7 +80,6 @@ set(SRC
intern/gpu_shader_interface.c
intern/gpu_state.cc
intern/gpu_texture.cc
intern/gpu_texture_image.c
intern/gpu_texture_fluid.c
intern/gpu_uniformbuffer.cc
intern/gpu_vertex_buffer.cc

View File

@ -26,29 +26,11 @@
#include "BLI_utildefines.h"
#include "DNA_image_types.h"
#include "DNA_object_enums.h"
#ifdef __cplusplus
extern "C" {
#endif
struct FluidModifierData;
struct GPUTexture;
struct Image;
struct ImageUser;
struct ImBuf;
struct Main;
struct MovieClip;
struct MovieClipUser;
/* Texture creation from blender datablocks. */
struct GPUTexture *GPU_texture_from_blender(struct Image *ima,
struct ImageUser *iuser,
struct ImBuf *ibuf,
eGPUTextureTarget target);
struct GPUTexture *GPU_texture_from_movieclip(struct MovieClip *clip, struct MovieClipUser *cuser);
/* Fluid simulation. */
void GPU_create_smoke(struct FluidModifierData *fmd, int highres);
@ -56,25 +38,9 @@ void GPU_create_smoke_coba_field(struct FluidModifierData *fmd);
void GPU_create_smoke_velocity(struct FluidModifierData *fmd);
/* Image updates and free. */
void GPU_free_image(struct Image *ima);
void GPU_free_movieclip(struct MovieClip *clip);
void GPU_free_smoke(struct FluidModifierData *fmd);
void GPU_free_smoke_velocity(struct FluidModifierData *fmd);
void GPU_free_images(struct Main *bmain);
void GPU_free_images_anim(struct Main *bmain);
void GPU_free_images_old(struct Main *bmain);
void GPU_paint_update_image(
struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h);
void GPU_paint_set_mipmap(struct Main *bmain, bool mipmap);
/* Delayed free of OpenGL buffers by main thread */
void GPU_free_unused_buffers(void);
/* For internal use. */
struct GPUTexture *GPU_texture_create_error(eGPUTextureTarget target);
#ifdef __cplusplus
}
#endif

View File

@ -52,6 +52,8 @@ bool GPU_context_local_shaders_workaround(void);
bool GPU_texture_copy_workaround(void);
bool GPU_crappy_amd_driver(void);
int GPU_texture_size_with_limit(int res);
bool GPU_mem_stats_supported(void);
void GPU_mem_stats_get(int *totalmem, int *freemem);

View File

@ -87,6 +87,7 @@ bool GPU_depth_mask_get(void);
void GPU_stencil_mask(uint stencil);
void GPU_unpack_row_length_set(uint len);
void GPU_clip_distances(int enabled_len);
bool GPU_mipmap_enabled(void);
void GPU_flush(void);
void GPU_finish(void);

View File

@ -233,6 +233,8 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint bu
GPUTexture *GPU_texture_create_compressed(
int w, int h, int miplen, eGPUTextureFormat format, const void *data);
GPUTexture *GPU_texture_create_error(int dimension, bool array);
void GPU_texture_add_mipmap(GPUTexture *tex,
eGPUDataFormat gpu_data_format,
int miplvl,

View File

@ -31,6 +31,8 @@
#include "BKE_global.h"
#include "MEM_guardedalloc.h"
#include "DNA_userdef_types.h"
#include "GPU_extensions.h"
#include "GPU_framebuffer.h"
#include "GPU_glew.h"
@ -239,6 +241,13 @@ bool GPU_crappy_amd_driver(void)
return GG.broken_amd_driver;
}
int GPU_texture_size_with_limit(int res)
{
int size = GPU_max_texture_size();
int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size;
return min_ii(reslimit, res);
}
void gpu_extensions_init(void)
{
/* during 2.8 development each platform has its own OpenGL minimum requirements

View File

@ -153,7 +153,6 @@ const struct GPUShaderConfigData GPU_shader_cfg_data[GPU_SHADER_CFG_LEN] = {
/* cache of built-in shaders (each is created on first use) */
static GPUShader *builtin_shaders[GPU_SHADER_CFG_LEN][GPU_SHADER_BUILTIN_LEN] = {{NULL}};
static int g_shader_builtin_srgb_transform = 0;
typedef struct {
const char *vert;

View File

@ -268,6 +268,12 @@ void GPU_clip_distances(int distances_new)
distances_enabled = distances_new;
}
bool GPU_mipmap_enabled(void)
{
/* TODO(fclem) this used to be a userdef option. */
return true;
}
/** \name GPU Push/Pop State
* \{ */

View File

@ -1118,15 +1118,15 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint
return tex;
}
static GLenum convert_target_to_gl(eGPUTextureTarget target)
static GLenum convert_target_to_gl(int dimension, bool is_array)
{
switch (target) {
case TEXTARGET_2D:
return GL_TEXTURE_2D;
case TEXTARGET_2D_ARRAY:
return GL_TEXTURE_2D_ARRAY;
case TEXTARGET_TILE_MAPPING:
return GL_TEXTURE_1D_ARRAY;
switch (dimension) {
case 1:
return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY;
case 2:
return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY;
case 3:
return GL_TEXTURE_3D;
default:
BLI_assert(0);
return GL_TEXTURE_2D;
@ -1134,9 +1134,9 @@ static GLenum convert_target_to_gl(eGPUTextureTarget target)
}
/* Create an error texture that will bind an invalid texture (pink) at draw time. */
GPUTexture *GPU_texture_create_error(eGPUTextureTarget target)
GPUTexture *GPU_texture_create_error(int dimension, bool is_array)
{
GLenum textarget = convert_target_to_gl(target);
GLenum textarget = convert_target_to_gl(dimension, is_array);
GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__);
tex->bindcode = 0;

View File

@ -23,6 +23,7 @@ set(INC
../blenkernel
../blenlib
../blenloader
../gpu
../makesdna
../makesrna
../../../intern/guardedalloc
@ -63,6 +64,7 @@ set(SRC
intern/thumbs_blend.c
intern/thumbs_font.c
intern/util.c
intern/util_gpu.c
intern/writeimage.c
IMB_colormanagement.h

View File

@ -88,6 +88,12 @@ struct GSet;
struct ImageFormatData;
struct Stereo3dFormat;
/**
*
* \attention defined in GPU_texture.h
*/
struct GPUTexture;
/**
*
* \attention Defined in allocimbuf.c
@ -727,6 +733,24 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines,
void IMB_ffmpeg_init(void);
const char *IMB_ffmpeg_last_error(void);
/**
*
* \attention defined in util_gpu.c
*/
void IMB_gpu_get_format(const struct ImBuf *ibuf,
bool high_bitdepth,
uint *r_data_format,
uint *r_texture_format);
void *IMB_gpu_get_data(const struct ImBuf *ibuf,
const bool do_rescale,
const int rescale_size[2],
const bool compress_as_srgb,
const bool store_premultiplied,
bool *r_freedata);
struct GPUTexture *IMB_create_gpu_texture(struct ImBuf *ibuf,
bool use_high_bitdepth,
bool use_premult);
/**
*
* \attention defined in stereoimbuf.c

View File

@ -0,0 +1,213 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
* util.c
*/
/** \file
* \ingroup imbuf
*/
#include "imbuf.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
#include "BKE_global.h"
#include "GPU_extensions.h"
#include "GPU_texture.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
/* gpu ibuf utils */
void IMB_gpu_get_format(const ImBuf *ibuf,
bool high_bitdepth,
uint *r_data_format /* eGPUDataFormat */,
uint *r_texture_format /* eGPUTextureFormat */)
{
const bool float_rect = (ibuf->rect_float != NULL);
const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) &&
!IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace));
high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
*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. */
#ifdef WITH_DDS
static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
{
/* 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_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1;
}
else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
*r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3;
}
else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
*r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5;
}
else {
return false;
}
return true;
}
#endif
/**
* Apply colormanagement and scale buffer if needed.
* *r_freedata is set to true if the returned buffer need to be manually freed.
**/
void *IMB_gpu_get_data(const ImBuf *ibuf,
const bool do_rescale,
const int rescale_size[2],
const bool compress_as_srgb,
const bool store_premultiplied,
bool *r_freedata)
{
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. */
if (ibuf->channels != 4 || !store_premultiplied) {
data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__);
*r_freedata = 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_freedata = 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. */
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_freedata = 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;
}
GPUTexture *IMB_create_gpu_texture(ImBuf *ibuf, bool use_high_bitdepth, bool use_premult)
{
GPUTexture *tex = NULL;
int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)};
bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]);
#ifdef WITH_DDS
if (ibuf->ftype == IMB_FTYPE_DDS) {
eGPUTextureFormat compressed_format;
if (!IMB_gpu_get_compressed_format(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_i(ibuf->x) || !is_power_of_2_i(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");
}
#endif
eGPUDataFormat data_format;
eGPUTextureFormat tex_format;
IMB_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8);
bool freebuf = false;
void *data = IMB_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &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 (freebuf) {
MEM_freeN(data);
}
return tex;
}

View File

@ -200,7 +200,7 @@ static void rna_Image_gpu_texture_update(Main *UNUSED(bmain),
Image *ima = (Image *)ptr->owner_id;
if (!G.background) {
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
}
WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id);
@ -516,7 +516,7 @@ static void rna_Image_pixels_set(PointerRNA *ptr, const float *values)
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID | IB_MIPMAP_INVALID;
BKE_image_mark_dirty(ima, ibuf);
if (!G.background) {
GPU_free_image(ima);
BKE_image_free_gputextures(ima);
}
WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id);
}

View File

@ -220,7 +220,7 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame)
BKE_imageuser_default(&iuser);
iuser.framenr = frame;
GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, TEXTARGET_2D);
GPUTexture *tex = BKE_image_get_gpu_texture(image, &iuser, NULL);
if (tex == NULL) {
BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2);
@ -246,7 +246,7 @@ static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame)
static void rna_Image_gl_free(Image *image)
{
GPU_free_image(image);
BKE_image_free_gputextures(image);
/* remove the nocollect flag, image is available for garbage collection again */
image->flag &= ~IMA_NOCOLLECT;

View File

@ -179,6 +179,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "BKE_blender.h"
# include "BKE_global.h"
# include "BKE_idprop.h"
# include "BKE_image.h"
# include "BKE_main.h"
# include "BKE_mesh_runtime.h"
# include "BKE_paint.h"
@ -371,7 +372,7 @@ static void rna_userdef_anisotropic_update(Main *bmain, Scene *scene, PointerRNA
static void rna_userdef_gl_texture_limit_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
GPU_free_images(bmain);
BKE_image_free_all_gputextures(bmain);
rna_userdef_update(bmain, scene, ptr);
}

View File

@ -999,7 +999,7 @@ void wm_draw_update(bContext *C)
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win;
GPU_free_unused_buffers();
BKE_image_free_unused_gpu_textures();
for (win = wm->windows.first; win; win = win->next) {
#ifdef WIN32

View File

@ -59,6 +59,7 @@
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_image.h"
#include "BKE_keyconfig.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
@ -575,7 +576,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_subdiv_exit();
if (opengl_is_init) {
GPU_free_unused_buffers();
BKE_image_free_unused_gpu_textures();
}
BKE_blender_free(); /* blender.c, does entire library and spacetypes */