diff options
Diffstat (limited to 'source/blender/gpu')
15 files changed, 672 insertions, 273 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 8daeda67c80..25f9ef886e9 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -129,6 +129,7 @@ set(SRC ) set(LIB + ${BLENDER_GL_LIBRARIES} ) if(NOT WITH_SYSTEM_GLEW) 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/GPU_texture.h b/source/blender/gpu/GPU_texture.h index a8e8ca72023..bb6af227369 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -36,6 +36,7 @@ struct ImageUser; struct MovieClip; struct MovieClipUser; struct PreviewImage; +struct ImBuf; struct GPUFrameBuffer; typedef struct GPUTexture GPUTexture; @@ -187,7 +188,10 @@ 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(int textarget, int bindcode); -GPUTexture *GPU_texture_from_blender(struct Image *ima, struct ImageUser *iuser, int textarget); +GPUTexture *GPU_texture_from_blender(struct Image *ima, + struct ImageUser *iuser, + struct ImBuf *ibuf, + int textarget); GPUTexture *GPU_texture_from_preview(struct PreviewImage *prv, int mipmap); /* movie clip drawing */ diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 2c74afd2d8e..bf7b1908321 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -116,7 +116,7 @@ void gpu_pbvh_init() g_vbo_id.msk = GPU_vertformat_attr_add( &g_vbo_id.format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_vbo_id.col = GPU_vertformat_attr_add( - &g_vbo_id.format, "ac", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + &g_vbo_id.format, "ac", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); } } @@ -240,8 +240,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, for (int j = 0; j < 3; j++) { const int loop_index = lt->tri[j]; const int vidx = face_vert_indices[i][j]; - const uchar *elem = &vcol[loop_index].r; - GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, elem); + const MLoopCol *mcol = &vcol[loop_index]; + ushort scol[4]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, scol); } } } @@ -289,8 +294,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, if (show_vcol) { const uint loop_index = lt->tri[j]; - const uchar *elem = &vcol[loop_index].r; - memcpy(GPU_vertbuf_raw_step(&col_step), elem, sizeof(uchar) * 4); + const MLoopCol *mcol = &vcol[loop_index]; + ushort scol[4]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); } } } @@ -654,7 +664,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, } if (show_vcol) { - char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, &vcol); } @@ -705,7 +715,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, empty_mask = empty_mask && (fmask == 0.0f); } - char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 0, &vcol); GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 1, &vcol); GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 2, &vcol); @@ -781,7 +791,7 @@ static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, } if (show_vcol) { - static char vcol[4] = {255, 255, 255, 255}; + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col, v_index, &vcol); } } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index ca804a26acd..a7dd34a5f96 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) { @@ -1031,12 +1042,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u input->attr_id, attr_prefix_get(input->attr_type), attr_safe_name); - /* Auto attribute can be vertex color byte buffer. - * We need to know and convert them to linear space in VS. */ - if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, "uniform bool ba%s;\n", attr_safe_name); - BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%s\n", input->attr_id, attr_safe_name); - } } BLI_dynstr_appendf(ds, "out %s var%d%s;\n", @@ -1090,24 +1095,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n"); - BLI_dynstr_append(ds, - "#define USE_ATTR\n" - "vec3 srgb_to_linear_attr(vec3 c) {\n" - "\tc = max(c, vec3(0.0));\n" - "\tvec3 c1 = c * (1.0 / 12.92);\n" - "\tvec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));\n" - "\treturn mix(c1, c2, step(vec3(0.04045), c));\n" - "}\n\n"); - - BLI_dynstr_append(ds, - "vec4 srgba_to_linear_attr(vec4 c) {\n" - "\tc = max(c, vec4(0.0));\n" - "\tvec4 c1 = c * (1.0 / 12.92);\n" - "\tvec4 c2 = pow((c + 0.055) * (1.0 / 1.055), vec4(2.4));\n" - "\tvec4 final = mix(c1, c2, step(vec4(0.04045), c));" - "\treturn vec4(final.xyz, c.a);\n" - "}\n\n"); - /* Prototype because defined later. */ BLI_dynstr_append(ds, "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" @@ -1213,22 +1200,6 @@ static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool u input->attr_id, use_geom ? "g" : ""); } - else if (input->attr_type == CD_MCOL) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = srgba_to_linear_attr(att%d);\n", - input->attr_id, - use_geom ? "g" : "", - input->attr_id); - } - else if (input->attr_type == CD_AUTO_FROM_NAME) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = (att%d_is_srgb) ? srgb_to_linear_attr(att%d) : att%d;\n", - input->attr_id, - use_geom ? "g" : "", - input->attr_id, - input->attr_id, - input->attr_id); - } else { BLI_dynstr_appendf( ds, "\tvar%d%s = att%d;\n", input->attr_id, use_geom ? "g" : "", input->attr_id); @@ -1329,17 +1300,12 @@ static char *code_generate_geometry(ListBase *nodes, const char *geom_code, cons "barycentricPosg[2]);\n"); } - BLI_dynstr_append(ds, "\tgl_Position = gl_in[0].gl_Position;\n"); - BLI_dynstr_append(ds, "\tpass_attr(0);\n"); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - - BLI_dynstr_append(ds, "\tgl_Position = gl_in[1].gl_Position;\n"); - BLI_dynstr_append(ds, "\tpass_attr(1);\n"); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - - BLI_dynstr_append(ds, "\tgl_Position = gl_in[2].gl_Position;\n"); - BLI_dynstr_append(ds, "\tpass_attr(2);\n"); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); + for (int i = 0; i < 3; i++) { + BLI_dynstr_appendf(ds, "\tgl_Position = gl_in[%d].gl_Position;\n", i); + BLI_dynstr_appendf(ds, "\tgl_ClipDistance[0] = gl_in[%d].gl_ClipDistance[0];\n", i); + BLI_dynstr_appendf(ds, "\tpass_attr(%d);\n", i); + BLI_dynstr_append(ds, "\tEmitVertex();\n"); + } BLI_dynstr_append(ds, "}\n"); } } @@ -1544,10 +1510,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 +1758,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..cfeb7f6bedb 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,9 +840,22 @@ 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) +/* 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, int textarget) { if (ima == NULL) { return NULL; @@ -460,19 +870,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,25 +879,40 @@ 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; } /* check if we have a valid image buffer */ - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; + 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; + } } - bindcode = gpu_texture_create_from_ibuf(ima, ibuf, textarget); + if (textarget == GL_TEXTURE_2D_ARRAY) { + bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); + } + else if (textarget == GL_TEXTURE_1D_ARRAY) { + bindcode = gpu_texture_create_tile_mapping(ima); + } + else { + bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget); + } - BKE_image_release_ibuf(ima, ibuf, NULL); + /* 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->x, ibuf->y); + GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); return *tex; } @@ -856,13 +1270,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 +1294,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 +1317,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 +1374,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_platform.c b/source/blender/gpu/intern/gpu_platform.c index 871052bb070..a758787466f 100644 --- a/source/blender/gpu/intern/gpu_platform.c +++ b/source/blender/gpu/intern/gpu_platform.c @@ -158,7 +158,8 @@ void gpu_platform_init(void) if (strstr(renderer, "UHD Graphics") || /* Not UHD but affected by the same bugs. */ - strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2")) { + strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") || + strstr(renderer, "Whiskey Lake")) { GPG.device |= GPU_DEVICE_INTEL_UHD; } } diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 497fc13a2c8..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; } @@ -1072,56 +1064,6 @@ GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) return tex; } -GPUTexture *GPU_texture_from_preview(PreviewImage *prv, int mipmap) -{ - GPUTexture *tex = prv->gputexture[0]; - GLuint bindcode = 0; - - if (tex) { - bindcode = tex->bindcode; - } - - /* this binds a texture, so that's why we restore it to 0 */ - if (bindcode == 0) { - GPU_create_gl_tex( - &bindcode, prv->rect[0], NULL, prv->w[0], prv->h[0], GL_TEXTURE_2D, mipmap, false, NULL); - } - if (tex) { - tex->bindcode = bindcode; - glBindTexture(GL_TEXTURE_2D, 0); - return tex; - } - - tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; - tex->number = -1; - tex->refcount = 1; - tex->target = GL_TEXTURE_2D; - tex->target_base = GL_TEXTURE_2D; - tex->format = -1; - tex->components = -1; - - prv->gputexture[0] = tex; - - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); - } - else { - GLint w, h; - - glBindTexture(GL_TEXTURE_2D, tex->bindcode); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - - tex->w = w; - tex->h = h; - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return tex; -} - GPUTexture *GPU_texture_create_1d(int w, eGPUTextureFormat tex_format, const float *pixels, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl index de3be98b715..94f69d35b7e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl @@ -106,7 +106,7 @@ void math_fraction(float a, float b, float c, out float result) void math_modulo(float a, float b, float c, out float result) { - result = c_mod(a, b); + result = compatible_fmod(a, b); } void math_trunc(float a, float b, float c, out float result) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl index e8487fb5d42..df1c0479159 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl @@ -5,10 +5,11 @@ float safe_divide(float a, float b) return (b != 0.0) ? a / b : 0.0; } -/* Modulo with C sign convention. mod in GLSL will take absolute for negative numbers. */ -float c_mod(float a, float b) +/* fmod function compatible with OSL using nvidia reference example. */ +float compatible_fmod(float a, float b) { - return (b != 0.0 && a != b) ? sign(a) * mod(abs(a), b) : 0.0; + float c = (b != 0.0) ? fract(abs(a / b)) * abs(b) : 0.0; + return (a < 0.0) ? -c : c; } float compatible_pow(float x, float y) @@ -88,9 +89,9 @@ vec4 safe_divide(vec4 a, float b) return (b != 0.0) ? a / b : vec4(0.0); } -vec3 c_mod(vec3 a, vec3 b) +vec3 compatible_fmod(vec3 a, vec3 b) { - return vec3(c_mod(a.x, b.x), c_mod(a.y, b.y), c_mod(a.z, b.z)); + return vec3(compatible_fmod(a.x, b.x), compatible_fmod(a.y, b.y), compatible_fmod(a.z, b.z)); } void invert_z(vec3 v, out vec3 outv) 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); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl index fce511deb79..b11d13a0413 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl @@ -1,21 +1,25 @@ /* White Noise */ -void node_white_noise_1d(vec3 vector, float w, out float value) +void node_white_noise_1d(vec3 vector, float w, out float value, out vec4 color) { value = hash_float_to_float(w); + color.xyz = hash_float_to_vec3(w); } -void node_white_noise_2d(vec3 vector, float w, out float value) +void node_white_noise_2d(vec3 vector, float w, out float value, out vec4 color) { value = hash_vec2_to_float(vector.xy); + color.xyz = hash_vec2_to_vec3(vector.xy); } -void node_white_noise_3d(vec3 vector, float w, out float value) +void node_white_noise_3d(vec3 vector, float w, out float value, out vec4 color) { value = hash_vec3_to_float(vector); + color.xyz = hash_vec3_to_vec3(vector); } -void node_white_noise_4d(vec3 vector, float w, out float value) +void node_white_noise_4d(vec3 vector, float w, out float value, out vec4 color) { value = hash_vec4_to_float(vec4(vector, w)); + color.xyz = hash_vec4_to_vec3(vec4(vector, w)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl index 35d2e903cf4..63e97e66c90 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl @@ -1,8 +1,41 @@ -void curves_vec(float fac, vec3 vec, sampler1DArray curvemap, float layer, out vec3 outvec) +/* ext is vec4(in_x, in_dy, out_x, out_dy). */ +float curve_extrapolate(float x, float y, vec4 ext) { - vec4 co = vec4(vec * 0.5 + 0.5, layer); + if (x < 0.0) { + return y + x * ext.y; + } + else if (x > 1.0) { + return y + (x - 1.0) * ext.w; + } + else { + return y; + } +} + +#define RANGE_RESCALE(x, min, range) ((x - min) * range) + +void curves_vec(float fac, + vec3 vec, + sampler1DArray curvemap, + float layer, + vec3 range, + vec4 ext_x, + vec4 ext_y, + vec4 ext_z, + out vec3 outvec) +{ + vec4 co = vec4(vec, layer); + + vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x); + co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range); + outvec.x = texture(curvemap, co.xw).x; outvec.y = texture(curvemap, co.yw).y; outvec.z = texture(curvemap, co.zw).z; + + outvec.x = curve_extrapolate(co.x, outvec.r, ext_x); + outvec.y = curve_extrapolate(co.y, outvec.g, ext_y); + outvec.z = curve_extrapolate(co.z, outvec.b, ext_z); + outvec = mix(vec, outvec, fac); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 93132b6044f..420f177e146 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -76,7 +76,7 @@ void vector_math_ceil(vec3 a, vec3 b, float scale, out vec3 outVector, out float void vector_math_modulo(vec3 a, vec3 b, float scale, out vec3 outVector, out float outValue) { - outVector = c_mod(a, b); + outVector = compatible_fmod(a, b); } void vector_math_fraction(vec3 a, vec3 b, float scale, out vec3 outVector, out float outValue) |