diff --git a/src/renderer/api.c b/src/renderer/api.c index d1d5175b..5d026bc8 100644 --- a/src/renderer/api.c +++ b/src/renderer/api.c @@ -178,17 +178,18 @@ void r_blend_unpack(BlendMode mode, UnpackedBlendMode *dest) { const UniformTypeInfo* r_uniform_type_info(UniformType type) { static UniformTypeInfo uniform_typemap[] = { - [UNIFORM_FLOAT] = { 1, sizeof(float) }, - [UNIFORM_VEC2] = { 2, sizeof(float) }, - [UNIFORM_VEC3] = { 3, sizeof(float) }, - [UNIFORM_VEC4] = { 4, sizeof(float) }, - [UNIFORM_INT] = { 1, sizeof(int) }, - [UNIFORM_IVEC2] = { 2, sizeof(int) }, - [UNIFORM_IVEC3] = { 3, sizeof(int) }, - [UNIFORM_IVEC4] = { 4, sizeof(int) }, - [UNIFORM_SAMPLER] = { 1, sizeof(void*) }, - [UNIFORM_MAT3] = { 9, sizeof(float) }, - [UNIFORM_MAT4] = { 16, sizeof(float) }, + [UNIFORM_FLOAT] = { 1, sizeof(float) }, + [UNIFORM_VEC2] = { 2, sizeof(float) }, + [UNIFORM_VEC3] = { 3, sizeof(float) }, + [UNIFORM_VEC4] = { 4, sizeof(float) }, + [UNIFORM_INT] = { 1, sizeof(int) }, + [UNIFORM_IVEC2] = { 2, sizeof(int) }, + [UNIFORM_IVEC3] = { 3, sizeof(int) }, + [UNIFORM_IVEC4] = { 4, sizeof(int) }, + [UNIFORM_SAMPLER_2D] = { 1, sizeof(void*) }, + [UNIFORM_SAMPLER_CUBE] = { 1, sizeof(void*) }, + [UNIFORM_MAT3] = { 9, sizeof(float) }, + [UNIFORM_MAT4] = { 16, sizeof(float) }, }; assert((uint)type < sizeof(uniform_typemap)/sizeof(UniformTypeInfo)); @@ -717,7 +718,20 @@ bool r_screenshot(Pixmap *out) { // uniforms garbage; hope your compiler is smart enough to inline most of this -#define ASSERT_UTYPE(uniform, type) do { if(uniform) assert(r_uniform_type(uniform) == type); } while(0) +// TODO: verify sampler-to-texture type consistency? + +#define ASSERT_UTYPE(uniform, type) do { \ + if(uniform) { \ + assert(r_uniform_type(uniform) == type); \ + } \ +} while(0) + +#define ASSERT_UTYPE_SAMPLER(uniform) do { \ + if(uniform) { \ + attr_unused UniformType _utype = r_uniform_type(uniform); \ + assert(UNIFORM_TYPE_IS_SAMPLER(_utype)); \ + } \ +} while(0) void r_uniform_ptr_unsafe(Uniform *uniform, uint offset, uint count, void *data) { if(uniform) B.uniform(uniform, offset, count, data); @@ -1001,7 +1015,7 @@ void _r_uniform_ivec4_array(const char *uniform, uint offset, uint count, ivec4_ } void _r_uniform_ptr_sampler_ptr(Uniform *uniform, Texture *tex) { - ASSERT_UTYPE(uniform, UNIFORM_SAMPLER); + ASSERT_UTYPE_SAMPLER(uniform); if(uniform) B.uniform(uniform, 0, 1, &tex); } @@ -1010,7 +1024,7 @@ void _r_uniform_sampler_ptr(const char *uniform, Texture *tex) { } void _r_uniform_ptr_sampler(Uniform *uniform, const char *tex) { - ASSERT_UTYPE(uniform, UNIFORM_SAMPLER); + ASSERT_UTYPE_SAMPLER(uniform); if(uniform) B.uniform(uniform, 0, 1, (Texture*[]) { res_texture(tex) }); } @@ -1019,7 +1033,7 @@ void _r_uniform_sampler(const char *uniform, const char *tex) { } void _r_uniform_ptr_sampler_array_ptr(Uniform *uniform, uint offset, uint count, Texture *values[count]) { - ASSERT_UTYPE(uniform, UNIFORM_SAMPLER); + ASSERT_UTYPE_SAMPLER(uniform); if(uniform && count) B.uniform(uniform, offset, count, values); } @@ -1028,7 +1042,7 @@ void _r_uniform_sampler_array_ptr(const char *uniform, uint offset, uint count, } void _r_uniform_ptr_sampler_array(Uniform *uniform, uint offset, uint count, const char *values[count]) { - ASSERT_UTYPE(uniform, UNIFORM_SAMPLER); + ASSERT_UTYPE_SAMPLER(uniform); if(uniform && count) { Texture *arr[count], **aptr = arr, **aend = aptr + count; const char **vptr = values; diff --git a/src/renderer/api.h b/src/renderer/api.h index 06da896d..81f539ed 100644 --- a/src/renderer/api.h +++ b/src/renderer/api.h @@ -280,12 +280,16 @@ typedef enum UniformType { UNIFORM_IVEC2, UNIFORM_IVEC3, UNIFORM_IVEC4, - UNIFORM_SAMPLER, + UNIFORM_SAMPLER_2D, + UNIFORM_SAMPLER_CUBE, UNIFORM_MAT3, UNIFORM_MAT4, UNIFORM_UNKNOWN, } UniformType; +#define UNIFORM_TYPE_IS_SAMPLER(ut) \ + ((ut) == UNIFORM_SAMPLER_2D || (ut) == UNIFORM_SAMPLER_CUBE) + typedef struct UniformTypeInfo { // Refers to vector elements, not array elements. uint8_t elements; diff --git a/src/renderer/gl33/gl33.c b/src/renderer/gl33/gl33.c index e4399358..168a9bae 100644 --- a/src/renderer/gl33/gl33.c +++ b/src/renderer/gl33/gl33.c @@ -29,6 +29,7 @@ #include "util/env.h" // #define GL33_DEBUG_TEXUNITS +// #define GL33_DRAW_STATS typedef struct TextureUnit { LIST_INTERFACE(struct TextureUnit); @@ -37,6 +38,7 @@ typedef struct TextureUnit { Texture *pending; GLuint gl_handle; GLenum bind_target; + int old_prio; bool locked; } TextureUnit; @@ -117,6 +119,7 @@ static struct { hrtime_t last_draw; hrtime_t draw_time; uint draw_calls; + uint texture_rebinds; } stats; #endif } R; @@ -184,13 +187,14 @@ static inline void gl33_stats_post_draw(void) { static inline void gl33_stats_post_frame(void) { #ifdef GL33_DRAW_STATS - log_debug("%.20gs spent in %u draw calls", (double)R.stats.draw_time, R.stats.draw_calls); + log_debug("%.1fµs spent in %u draw calls", R.stats.draw_time / (HRTIME_RESOLUTION / 1000000.0) , R.stats.draw_calls); + log_debug("%u texture rebinds", R.stats.texture_rebinds); memset(&R.stats, 0, sizeof(R.stats)); #endif } static void gl33_init_texunits(void) { - GLint texunits_available, texunits_capped, texunits_max, texunits_min = 4; + GLint texunits_available, texunits_capped, texunits_max, texunits_min = 8; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &texunits_available); if(texunits_available < texunits_min) { @@ -538,7 +542,6 @@ static void gl33_activate_texunit(TextureUnit *unit) { static int gl33_texunit_priority(TextureUnit *u) { if(u->locked) { - assert(u->pending); return 3; } @@ -566,20 +569,24 @@ attr_unused static void texture_str(Texture *tex, char *buf, size_t bufsize) { } } +attr_unused static void gl33_dump_texunit(TextureUnit *u) { + char buf1[128], buf2[128]; + texture_str(u->active, buf1, sizeof(buf1)); + texture_str(u->pending, buf2, sizeof(buf2)); + log_debug("[Unit %u | prio=%i | target=0x%x] bound: %s; pending: %s", + (uint)TU_INDEX(u), + gl33_texunit_priority(u), + u->bind_target, + buf1, + buf2 + ); +} + attr_unused static void gl33_dump_texunits(void) { log_debug("=== BEGIN DUMP ==="); for(TextureUnit *u = R.texunits.list.first; u; u = u->next) { - char buf1[128], buf2[128]; - texture_str(u->active, buf1, sizeof(buf1)); - texture_str(u->pending, buf2, sizeof(buf2)); - log_debug("[Unit %u | %i | 0x%x] bound: %s; pending: %s", - (uint)TU_INDEX(u), - gl33_texunit_priority(u), - u->bind_target, - buf1, - buf2 - ); + gl33_dump_texunit(u); } log_debug("=== END DUMP ==="); @@ -588,6 +595,12 @@ attr_unused static void gl33_dump_texunits(void) { static void gl33_relocate_texuint(TextureUnit *unit) { int prio = gl33_texunit_priority(unit); + if(unit->old_prio == prio) { + return; + } + + // TODO: optimize with a heap-based priority queue? + alist_unlink(&R.texunits.list, unit); if(prio > 1) { @@ -596,6 +609,8 @@ static void gl33_relocate_texuint(TextureUnit *unit) { alist_insert_at_priority_head(&R.texunits.list, unit, prio, gl33_texunit_priority_callback); } + unit->old_prio = prio; + #ifdef GL33_DEBUG_TEXUNITS // gl33_dump_texunits(); // log_debug("Relocated unit %u", (uint)TU_INDEX(unit)); @@ -615,7 +630,7 @@ static void gl33_set_texunit_binding(TextureUnit *unit, Texture *tex, bool lock) unit->locked = true; } - // gl33_relocate_texuint(unit); + gl33_relocate_texuint(unit); return; } @@ -651,14 +666,30 @@ static void gl33_bind_handle_to_texunit(TextureUnit *u, GLenum target, GLuint ha * Therefore we maintain only 1 kind of texture per unit here. */ +#ifdef GL33_DEBUG_TEXUNITS + if(target != u->bind_target) { + log_debug("[%u] TYPE CHANGE: 0x%x -> 0x%x", (uint)TU_INDEX(u), u->bind_target, target); + gl33_dump_texunit(u); + } +#endif + if(target != u->bind_target && u->bind_target != 0) { // If changing target, clear the old binding. // Probably not strictly necessary, but safer and more debuggable. glBindTexture(u->bind_target, 0); - u->bind_target = target; + +#ifdef GL33_DRAW_STATS + ++R.stats.texture_rebinds; +#endif } glBindTexture(target, handle); + +#ifdef GL33_DRAW_STATS + ++R.stats.texture_rebinds; +#endif + + u->bind_target = target; u->gl_handle = handle; } @@ -670,7 +701,7 @@ void gl33_sync_texunit(TextureUnit *unit, bool prepare_rendering, bool ensure_ac attr_unused char buf1[128], buf2[128]; texture_str(unit->active, buf1, sizeof(buf1)); texture_str(unit->pending, buf2, sizeof(buf2)); - log_debug("[Unit %u] %s ===> %s", (uint)TU_INDEX(unit), buf1, buf2); + log_debug("[Unit %u] %s ===> %s (render: %i)", (uint)TU_INDEX(unit), buf1, buf2, prepare_rendering); } #endif @@ -695,9 +726,13 @@ void gl33_sync_texunit(TextureUnit *unit, bool prepare_rendering, bool ensure_ac gl33_activate_texunit(unit); } - if(prepare_rendering && unit->active != NULL) { - gl33_texture_prepare(unit->active); + if(prepare_rendering) { + if(unit->active != NULL) { + gl33_texture_prepare(unit->active); + } + unit->locked = false; + gl33_relocate_texuint(unit); } } @@ -837,7 +872,49 @@ void gl33_sync_blend_mode(void) { } } +static TextureUnit *gl33_get_free_texunit(void) { + TextureUnit *u = R.texunits.list.first; + + if( + u->pending && + u->pending != u->active + ) { + log_warn("Ran out of texturing units, expect rendering errors!"); + } + + assert(!u->locked); + return u; +} + uint gl33_bind_texture(Texture *texture, bool for_rendering, int preferred_unit) { + if(UNLIKELY(texture == NULL)) { + TextureUnit *unit = NULL; + + if(preferred_unit >= 0) { + assert(preferred_unit < R.texunits.limit); + unit = &R.texunits.array[preferred_unit]; + + if(unit->pending != NULL && unit->locked) { + // someone else already took this unit for rendering + unit = NULL; + } + } + + if(unit == NULL) { + assert(!glext.issues.avoid_sampler_uniform_updates); + unit = gl33_get_free_texunit(); + } + + if(unit->locked) { + gl33_relocate_texuint(unit); + } else { + gl33_set_texunit_binding(unit, NULL, for_rendering); + } + + assert(unit->pending == NULL); + return TU_INDEX(unit); + } + if(glext.issues.avoid_sampler_uniform_updates && preferred_unit >= 0) { assert(preferred_unit < R.texunits.limit); TextureUnit *u = &R.texunits.array[preferred_unit]; @@ -853,16 +930,7 @@ uint gl33_bind_texture(Texture *texture, bool for_rendering, int preferred_unit) // most recent binding. texture->binding_unit = u; } else if(!texture->binding_unit) { - // assert(R.texunits.list.first->tex2d.pending != texture); - - if( - R.texunits.list.first->pending && - R.texunits.list.first->pending != R.texunits.list.first->active - ) { - log_warn("Ran out of texturing units, expect rendering errors!"); - } - - gl33_set_texunit_binding(R.texunits.list.first, texture, for_rendering); + gl33_set_texunit_binding(gl33_get_free_texunit(), texture, for_rendering); } else /* if(for_rendering) */ { texture->binding_unit->locked |= for_rendering; gl33_relocate_texuint(texture->binding_unit); diff --git a/src/renderer/gl33/shader_program.c b/src/renderer/gl33/shader_program.c index 8605e0b2..40ee6951 100644 --- a/src/renderer/gl33/shader_program.c +++ b/src/renderer/gl33/shader_program.c @@ -11,6 +11,7 @@ #include "gl33.h" #include "shader_program.h" #include "shader_object.h" +#include "texture.h" #include "../glcommon/debug.h" #include "../api.h" @@ -136,18 +137,19 @@ static void uget_mat4(Uniform *uniform, uint count, void *data) { static struct { void (*setter)(Uniform *uniform, uint offset, uint count, const void *data); void (*getter)(Uniform *uniform, uint count, void *data); -} type_to_accessors[] = { - [UNIFORM_FLOAT] = { uset_float, uget_float }, - [UNIFORM_VEC2] = { uset_vec2, uget_vec2 }, - [UNIFORM_VEC3] = { uset_vec3, uget_vec3 }, - [UNIFORM_VEC4] = { uset_vec4, uget_vec4 }, - [UNIFORM_INT] = { uset_int, uget_int }, - [UNIFORM_IVEC2] = { uset_ivec2, uget_ivec2 }, - [UNIFORM_IVEC3] = { uset_ivec3, uget_ivec3 }, - [UNIFORM_IVEC4] = { uset_ivec4, uget_ivec4 }, - [UNIFORM_SAMPLER] = { uset_int, uget_int }, - [UNIFORM_MAT3] = { uset_mat3, uget_mat3 }, - [UNIFORM_MAT4] = { uset_mat4, uget_mat4 }, +} type_to_accessors[] = { + [UNIFORM_FLOAT] = { uset_float, uget_float }, + [UNIFORM_VEC2] = { uset_vec2, uget_vec2 }, + [UNIFORM_VEC3] = { uset_vec3, uget_vec3 }, + [UNIFORM_VEC4] = { uset_vec4, uget_vec4 }, + [UNIFORM_INT] = { uset_int, uget_int }, + [UNIFORM_IVEC2] = { uset_ivec2, uget_ivec2 }, + [UNIFORM_IVEC3] = { uset_ivec3, uget_ivec3 }, + [UNIFORM_IVEC4] = { uset_ivec4, uget_ivec4 }, + [UNIFORM_SAMPLER_2D] = { uset_int, uget_int }, + [UNIFORM_SAMPLER_CUBE] = { uset_int, uget_int }, + [UNIFORM_MAT3] = { uset_mat3, uget_mat3 }, + [UNIFORM_MAT4] = { uset_mat4, uget_mat4 }, }; typedef struct MagicUniformSpec { @@ -219,28 +221,27 @@ static void *gl33_sync_uniform(const char *key, void *value, void *arg) { Uniform *uniform = value; // special case: for sampler uniforms, we have to construct the actual data from the texture pointers array. - if(uniform->type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(uniform->type)) { Uniform *size_uniform = uniform->size_uniform; for(uint i = 0; i < uniform->array_size; ++i) { Texture *tex = uniform->textures[i]; + GLuint preferred_unit = CASTPTR_ASSUME_ALIGNED(uniform->cache.pending, int)[i]; + GLuint unit = gl33_bind_texture(tex, true, preferred_unit); - if(tex == NULL) { - continue; - } - - if(glext.issues.avoid_sampler_uniform_updates) { - int preferred_unit = CASTPTR_ASSUME_ALIGNED(uniform->cache.pending, int)[i]; - attr_unused GLuint unit = gl33_bind_texture(tex, true, preferred_unit); - assert(unit == preferred_unit); - } else { - GLuint unit = gl33_bind_texture(tex, true, -1); + if(unit != preferred_unit) { gl33_update_uniform(uniform, i, 1, &unit); } if(size_uniform) { uint w, h; - r_texture_get_size(tex, 0, &w, &h); + + if(tex) { + r_texture_get_size(tex, 0, &w, &h); + } else { + w = h = 0; + } + vec2_noalign size = { w, h }; gl33_update_uniform(size_uniform, i, 1, &size); gl33_commit_uniform(size_uniform); @@ -273,8 +274,15 @@ void gl33_uniform(Uniform *uniform, uint offset, uint count, const void *data) { } // special case: for sampler uniforms, data is an array of Texture pointers that we'll have to bind later. - if(uniform->type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(uniform->type)) { Texture **textures = (Texture**)data; + + for(uint i = 0; i < count; ++i) { + if(textures[i]) { + assert(gl33_texture_sampler_compatible(textures[i], uniform->type)); + } + } + memcpy(uniform->textures + offset, textures, sizeof(Texture*) * count); } else { gl33_update_uniform(uniform, offset, count, data); @@ -311,18 +319,18 @@ static bool cache_uniforms(ShaderProgram *prog) { } switch(type) { - case GL_FLOAT: uni.type = UNIFORM_FLOAT; break; - case GL_FLOAT_VEC2: uni.type = UNIFORM_VEC2; break; - case GL_FLOAT_VEC3: uni.type = UNIFORM_VEC3; break; - case GL_FLOAT_VEC4: uni.type = UNIFORM_VEC4; break; - case GL_INT: uni.type = UNIFORM_INT; break; - case GL_INT_VEC2: uni.type = UNIFORM_IVEC2; break; - case GL_INT_VEC3: uni.type = UNIFORM_IVEC3; break; - case GL_INT_VEC4: uni.type = UNIFORM_IVEC4; break; - case GL_SAMPLER_2D: uni.type = UNIFORM_SAMPLER; break; - case GL_SAMPLER_CUBE: uni.type = UNIFORM_SAMPLER; break; - case GL_FLOAT_MAT3: uni.type = UNIFORM_MAT3; break; - case GL_FLOAT_MAT4: uni.type = UNIFORM_MAT4; break; + case GL_FLOAT: uni.type = UNIFORM_FLOAT; break; + case GL_FLOAT_VEC2: uni.type = UNIFORM_VEC2; break; + case GL_FLOAT_VEC3: uni.type = UNIFORM_VEC3; break; + case GL_FLOAT_VEC4: uni.type = UNIFORM_VEC4; break; + case GL_INT: uni.type = UNIFORM_INT; break; + case GL_INT_VEC2: uni.type = UNIFORM_IVEC2; break; + case GL_INT_VEC3: uni.type = UNIFORM_IVEC3; break; + case GL_INT_VEC4: uni.type = UNIFORM_IVEC4; break; + case GL_SAMPLER_2D: uni.type = UNIFORM_SAMPLER_2D; break; + case GL_SAMPLER_CUBE: uni.type = UNIFORM_SAMPLER_CUBE; break; + case GL_FLOAT_MAT3: uni.type = UNIFORM_MAT3; break; + case GL_FLOAT_MAT4: uni.type = UNIFORM_MAT4; break; default: log_warn("Uniform '%s' is of an unsupported type 0x%04x and will be ignored.", name, type); @@ -352,9 +360,9 @@ static bool cache_uniforms(ShaderProgram *prog) { uni.location = loc; uni.array_size = size; - if(uni.type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(uni.type)) { uni.elem_size = sizeof(GLint); - uni.textures = calloc(uni.array_size, sizeof(Texture*)); + uni.textures = calloc(uni.array_size, sizeof(*uni.textures)); } else { uni.elem_size = typeinfo->element_size * typeinfo->elements; } @@ -380,7 +388,7 @@ static bool cache_uniforms(ShaderProgram *prog) { Uniform *new_uni = memdup(&uni, sizeof(uni)); - if(uni.type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(uni.type)) { list_push(&sampler_uniforms, new_uni); if(glext.issues.avoid_sampler_uniform_updates) { @@ -413,7 +421,7 @@ static bool cache_uniforms(ShaderProgram *prog) { while(iter.has_data) { Uniform *u = iter.value; - if(u->type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(u->type)) { const char *sampler_name = iter.key; const char size_suffix[] = "_SIZE"; char size_uniform_name[strlen(sampler_name) + sizeof(size_suffix)]; @@ -446,7 +454,7 @@ static bool cache_uniforms(ShaderProgram *prog) { void gl33_unref_texture_from_samplers(Texture *tex) { for(Uniform *u = sampler_uniforms; u; u = u->next) { - assert(u->type == UNIFORM_SAMPLER); + assert(UNIFORM_TYPE_IS_SAMPLER(u->type)); assert(u->textures != NULL); for(Texture **slot = u->textures; slot < u->textures + u->array_size; ++slot) { @@ -476,7 +484,7 @@ static void print_info_log(GLuint prog) { static void *free_uniform(const char *key, void *data, void *arg) { Uniform *uniform = data; - if(uniform->type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(uniform->type)) { list_unlink(&sampler_uniforms, uniform); } diff --git a/src/renderer/gl33/texture.c b/src/renderer/gl33/texture.c index 315e3b90..99009c66 100644 --- a/src/renderer/gl33/texture.c +++ b/src/renderer/gl33/texture.c @@ -576,3 +576,18 @@ const char *gl33_texture_get_debug_label(Texture *tex) { void gl33_texture_set_debug_label(Texture *tex, const char *label) { glcommon_set_debug_label(tex->debug_label, "Texture", GL_TEXTURE, tex->gl_handle, label); } + +bool gl33_texture_sampler_compatible(Texture *tex, UniformType sampler_type) { + assert(UNIFORM_TYPE_IS_SAMPLER(sampler_type)); + + switch(tex->bind_target) { + case GL_TEXTURE_2D: + return sampler_type == UNIFORM_SAMPLER_2D; + + case GL_TEXTURE_CUBE_MAP: + return sampler_type == UNIFORM_SAMPLER_CUBE; + + default: + UNREACHABLE; + } +} diff --git a/src/renderer/gl33/texture.h b/src/renderer/gl33/texture.h index 00c5be26..5e856054 100644 --- a/src/renderer/gl33/texture.h +++ b/src/renderer/gl33/texture.h @@ -44,5 +44,6 @@ void gl44_texture_clear(Texture *tex, const Color *clr); void gl33_texture_clear(Texture *tex, const Color *clr); void gl33_texture_destroy(Texture *tex); bool gl33_texture_type_query(TextureType type, TextureFlags flags, PixmapFormat pxfmt, PixmapOrigin pxorigin, TextureTypeQueryResult *result); +bool gl33_texture_sampler_compatible(Texture *tex, UniformType sampler_type) attr_nonnull(1); #endif // IGUARD_renderer_gl33_texture_h diff --git a/src/resource/postprocess.c b/src/resource/postprocess.c index 0d337b1b..e6b0fb03 100644 --- a/src/resource/postprocess.c +++ b/src/resource/postprocess.c @@ -67,7 +67,7 @@ static bool postprocess_load_callback(const char *key, const char *value, void * UniformType type = r_uniform_type(uni); const UniformTypeInfo *type_info = r_uniform_type_info(type); - if(type == UNIFORM_SAMPLER) { + if(UNIFORM_TYPE_IS_SAMPLER(type)) { Texture *tex = get_resource_data(RES_TEXTURE, value, ldata->resflags); if(tex) {