+ set_texture(tag, tex, -1, samp);
+}
+
+void Renderer::set_texture(Tag tag, const Texture *tex, int level, const Sampler *samp)
+{
+ State &state = get_state();
+
+ if(tex)
+ {
+ if(ResourceManager *res_mgr = tex->get_manager())
+ res_mgr->resource_used(*tex);
+ if(!tex->is_loaded())
+ tex = &placeholder_texture;
+ if(!samp)
+ samp = &default_sampler;
+ }
+ else
+ samp = 0;
+
+ set_resource(texture_stack, state.texture_count, tag, { tex, samp, level });
+}
+
+void Renderer::set_storage_texture(Tag tag, const Texture *tex)
+{
+ State &state = get_state();
+ set_resource(texture_stack, state.texture_count, tag, { tex, 0, 0 });
+}
+
+template<typename T>
+void Renderer::set_resource(vector<BoundResource<T>> &stack, unsigned &count, Tag tag, const T &res)
+{
+ if(stack.size()>count)
+ {
+ BoundResource<T> &top = stack[count];
+ if(top.tag==tag && top.resource==res)
+ {
+ ++count;
+ return;
+ }
+ else
+ flush_resources(stack, count);
+ }
+
+ for(auto i=stack.end(); i!=stack.begin(); )
+ if((--i)->tag==tag)
+ {
+ i->replaced = stack.size();
+ break;
+ }
+
+ stack.emplace_back();
+ BoundResource<T> &bound_res = stack.back();
+ bound_res.tag = tag;
+ bound_res.resource = res;
+ count = stack.size();
+}
+
+void Renderer::flush_shader_data()
+{
+ const State &state = get_state();
+
+ if(shdata_stack.size()>state.shdata_count)
+ shdata_stack.erase(shdata_stack.begin()+state.shdata_count, shdata_stack.end());
+}
+
+template<typename T>
+void Renderer::flush_resources(vector<BoundResource<T>> &stack, unsigned &count)
+{
+ for(unsigned i=0; i<count; ++i)
+ if(stack[i].replaced>=static_cast<int>(count))
+ stack[i].replaced = -1;
+
+ stack.erase(stack.begin()+count, stack.end());