diff options
author | Lukas Stockner <lukas.stockner@freenet.de> | 2020-01-16 04:21:32 +0300 |
---|---|---|
committer | Lukas Stockner <lukas.stockner@freenet.de> | 2020-01-16 04:21:32 +0300 |
commit | 7f571aad22724a1b0cb2427ee5e6829580050140 (patch) | |
tree | 331f0ed0de3b8c32449a5ac1b0d94aeb16f0cf91 /source/blender/gpu | |
parent | eca8bae6718d2cba788713bfb677f92f2fa3ca4e (diff) | |
parent | 7d8a186335400cb7ad69d24aab89e7a215a70348 (diff) |
Merge branch 'blender-v2.82-release'
Diffstat (limited to 'source/blender/gpu')
-rw-r--r-- | source/blender/gpu/GPU_material.h | 12 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.c | 26 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.h | 5 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_draw.c | 564 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_texture.c | 12 | ||||
-rw-r--r-- | source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl | 95 |
6 files changed, 572 insertions, 142 deletions
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index a5363c7a42c..8c166ed6b64 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -71,12 +71,13 @@ typedef enum eGPUType { /* Values not in GPU_DATATYPE_STR */ GPU_TEX1D_ARRAY = 1001, GPU_TEX2D = 1002, - GPU_TEX3D = 1003, - GPU_SHADOW2D = 1004, - GPU_TEXCUBE = 1005, + GPU_TEX2D_ARRAY = 1003, + GPU_TEX3D = 1004, + GPU_SHADOW2D = 1005, + GPU_TEXCUBE = 1006, /* GLSL Struct types */ - GPU_CLOSURE = 1006, + GPU_CLOSURE = 1007, /* Opengl Attributes */ GPU_ATTR = 3001, @@ -142,7 +143,8 @@ typedef enum eGPUMaterialStatus { GPUNodeLink *GPU_attribute(CustomDataType type, const char *name); GPUNodeLink *GPU_constant(float *num); GPUNodeLink *GPU_uniform(float *num); -GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, int tile); +GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser); +GPUNodeLink *GPU_image_tilemap(struct Image *ima, struct ImageUser *iuser); GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *layer); GPUNodeLink *GPU_builtin(eGPUBuiltin builtin); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index ca804a26acd..1da7274b2cd 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -279,6 +279,9 @@ static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) if (!type && gpu_str_prefix(code, "sampler1DArray")) { type = GPU_TEX1D_ARRAY; } + if (!type && gpu_str_prefix(code, "sampler2DArray")) { + type = GPU_TEX2D_ARRAY; + } if (!type && gpu_str_prefix(code, "sampler2D")) { type = GPU_TEX2D; } @@ -618,7 +621,7 @@ static void codegen_set_unique_ids(ListBase *nodes) input->bindtex = false; if (input->ima) { /* input is texture from image */ - codegen_set_texid(bindhash, input, &texid, input->ima, input->image_tile); + codegen_set_texid(bindhash, input, &texid, input->ima, input->type); } else if (input->coba) { /* input is color band texture, check coba pointer */ @@ -657,10 +660,18 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, if (input->source == GPU_SOURCE_TEX) { /* create exactly one sampler for each texture */ if (codegen_input_has_texture(input) && input->bindtex) { - BLI_dynstr_appendf(ds, - "uniform %s samp%d;\n", - (input->coba) ? "sampler1DArray" : "sampler2D", - input->texid); + const char *type; + if (input->coba || input->type == GPU_TEX1D_ARRAY) { + type = "sampler1DArray"; + } + else if (input->type == GPU_TEX2D_ARRAY) { + type = "sampler2DArray"; + } + else { + BLI_assert(input->type == GPU_TEX2D); + type = "sampler2D"; + } + BLI_dynstr_appendf(ds, "uniform %s samp%d;\n", type, input->texid); } } else if (input->source == GPU_SOURCE_BUILTIN) { @@ -1544,10 +1555,10 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->coba = link->coba; break; case GPU_NODE_LINK_IMAGE_BLENDER: + case GPU_NODE_LINK_IMAGE_TILEMAP: input->source = GPU_SOURCE_TEX; input->ima = link->ima; input->iuser = link->iuser; - input->image_tile = link->image_tile; break; case GPU_NODE_LINK_ATTR: input->source = GPU_SOURCE_ATTR; @@ -1792,13 +1803,12 @@ GPUNodeLink *GPU_uniform(float *num) return link; } -GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, int tile) +GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser) { GPUNodeLink *link = GPU_node_link_create(); link->link_type = GPU_NODE_LINK_IMAGE_BLENDER; link->ima = ima; link->iuser = iuser; - link->image_tile = tile; return link; } diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 0e6982c603e..f8e1b76580f 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -59,6 +59,7 @@ typedef enum { GPU_NODE_LINK_COLORBAND, GPU_NODE_LINK_CONSTANT, GPU_NODE_LINK_IMAGE_BLENDER, + GPU_NODE_LINK_IMAGE_TILEMAP, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, } GPUNodeLinkType; @@ -95,11 +96,10 @@ struct GPUNodeLink { const char *attr_name; CustomDataType attr_type; }; - /* GPU_NODE_LINK_IMAGE_BLENDER */ + /* GPU_NODE_LINK_IMAGE_BLENDER | GPU_NODE_LINK_IMAGE_TILEMAP */ struct { struct Image *ima; struct ImageUser *iuser; - int image_tile; }; }; }; @@ -139,7 +139,6 @@ typedef struct GPUInput { struct ImageUser *iuser; /* image user */ bool bindtex; /* input is responsible for binding the texture? */ int texid; /* number for multitexture, starting from zero */ - int image_tile; /* image tile */ eGPUType textype; /* texture type (2D, 1D Array ...) */ }; /* GPU_SOURCE_ATTR */ diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 95738bb1a95..d2008680098 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -31,6 +31,7 @@ #include <string.h> #include "BLI_blenlib.h" +#include "BLI_boxpack_2d.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_threads.h" @@ -71,7 +72,7 @@ static bool is_power_of_2_resolution(int w, int h) static bool is_over_resolution_limit(GLenum textarget, int w, int h) { - int size = (textarget == GL_TEXTURE_2D) ? GPU_max_texture_size() : GPU_max_cube_map_size(); + int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size(); int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; return (w > reslimit || h > reslimit); @@ -179,18 +180,294 @@ float GPU_get_anisotropic(void) /* Set OpenGL state for an MTFace */ -static GPUTexture **gpu_get_tile_gputexture(ImageTile *tile, GLenum textarget) +static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget) { if (textarget == GL_TEXTURE_2D) { - return &tile->gputexture[TEXTARGET_TEXTURE_2D]; + return &ima->gputexture[TEXTARGET_TEXTURE_2D]; } else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &tile->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; + return &ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; + } + else if (textarget == GL_TEXTURE_2D_ARRAY) { + return &ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]; + } + else if (textarget == GL_TEXTURE_1D_ARRAY) { + return &ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING]; } return NULL; } +static uint gpu_texture_create_tile_mapping(Image *ima) +{ + GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]; + if (tilearray == NULL) { + return 0; + } + + float array_w = GPU_texture_width(tilearray); + float array_h = GPU_texture_height(tilearray); + + ImageTile *last_tile = ima->tiles.last; + /* Tiles are sorted by number. */ + 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 = MEM_callocN(width * 8 * sizeof(float), __func__); + for (int i = 0; i < width; i++) { + data[4 * i] = -1.0f; + } + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int i = tile->tile_number - 1001; + data[4 * i] = tile->runtime.tilearray_layer; + + float *tile_info = &data[4 * width + 4 * i]; + tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; + tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; + tile_info[2] = tile->runtime.tilearray_size[0] / array_w; + 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); + 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; +} + +typedef struct PackTile { + FixedSizeBoxPack boxpack; + ImageTile *tile; + float pack_score; +} PackTile; + +static int compare_packtile(const void *a, const void *b) +{ + const PackTile *tile_a = a; + const PackTile *tile_b = b; + + return tile_a->pack_score < tile_b->pack_score; +} + +static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +{ + int arraywidth = 0, arrayheight = 0; + + ListBase boxes = {NULL}; + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf) { + PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__); + packtile->tile = tile; + 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)) { + packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); + packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); + } + arraywidth = max_ii(arraywidth, packtile->boxpack.w); + arrayheight = max_ii(arrayheight, packtile->boxpack.h); + + /* We sort the tiles by decreasing size, with an additional penalty term + * for high aspect ratios. This improves packing efficiency. */ + float w = packtile->boxpack.w, h = packtile->boxpack.h; + packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; + + BKE_image_release_ibuf(ima, ibuf, NULL); + BLI_addtail(&boxes, packtile); + } + } + + BLI_assert(arraywidth > 0 && arrayheight > 0); + + BLI_listbase_sort(&boxes, compare_packtile); + int arraylayers = 0; + /* Keep adding layers until all tiles are packed. */ + while (boxes.first != NULL) { + ListBase packed = {NULL}; + BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); + BLI_assert(packed.first != NULL); + + LISTBASE_FOREACH (PackTile *, packtile, &packed) { + ImageTile *tile = packtile->tile; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + tileoffset[0] = packtile->boxpack.x; + tileoffset[1] = packtile->boxpack.y; + tilesize[0] = packtile->boxpack.w; + tilesize[1] = packtile->boxpack.h; + tile->runtime.tilearray_layer = arraylayers; + } + + BLI_freelistN(&packed); + arraylayers++; + } + + /* create image */ + int bindcode; + glGenTextures(1, (GLuint *)&bindcode); + glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode); + + GLenum data_type, internal_format; + if (main_ibuf->rect_float) { + data_type = GL_FLOAT; + internal_format = 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; + } + } + + glTexImage3D(GL_TEXTURE_2D_ARRAY, + 0, + internal_format, + arraywidth, + arrayheight, + arraylayers, + 0, + GL_RGBA, + data_type, + NULL); + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int tilelayer = tile->runtime.tilearray_layer; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + if (tilesize[0] == 0 || tilesize[1] == 0) { + continue; + } + + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + BLI_assert(ibuf != NULL); + + bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); + + ImBuf *scale_ibuf = NULL; + if (ibuf->rect_float) { + float *rect_float = ibuf->rect_float; + + const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; + if (ibuf->channels != 4 || !store_premultiplied) { + rect_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 = 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); + } + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); + + if (GPU_get_mipmap()) { + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; + } + } + else { + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + if (GLEW_EXT_texture_filter_anisotropic) { + glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY_EXT, GPU_get_anisotropic()); + } + + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + + return bindcode; +} + static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) { uint bindcode = 0; @@ -305,48 +582,105 @@ static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, return NULL; } -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) +static ImBuf *update_do_scale(uchar *rect, + float *rect_float, + int *x, + int *y, + int *w, + int *h, + int limit_w, + int limit_h, + int full_w, + int full_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); float xratio = limit_w / (float)full_w; float yratio = limit_h / (float)full_h; + int part_w = *w, part_h = *h; + /* Find sub coordinates in scaled image. Take ceiling because we will be * losing 1 pixel due to rounding errors in x,y. */ - int sub_x = x * xratio; - int sub_y = y * yratio; - int sub_w = (int)ceil(xratio * w); - int sub_h = (int)ceil(yratio * h); + *x *= xratio; + *y *= yratio; + *w = (int)ceil(xratio * (*w)); + *h = (int)ceil(yratio * (*h)); /* ...but take back if we are over the limit! */ - if (sub_w + sub_x > limit_w) { - sub_w--; + if (*x + *w > limit_w) { + (*w)--; } - if (sub_h + sub_y > limit_h) { - sub_h--; + if (*y + *h > limit_h) { + (*h)--; } /* Scale pixels. */ - ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, w, h, 4); - IMB_scaleImBuf(ibuf, sub_w, sub_h); + ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); + IMB_scaleImBuf(ibuf, *w, *h); + + 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) +{ + ImBuf *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) { - glTexSubImage2D( - GL_TEXTURE_2D, 0, sub_x, sub_y, sub_w, sub_h, GL_RGBA, GL_FLOAT, ibuf->rect_float); + glTexSubImage3D( + GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float); } else { - glTexSubImage2D( - GL_TEXTURE_2D, 0, sub_x, sub_y, sub_w, sub_h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glTexSubImage3D( + GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); } IMB_freeImBuf(ibuf); } -static void gpu_texture_update_unscaled( - uchar *rect, float *rect_float, int x, int y, int w, int h, GLint tex_stride, GLint tex_offset) +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, + float *rect_float, + int x, + int y, + int layer, + int w, + int h, + GLint tex_stride, + GLint tex_offset) { /* Partial update without scaling. Stride and offset are used to copy only a * subset of a possible larger buffer than what we are updating. */ @@ -354,22 +688,61 @@ static void gpu_texture_update_unscaled( glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length); glPixelStorei(GL_UNPACK_ROW_LENGTH, tex_stride); - if (rect_float == NULL) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); + 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 { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); + 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); + } } glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); } -static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) +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. - * Assumes the OpenGL texture is bound to 0. */ - const bool scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); + * 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; + scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); + } + else { + scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); + } if (scaled) { /* Extra padding to account for bleed from neighboring pixels. */ @@ -429,11 +802,35 @@ static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, if (scaled) { /* Slower update where we first have to scale the input pixels. */ - gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); + if (tile != NULL) { + 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); + } + else { + gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); + } } else { /* Fast update at same resolution. */ - gpu_texture_update_unscaled(rect, rect_float, x, y, w, h, tex_stride, tex_offset); + 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); + } + else { + gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset); + } } /* Free buffers if needed. */ @@ -443,6 +840,15 @@ static void gpu_texture_update_from_ibuf(Image *ima, ImBuf *ibuf, int x, int y, if (rect_float && rect_float != ibuf->rect_float) { MEM_freeN(rect_float); } + + if (GPU_get_mipmap()) { + glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); + } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } + + GPU_texture_unbind(tex); } GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget) @@ -460,19 +866,8 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget /* Tag as in active use for garbage collector. */ BKE_image_tag_time(ima); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if (tile == NULL) { - /* TODO(lukas): When a tile gets deleted, the materials using the image - * aren't rebuilt and therefore continue to use it. - * This workaround isn't ideal, the result should be a pink color - * (for a missing tile). With the current behavior, new tiles also won't - * be detected. */ - tile = BKE_image_get_tile(ima, 0); - } - /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_tile_gputexture(tile, textarget); + GPUTexture **tex = gpu_get_image_gputexture(ima, textarget); if (*tex) { return *tex; } @@ -480,6 +875,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget /* 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->ok == 0) { *tex = GPU_texture_from_bindcode(textarget, bindcode); return *tex; @@ -492,7 +888,15 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget return *tex; } - bindcode = gpu_texture_create_from_ibuf(ima, ibuf, textarget); + if (textarget == GL_TEXTURE_2D_ARRAY) { + bindcode = gpu_texture_create_tile_array(ima, ibuf); + } + else if (textarget == GL_TEXTURE_1D_ARRAY) { + bindcode = gpu_texture_create_tile_mapping(ima); + } + else { + bindcode = gpu_texture_create_from_ibuf(ima, ibuf, textarget); + } BKE_image_release_ibuf(ima, ibuf, NULL); @@ -856,13 +1260,15 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_opengl_texture(ima)) { if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; - 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); + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { + GPUTexture *tex = ima->gputexture[a]; + 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); + } } } } @@ -878,13 +1284,15 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) else { for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_opengl_texture(ima)) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; - 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); + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { + GPUTexture *tex = ima->gputexture[a]; + 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); + } } } } @@ -899,26 +1307,22 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; - if ((tex == NULL) || (ibuf == NULL) || (w == 0) || (h == 0)) { + if ((ibuf == NULL) || (w == 0) || (h == 0)) { /* Full reload of texture. */ GPU_free_image(ima); } - else { - /* Partial update of texture. */ - GPU_texture_bind(tex, 0); - gpu_texture_update_from_ibuf(ima, ibuf, x, y, w, h); - - if (GPU_get_mipmap()) { - glGenerateMipmap(GL_TEXTURE_2D); - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } + GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D]; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); + } - GPU_texture_unbind(tex); + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } BKE_image_release_ibuf(ima, ibuf, NULL); @@ -960,13 +1364,11 @@ void GPU_free_unused_buffers(Main *bmain) static void gpu_free_image_immediate(Image *ima) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (tile->gputexture[i] != NULL) { - GPU_texture_free(tile->gputexture[i]); - tile->gputexture[i] = NULL; - } + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* free glsl image binding */ + if (ima->gputexture[i] != NULL) { + GPU_texture_free(ima->gputexture[i]); + ima->gputexture[i] = NULL; } } diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 1feb5b7732d..84328b8dfd4 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -1032,10 +1032,6 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) { - /* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP - * these values are correct for glDisable, so textarget can be safely used in - * GPU_texture_bind/GPU_texture_unbind through tex->target_base */ - /* (is any of this obsolete now that we don't glEnable/Disable textures?) */ GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->bindcode = bindcode; tex->number = -1; @@ -1052,12 +1048,8 @@ GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) else { GLint w, h; - GLenum gettarget; - - if (textarget == GL_TEXTURE_2D) { - gettarget = GL_TEXTURE_2D; - } - else { + GLenum gettarget = textarget; + if (textarget == GL_TEXTURE_CUBE_MAP) { gettarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl index fadb3b92df4..4633e59fde1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_image.glsl @@ -354,67 +354,92 @@ void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) alpha = 0.0; } -void node_tex_tile_map(vec3 co, out vec4 color, out vec3 map) +bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) { - float tx = floor(co.x); - float ty = floor(co.y); + vec2 tile_pos = floor(co.xy); - if (tx < 0 || ty < 0 || tx >= 10) - map = vec3(0, 0, -1); - else - map = vec3(co.x - tx, co.y - ty, 1001 + 10 * ty + tx); + if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) + return false; - color = vec4(1.0, 0.0, 1.0, 1.0); + float tile = 10 * tile_pos.y + tile_pos.x; + if (tile >= textureSize(map, 0).x) + return false; + + /* Fetch tile information. */ + float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x; + if (tile_layer < 0) + return false; + + vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0); + + co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); + return true; } void node_tex_tile_linear( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_linear(co, ima, color, alpha); + if (node_tex_tile_lookup(co, ima, map)) { + color = safe_color(texture(ima, co)); } else { - color = in_color; - alpha = color.a; + color = vec4(1.0, 0.0, 1.0, 1.0); } + + alpha = color.a; } void node_tex_tile_nearest( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_nearest(co, ima, color, alpha); + if (node_tex_tile_lookup(co, ima, map)) { + ivec3 pix = ivec3(fract(co.xy) * textureSize(ima, 0).xy, co.z); + color = safe_color(texelFetch(ima, pix, 0)); } else { - color = in_color; - alpha = color.a; + color = vec4(1.0, 0.0, 1.0, 1.0); } + + alpha = color.a; } void node_tex_tile_cubic( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_cubic(co, ima, color, alpha); + if (node_tex_tile_lookup(co, ima, map)) { + vec2 tex_size = vec2(textureSize(ima, 0).xy); + + co.xy *= tex_size; + /* texel center */ + vec2 tc = floor(co.xy - 0.5) + 0.5; + vec2 w0, w1, w2, w3; + cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3); + + vec2 s0 = w0 + w1; + vec2 s1 = w2 + w3; + + vec2 f0 = w1 / (w0 + w1); + vec2 f1 = w3 / (w2 + w3); + + vec4 final_co; + final_co.xy = tc - 1.0 + f0; + final_co.zw = tc + 1.0 + f1; + final_co /= tex_size.xyxy; + + color = safe_color(textureLod(ima, vec3(final_co.xy, co.z), 0.0)) * s0.x * s0.y; + color += safe_color(textureLod(ima, vec3(final_co.zy, co.z), 0.0)) * s1.x * s0.y; + color += safe_color(textureLod(ima, vec3(final_co.xw, co.z), 0.0)) * s0.x * s1.y; + color += safe_color(textureLod(ima, vec3(final_co.zw, co.z), 0.0)) * s1.x * s1.y; } else { - color = in_color; - alpha = color.a; + color = vec4(1.0, 0.0, 1.0, 1.0); } + + alpha = color.a; } void node_tex_tile_smart( - vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) + vec3 co, sampler2DArray ima, sampler1DArray map, out vec4 color, out float alpha) { - if (map.z == tile_id) { - vec3 co = map.xyy; - node_tex_image_smart(co, ima, color, alpha); - } - else { - color = in_color; - alpha = color.a; - } + node_tex_tile_cubic(co, ima, map, color, alpha); } |