void Batch::draw() const
{
- BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
const void *data_ptr = setup_draw();
-
glDrawElements(prim_type, size(), gl_index_type, data_ptr);
}
{
static Require req(ARB_draw_instanced);
- BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
const void *data_ptr = setup_draw();
-
glDrawElementsInstanced(prim_type, size(), gl_index_type, data_ptr, count);
}
namespace Msp {
namespace GL {
-const Buffer *Buffer::bound[5] = { 0, 0, 0, 0, 0 };
-BufferType buffer_types[] = { ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, PIXEL_PACK_BUFFER, PIXEL_UNPACK_BUFFER, UNIFORM_BUFFER };
-
Buffer::Buffer():
size(0),
allocated(false)
Buffer::~Buffer()
{
- for(unsigned i=0; i<5; ++i)
- if(bound[i]==this)
- unbind_from(buffer_types[i]);
glDeleteBuffers(1, &id);
}
-void Buffer::require_buffer_type(BufferType type)
-{
- static Require _req_vbo(ARB_vertex_buffer_object);
- if(type==PIXEL_PACK_BUFFER || type==PIXEL_UNPACK_BUFFER)
- static Require _req_pbo(ARB_pixel_buffer_object);
- else if(type==UNIFORM_BUFFER)
- static Require _req_ubo(ARB_uniform_buffer_object);
-}
-
void Buffer::storage(unsigned sz)
{
if(size>0)
glNamedBufferStorage(id, size, 0, flags);
else
{
- BindRestore _bind(this, ARRAY_BUFFER);
- glBufferStorage(ARRAY_BUFFER, size, 0, flags);
+ glBindBuffer(GL_ARRAY_BUFFER, id);
+ glBufferStorage(GL_ARRAY_BUFFER, size, 0, flags);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
}
allocated = true;
glNamedBufferData(id, size, d, STATIC_DRAW);
else
{
- BindRestore _bind(this, ARRAY_BUFFER);
- glBufferData(ARRAY_BUFFER, size, d, STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, id);
+ glBufferData(GL_ARRAY_BUFFER, size, d, STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
}
allocated = true;
glNamedBufferSubData(id, off, sz, d);
else
{
- BindRestore _bind(this, ARRAY_BUFFER);
- glBufferSubData(ARRAY_BUFFER, off, sz, d);
+ glBindBuffer(GL_ARRAY_BUFFER, id);
+ glBufferSubData(GL_ARRAY_BUFFER, off, sz, d);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
}
-BufferRange *Buffer::create_range(unsigned s, unsigned o)
-{
- return new BufferRange(*this, s, o);
-}
-
void *Buffer::map()
{
static Require _req(ARB_map_buffer_range);
return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
else
{
- BindRestore _bind(this, ARRAY_BUFFER);
- return glMapBufferRange(ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
+ glBindBuffer(GL_ARRAY_BUFFER, id);
+ void *result = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ return result;
}
}
return glUnmapNamedBuffer(id);
else if(OES_mapbuffer)
{
- BindRestore _bind(this, ARRAY_BUFFER);
- return glUnmapBuffer(ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, id);
+ bool result = glUnmapBuffer(GL_ARRAY_BUFFER);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ return result;
}
else
return true;
}
-void Buffer::bind_to(BufferType t) const
-{
- require_buffer_type(t);
- if(t==ELEMENT_ARRAY_BUFFER)
- if(const VertexSetup *vs = VertexSetup::current())
- {
- // Don't change the binding in a vertex array object
- if(this==vs->get_index_buffer())
- return;
- throw invalid_operation("Buffer::bind_to(ELEMENT_ARRAY_BUFFER)");
- }
- if(set_current(t, this))
- glBindBuffer(t, id);
-}
-
-const Buffer *Buffer::current(BufferType t)
-{
- if(t==ELEMENT_ARRAY_BUFFER)
- if(const VertexSetup *vs = VertexSetup::current())
- return vs->get_index_buffer();
- return binding(t);
-}
-
-void Buffer::unbind_from(BufferType type)
-{
- if(type==ELEMENT_ARRAY_BUFFER && VertexSetup::current())
- throw invalid_operation("Buffer::unbind_from(ELEMENT_ARRAY_BUFFER)");
- if(set_current(type, 0))
- glBindBuffer(type, 0);
-}
-
-const Buffer *&Buffer::binding(BufferType type)
-{
- switch(type)
- {
- case ARRAY_BUFFER: return bound[0];
- case ELEMENT_ARRAY_BUFFER: return bound[1];
- case PIXEL_PACK_BUFFER: return bound[2];
- case PIXEL_UNPACK_BUFFER: return bound[3];
- case UNIFORM_BUFFER: return bound[4];
- default: throw invalid_argument("Buffer::binding");
- }
-}
-
-bool Buffer::set_current(BufferType type, const Buffer *buf)
-{
- const Buffer *&ptr = binding(type);
- if(ptr==buf)
- return false;
-
- ptr = buf;
- return true;
-}
-
void Buffer::set_debug_name(const string &name)
{
#ifdef DEBUG
#endif
}
-
-vector<const BufferRange *> BufferRange::bound_uniform;
-
-BufferRange::BufferRange(Buffer &b, unsigned o, unsigned s):
- buffer(b),
- offset(o),
- size(s)
-{
- if(o>buffer.get_size() || o+s>buffer.get_size())
- throw out_of_range("BufferRange::BufferRange");
-}
-
-BufferRange::~BufferRange()
-{
- for(unsigned i=0; i<bound_uniform.size(); ++i)
- if(bound_uniform[i]==this)
- unbind_from(UNIFORM_BUFFER, i);
-}
-
-void BufferRange::data(const void *d)
-{
- buffer.sub_data(offset, size, d);
-}
-
-void BufferRange::bind_to(BufferType t, unsigned i)
-{
- Buffer::require_buffer_type(t);
- if(set_current(t, i, this))
- {
- // The buffer gets bound as a side effect
- Buffer::set_current(t, &buffer);
- glBindBufferRange(t, i, buffer.id, offset, size);
- }
-}
-
-void BufferRange::unbind_from(BufferType t, unsigned i)
-{
- if(set_current(t, i, 0))
- {
- Buffer::set_current(t, 0);
- glBindBufferBase(t, i, 0);
- }
-}
-
-const BufferRange *&BufferRange::binding(BufferType type, unsigned index)
-{
- if(type==UNIFORM_BUFFER)
- {
- if(index>=Limits::get_global().max_uniform_bindings)
- throw out_of_range("BufferRange::binding");
- if(bound_uniform.size()<=index)
- bound_uniform.resize(index+1);
- return bound_uniform[index];
- }
- else
- throw invalid_argument("BufferRange::binding");
-}
-
-bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf)
-{
- const BufferRange *&ptr = binding(type, index);
- if(ptr==buf)
- return false;
-
- ptr = buf;
- return true;
-}
-
-unsigned BufferRange::get_n_uniform_buffer_bindings()
-{
- return Limits::get_global().max_uniform_bindings;
-}
-
-unsigned BufferRange::get_uniform_buffer_alignment()
-{
- return Limits::get_global().uniform_buffer_alignment;
-}
-
} // namespace GL
} // namespace Msp
virtual ~buffer_too_small() throw() { }
};
-enum BufferType
-{
- ARRAY_BUFFER = GL_ARRAY_BUFFER,
- ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER,
- PIXEL_PACK_BUFFER = GL_PIXEL_PACK_BUFFER,
- PIXEL_UNPACK_BUFFER = GL_PIXEL_UNPACK_BUFFER,
- UNIFORM_BUFFER = GL_UNIFORM_BUFFER
-};
-
enum BufferUsage
{
STREAM_DRAW = GL_STREAM_DRAW,
unsigned size;
bool allocated;
- static const Buffer *bound[5];
-
public:
Buffer();
~Buffer();
-private:
- static void require_buffer_type(BufferType);
-
-public:
/** Returns the OpenGL ID of the buffer. For internal use only. */
unsigned get_id() const { return id; }
void require_size(unsigned) const;
- BufferRange *create_range(unsigned, unsigned);
-
void *map();
DEPRECATED void *map(BufferAccess) { return map(); }
bool unmap();
- /** Binds the buffer. */
- void bind_to(BufferType) const;
-
- static const Buffer *current(BufferType);
- static void unbind_from(BufferType);
-private:
- static const Buffer *&binding(BufferType);
- static bool set_current(BufferType, const Buffer *);
-
-public:
void set_debug_name(const std::string &);
};
-
-/**
-A proxy for a subset of a buffer. Can be bound for use with uniform blocks.
-*/
-class BufferRange
-{
-private:
- Buffer &buffer;
- unsigned offset;
- unsigned size;
-
- static std::vector<const BufferRange *> bound_uniform;
-
-public:
- BufferRange(Buffer &, unsigned, unsigned);
- ~BufferRange();
-
- void data(const void *);
-
- void bind_to(BufferType, unsigned);
-
- static const BufferRange *current(BufferType t, unsigned i) { return binding(t, i); }
- static void unbind_from(BufferType, unsigned);
-private:
- static const BufferRange *&binding(BufferType, unsigned);
- static bool set_current(BufferType, unsigned, const BufferRange *);
-
-public:
- DEPRECATED static unsigned get_n_uniform_buffer_bindings();
- DEPRECATED static unsigned get_uniform_buffer_alignment();
-};
-
} // namespace GL
} // namespace Msp
/** Returns the buffer in which the data is stored. */
const Buffer *get_buffer() const { return buffer; }
-protected:
- /** Returns the amount of data to be stored in the buffer, in bytes. */
+ /** Returns the size of the data, in bytes. */
virtual unsigned get_data_size() const = 0;
+protected:
/** Returns a pointer to the start of data in client memory. */
virtual const void *get_data_pointer() const = 0;
changes. */
void update_offset();
- /** Returns the offset where the data should be uploaded. */
+public:
+ /** Returns the offset of the data from the beginning of the buffer. */
unsigned get_offset() const { return offset; }
+private:
/** Called when the target buffer or offset within it has changed. */
virtual void location_changed(Buffer *, unsigned, unsigned) const { }
-private:
/** Uploads data to the buffer. Receives pointer to mapped buffer memory as
parameter, or null to use the buffer upload interface. */
void upload_data(char *) const;
void attach(const ClipPlane &);
void detach(const ClipPlane &);
+ unsigned get_n_planes() const { return planes.size(); }
+
DEPRECATED void attach(unsigned, const ClipPlane &p) { attach(p); }
DEPRECATED void detach(unsigned);
--- /dev/null
+#include <stdexcept>
+#include <msp/core/algorithm.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_sampler_objects.h>
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include "buffer.h"
+#include "deviceinfo.h"
+#include "pipelinestate.h"
+#include "program.h"
+#include "texture.h"
+#include "uniformblock.h"
+#include "vertexsetup.h"
+#include "windingtest.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+const PipelineState *PipelineState::last_applied = 0;
+vector<int> PipelineState::bound_tex_targets;
+
+PipelineState::PipelineState():
+ shprog(0),
+ vertex_setup(0),
+ winding_test(0),
+ enabled_clip_planes(0),
+ changes(0)
+{
+ if(!ARB_direct_state_access && bound_tex_targets.empty())
+ bound_tex_targets.resize(Limits::get_global().max_texture_bindings);
+}
+
+PipelineState::~PipelineState()
+{
+ if(this==last_applied)
+ last_applied = 0;
+}
+
+void PipelineState::set_shader_program(const Program *p)
+{
+ if(p!=shprog)
+ {
+ shprog = p;
+ changes |= SHPROG;
+ }
+}
+
+void PipelineState::set_vertex_setup(const VertexSetup *s)
+{
+ if(s!=vertex_setup)
+ {
+ vertex_setup = s;
+ changes |= VERTEX_SETUP;
+ }
+}
+
+void PipelineState::set_winding_test(const WindingTest *w)
+{
+ if(w!=winding_test)
+ {
+ winding_test = w;
+ changes |= WINDING_TEST;
+ }
+}
+
+void PipelineState::set_enabled_clip_planes(unsigned p)
+{
+ if(p!=enabled_clip_planes)
+ {
+ enabled_clip_planes = p;
+ changes |= CLIP_PLANES;
+ }
+}
+
+void PipelineState::set_texture(unsigned binding, const Texture *tex, const Sampler *samp)
+{
+ if((tex!=0)!=(samp!=0))
+ throw invalid_argument("PipelineState::set_texture");
+
+ vector<BoundTexture>::iterator i = lower_bound_member(textures, binding, &BoundTexture::binding);
+ if(i==textures.end() || i->binding!=binding)
+ i = textures.insert(i, BoundTexture(binding));
+ if(tex!=i->texture || samp!=i->sampler)
+ {
+ i->texture = tex;
+ i->sampler = samp;
+ i->changed = true;
+ changes |= TEXTURES;
+ }
+}
+
+void PipelineState::set_uniforms(const DefaultUniformBlock *block)
+{
+ set_uniform_block_(-1, block);
+}
+
+void PipelineState::set_uniform_block(unsigned binding, const BufferBackedUniformBlock *block)
+{
+ set_uniform_block_(binding, block);
+}
+
+void PipelineState::set_uniform_block_(int binding, const UniformBlock *block)
+{
+ vector<BoundUniformBlock>::iterator i = lower_bound_member(uniform_blocks, binding, &BoundUniformBlock::binding);
+ if(i==uniform_blocks.end() || i->binding!=binding)
+ i = uniform_blocks.insert(i, BoundUniformBlock(binding));
+ if(block!=i->block || binding<0)
+ {
+ i->block = block;
+ i->changed = true;
+ changes |= UNIFORMS;
+ }
+}
+
+void PipelineState::apply() const
+{
+ apply(this==last_applied ? changes : ~0U);
+}
+
+void PipelineState::apply(unsigned mask) const
+{
+ if(mask&SHPROG)
+ glUseProgram(shprog ? shprog->get_id() : 0);
+
+ if(mask&VERTEX_SETUP)
+ {
+ glBindVertexArray(vertex_setup ? vertex_setup->get_id() : 0);
+ if(vertex_setup)
+ vertex_setup->refresh();
+ }
+
+ if(mask&WINDING_TEST)
+ {
+ if(winding_test)
+ {
+ glEnable(GL_CULL_FACE);
+ glFrontFace(winding_test->get_winding());
+ }
+ else
+ glDisable(GL_CULL_FACE);
+ }
+
+ if(mask&CLIP_PLANES)
+ {
+ unsigned max_clip_planes = Limits::get_global().max_clip_planes;
+ for(unsigned i=0; i<max_clip_planes; ++i)
+ {
+ if((enabled_clip_planes>>i)&1)
+ glEnable(GL_CLIP_PLANE0+i);
+ else
+ glDisable(GL_CLIP_PLANE0+i);
+ }
+ }
+
+ if(mask&TEXTURES)
+ {
+ if(last_applied && this!=last_applied)
+ {
+ vector<BoundTexture>::const_iterator i = textures.begin();
+ vector<BoundTexture>::const_iterator j = last_applied->textures.begin();
+ while(j!=last_applied->textures.end())
+ {
+ if(i==textures.end() || j->binding<i->binding)
+ {
+ if(bound_tex_targets[j->binding])
+ {
+ if(ARB_direct_state_access)
+ glBindTextureUnit(j->binding, 0);
+ else
+ {
+ glActiveTexture(GL_TEXTURE0+j->binding);
+ glBindTexture(bound_tex_targets[j->binding], 0);
+ }
+ }
+ ++j;
+ }
+ else
+ {
+ if(i->binding==j->binding)
+ ++j;
+ ++i;
+ }
+ }
+ }
+
+ for(vector<BoundTexture>::const_iterator i=textures.begin(); i!=textures.end(); ++i)
+ if(i->changed)
+ {
+ if(i->texture && i->sampler)
+ {
+ if(ARB_direct_state_access)
+ glBindTextureUnit(i->binding, i->texture->get_id());
+ else
+ {
+ glActiveTexture(GL_TEXTURE0+i->binding);
+ if(bound_tex_targets[i->binding] && static_cast<int>(i->texture->get_target())!=bound_tex_targets[i->binding])
+ glBindTexture(bound_tex_targets[i->binding], 0);
+ glBindTexture(i->texture->get_target(), i->texture->get_id());
+ bound_tex_targets[i->binding] = i->texture->get_target();
+ }
+
+ glBindSampler(i->binding, i->sampler->get_id());
+ i->sampler->refresh();
+ }
+ else if(bound_tex_targets[i->binding])
+ {
+ if(ARB_direct_state_access)
+ glBindTextureUnit(i->binding, 0);
+ else
+ {
+ glActiveTexture(GL_TEXTURE0+i->binding);
+ glBindTexture(bound_tex_targets[i->binding], 0);
+ bound_tex_targets[i->binding] = 0;
+ }
+ }
+
+ i->changed = false;
+ }
+ }
+
+ if(mask&UNIFORMS)
+ {
+ if(last_applied && this!=last_applied)
+ {
+ vector<BoundUniformBlock>::const_iterator i = uniform_blocks.begin();
+ vector<BoundUniformBlock>::const_iterator j = last_applied->uniform_blocks.begin();
+ while(j!=last_applied->uniform_blocks.end())
+ {
+ if(i==uniform_blocks.end() || j->binding<i->binding)
+ {
+ glBindBufferBase(GL_UNIFORM_BUFFER, j->binding, 0);
+ ++j;
+ }
+ else
+ {
+ if(i->binding==j->binding)
+ ++j;
+ ++i;
+ }
+ }
+ }
+
+ for(vector<BoundUniformBlock>::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i)
+ if(i->changed)
+ {
+ if(i->block)
+ {
+ if(i->binding>=0)
+ {
+ const BufferBackedUniformBlock *block = static_cast<const BufferBackedUniformBlock *>(i->block);
+ glBindBufferRange(GL_UNIFORM_BUFFER, i->binding, block->get_buffer()->get_id(), block->get_offset(), block->get_data_size());
+ }
+ else
+ static_cast<const DefaultUniformBlock *>(i->block)->apply();
+ }
+ else
+ glBindBufferBase(GL_UNIFORM_BUFFER, i->binding, 0);
+
+ i->changed = false;
+ }
+ }
+
+ last_applied = this;
+ changes &= ~mask;
+}
+
+void PipelineState::clear()
+{
+ if(last_applied)
+ {
+ glUseProgram(0);
+ glBindVertexArray(0);
+
+ unsigned max_clip_planes = Limits::get_global().max_clip_planes;
+ for(unsigned i=0; i<max_clip_planes; ++i)
+ if((last_applied->enabled_clip_planes>>i)&1)
+ glDisable(GL_CLIP_PLANE0+i);
+
+ for(vector<BoundTexture>::const_iterator i=last_applied->textures.begin(); i!=last_applied->textures.end(); ++i)
+ if(i->texture && i->sampler)
+ {
+ if(ARB_direct_state_access)
+ glBindTextureUnit(i->binding, 0);
+ else
+ {
+ glActiveTexture(GL_TEXTURE0+i->binding);
+ glBindTexture(bound_tex_targets[i->binding], 0);
+ bound_tex_targets[i->binding] = 0;
+ }
+ }
+
+ for(vector<BoundUniformBlock>::const_iterator i=last_applied->uniform_blocks.begin(); i!=last_applied->uniform_blocks.end(); ++i)
+ if(i->block)
+ glBindBufferBase(GL_UNIFORM_BUFFER, i->binding, 0);
+
+ last_applied = 0;
+ }
+}
+
+
+PipelineState::BoundTexture::BoundTexture(unsigned b):
+ binding(b),
+ changed(false),
+ texture(0),
+ sampler(0)
+{ }
+
+
+PipelineState::BoundUniformBlock::BoundUniformBlock(int b):
+ binding(b),
+ changed(false),
+ block(0)
+{ }
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef PIPELINESTATE_H_
+#define PIPELINESTATE_H_
+
+#include <vector>
+#include <msp/core/noncopyable.h>
+
+namespace Msp {
+namespace GL {
+
+class BufferBackedUniformBlock;
+class DefaultUniformBlock;
+class Program;
+class Sampler;
+class Texture;
+class UniformBlock;
+class VertexSetup;
+class WindingTest;
+
+class PipelineState: public NonCopyable
+{
+private:
+ struct BoundTexture
+ {
+ unsigned binding;
+ mutable bool changed;
+ const Texture *texture;
+ const Sampler *sampler;
+
+ BoundTexture(unsigned);
+ };
+
+ struct BoundUniformBlock
+ {
+ int binding;
+ mutable bool changed;
+ const UniformBlock *block;
+
+ BoundUniformBlock(int);
+ };
+
+ enum ChangeMask
+ {
+ SHPROG = 1,
+ VERTEX_SETUP = 2,
+ WINDING_TEST = 4,
+ CLIP_PLANES = 8,
+ TEXTURES = 16,
+ UNIFORMS = 32
+ };
+
+ const Program *shprog;
+ const VertexSetup *vertex_setup;
+ const WindingTest *winding_test;
+ unsigned enabled_clip_planes;
+ std::vector<BoundTexture> textures;
+ std::vector<BoundUniformBlock> uniform_blocks;
+ mutable unsigned changes;
+
+ static const PipelineState *last_applied;
+ static std::vector<int> bound_tex_targets;
+
+public:
+ PipelineState();
+ ~PipelineState();
+
+ void set_shader_program(const Program *);
+ void set_vertex_setup(const VertexSetup *);
+ void set_winding_test(const WindingTest *);
+ void set_enabled_clip_planes(unsigned);
+ void set_texture(unsigned, const Texture *, const Sampler *);
+ void set_uniforms(const DefaultUniformBlock *);
+ void set_uniform_block(unsigned, const BufferBackedUniformBlock *);
+private:
+ void set_uniform_block_(int, const UniformBlock *);
+public:
+
+ const Program *get_shader_program() const { return shprog; }
+ const VertexSetup *get_vertex_setup() const { return vertex_setup; }
+ const WindingTest *get_winding_test() const { return winding_test; }
+
+ void apply() const;
+private:
+ void apply(unsigned) const;
+public:
+ static void clear();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
}
}
- Conditional<BindRestore> _bind(!ARB_separate_shader_objects, this);
+ if(!ARB_separate_shader_objects)
+ glUseProgram(id);
for(map<string, unsigned>::const_iterator i=transient->textures.begin(); i!=transient->textures.end(); ++i)
{
int location = get_uniform_location(i->first);
return i!=attributes.end() && i->name==name ? i->location : -1;
}
-void Program::bind() const
-{
- if(!linked)
- throw invalid_operation("Program::bind");
-
- if(!set_current(this))
- return;
-
- glUseProgram(id);
-}
-
-void Program::unbind()
-{
- if(!set_current(0))
- return;
-
- glUseProgram(0);
-}
-
void Program::set_debug_name(const string &name)
{
#ifdef DEBUG
A complete shader program. Programs can be assembled of individual Shaders or
generated with a set of standard features.
*/
-class Program: public Bindable<Program>
+class Program
{
public:
class Loader: public DataFile::CollectionObjectLoader<Program>
const AttributeInfo &get_attribute_info(const std::string &) const;
int get_attribute_location(const std::string &) const;
- void bind() const;
- static void unbind();
+ unsigned get_id() const { return id; }
void set_debug_name(const std::string &);
private:
#include "error.h"
#include "sampler.h"
#include "texture.h"
-#include "texunit.h"
using namespace std;
glGenSamplers(1, &id);
}
-void Sampler::update_parameter(int mask) const
+void Sampler::update() const
{
- if(mask&MIN_FILTER)
+ if(dirty_params&MIN_FILTER)
glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, min_filter);
- if(mask&MAG_FILTER)
+ if(dirty_params&MAG_FILTER)
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, mag_filter);
- if(mask&MAX_ANISOTROPY)
+ if(dirty_params&MAX_ANISOTROPY)
glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
- if(mask&WRAP_S)
+ if(dirty_params&WRAP_S)
glSamplerParameteri(id, GL_TEXTURE_WRAP_S, wrap_s);
- if(mask&WRAP_T)
+ if(dirty_params&WRAP_T)
glSamplerParameteri(id, GL_TEXTURE_WRAP_T, wrap_t);
- if(mask&WRAP_R)
+ if(dirty_params&WRAP_R)
glSamplerParameteri(id, GL_TEXTURE_WRAP_R, wrap_r);
- if(mask&BORDER_COLOR)
+ if(dirty_params&BORDER_COLOR)
glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, &border_color.r);
- if(mask&COMPARE)
+ if(dirty_params&COMPARE)
{
glSamplerParameteri(id, GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
if(compare)
glSamplerParameteri(id, GL_TEXTURE_COMPARE_FUNC, cmp_func);
}
+
+ dirty_params = 0;
}
void Sampler::set_min_filter(TextureFilter f)
{
min_filter = f;
- update_parameter(MIN_FILTER);
+ dirty_params |= MIN_FILTER;
}
void Sampler::set_mag_filter(TextureFilter f)
{
mag_filter = f;
- update_parameter(MAG_FILTER);
+ dirty_params |= MAG_FILTER;
}
void Sampler::set_filter(TextureFilter f)
static Require _req(EXT_texture_filter_anisotropic);
max_anisotropy = a;
if(EXT_texture_filter_anisotropic)
- update_parameter(MAX_ANISOTROPY);
+ dirty_params |= MAX_ANISOTROPY;
}
void Sampler::set_wrap(TextureWrap w)
void Sampler::set_wrap_s(TextureWrap w)
{
wrap_s = w;
- update_parameter(WRAP_S);
+ dirty_params |= WRAP_S;
}
void Sampler::set_wrap_t(TextureWrap w)
{
wrap_t = w;
- update_parameter(WRAP_T);
+ dirty_params |= WRAP_T;
}
void Sampler::set_wrap_r(TextureWrap w)
{
static Require _req(EXT_texture3D);
wrap_r = w;
- update_parameter(WRAP_R);
+ dirty_params |= WRAP_R;
}
void Sampler::set_border_color(const Color &c)
{
border_color = c;
- update_parameter(BORDER_COLOR);
+ dirty_params |= BORDER_COLOR;
}
void Sampler::disable_compare()
{
compare = false;
- update_parameter(COMPARE);
+ dirty_params |= COMPARE;
}
void Sampler::set_compare(Predicate f)
dirty_params |= COMPARE;
}
-void Sampler::bind_to(unsigned i) const
-{
- TexUnit &unit = TexUnit::get_unit(i);
-
- if(unit.set_sampler(this))
- {
- glBindSampler(i, id);
-
- if(dirty_params)
- {
- update_parameter(dirty_params);
- dirty_params = 0;
- }
- }
-}
-
-const Sampler *Sampler::current(unsigned i)
-{
- return TexUnit::get_unit(i).get_sampler();
-}
-
-void Sampler::unbind_from(unsigned i)
-{
- TexUnit &unit = TexUnit::get_unit(i);
- if(unit.set_sampler(0))
- glBindSampler(i, 0);
-}
-
void Sampler::set_debug_name(const string &name)
{
#ifdef DEBUG
Sampler();
private:
- void update_parameter(int) const;
+ void update() const;
void set_parameter_i(unsigned, int) const;
void set_parameter_f(unsigned, float) const;
void set_parameter_fv(unsigned, const float *) const;
bool is_compare_enabled() const { return compare; }
Predicate get_compare_function() const { return cmp_func; }
- void bind() const { bind_to(0); }
- void bind_to(unsigned) const;
+ void refresh() const { if(dirty_params) update(); }
- static const Sampler *current(unsigned = 0);
- static void unbind() { unbind_from(0); }
- static void unbind_from(unsigned);
+ unsigned get_id() const { return id; }
void set_debug_name(const std::string &);
};
#include "resourcemanager.h"
#include "resources.h"
#include "texture.h"
-#include "texunit.h"
using namespace std;
Texture::~Texture()
{
- while(TexUnit *unit = TexUnit::find_unit(this))
- unbind_from(unit->get_index());
-
if(id)
glDeleteTextures(1, &id);
}
// glGenerateMipmap is defined here
static Require _req(EXT_framebuffer_object);
+ if(!ARB_direct_state_access)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+ generate_mipmap_();
+ if(!ARB_direct_state_access)
+ glBindTexture(target, 0);
+}
+
+void Texture::generate_mipmap_()
+{
if(ARB_direct_state_access)
glGenerateTextureMipmap(id);
else
- {
- BindRestore _bind(this);
glGenerateMipmap(target);
- }
}
void Texture::set_auto_generate_mipmap(bool gm)
image(img, 0U);
}
-void Texture::bind_to(unsigned i) const
-{
- if(!id)
- {
- if(manager)
- manager->resource_used(*this);
- if(!id)
- {
- unbind_from(i);
- return;
- }
- }
-
- TexUnit &unit = TexUnit::get_unit(i);
- if(unit.set_texture(this))
- {
- if(manager)
- manager->resource_used(*this);
-
- if(ARB_direct_state_access)
- glBindTextureUnit(i, id);
- else
- {
- unit.bind();
- glBindTexture(target, id);
- }
- }
-}
-
-const Texture *Texture::current(unsigned i)
-{
- return TexUnit::get_unit(i).get_texture();
-}
-
-void Texture::unbind_from(unsigned i)
-{
- TexUnit &unit = TexUnit::get_unit(i);
- const Texture *cur = unit.get_texture();
- if(unit.set_texture(0))
- {
- if(ARB_direct_state_access)
- glBindTextureUnit(i, 0);
- else
- {
- unit.bind();
- glBindTexture(cur->target, 0);
- }
- }
-}
-
void Texture::set_debug_name(const string &name)
{
#ifdef DEBUG
static bool can_generate_mipmap();
void generate_mipmap();
+protected:
+ void generate_mipmap_();
+public:
/** Sets automatic mipmap generation. If enabled, mipmaps are generated
when a texture image is uploaded. */
void set_auto_generate_mipmap(bool);
GLenum get_target() const { return target; }
unsigned get_id() const { return id; }
- void bind() const { bind_to(0); }
- void bind_to(unsigned) const;
-
- static const Texture *current(unsigned = 0);
- static void unbind() { unbind_from(0); }
- static void unbind_from(unsigned);
-
virtual UInt64 get_data_size() const { return 0; }
void set_debug_name(const std::string &);
throw invalid_operation("Texture1D::allocate");
if(level>=levels)
throw invalid_argument("Texture1D::allocate");
+
+ bool direct = ARB_texture_storage && ARB_direct_state_access;
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
+
+ if(!direct)
+ glBindTexture(target, 0);
+}
+
+void Texture1D::allocate_(unsigned level)
+{
if(allocated&(1<<level))
return;
if(ARB_texture_storage)
{
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
if(ARB_direct_state_access)
glTextureStorage1D(id, levels, storage_fmt, width);
else
allocated |= (1<<levels)-1;
}
else
- image(level, 0);
+ image_(level, 0);
}
void Texture1D::image(unsigned level, const void *data)
if(level>=levels)
throw out_of_range("Texture1D::image");
- unsigned w = get_level_size(level);
-
if(ARB_texture_storage)
- return sub_image(level, 0, w, data);
+ return sub_image(level, 0, get_level_size(level), data);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+
+ image_(level, data);
+
+ if(auto_gen_mipmap && level==0)
+ {
+ generate_mipmap_();
+ allocated |= (1<<levels)-1;
+ }
- BindRestore _bind(this);
+ glBindTexture(target, 0);
+}
+void Texture1D::image_(unsigned level, const void *data)
+{
if(!allocated)
{
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
- glTexImage1D(target, level, storage_fmt, w, 0, comp, type, data);
+ glTexImage1D(target, level, storage_fmt, get_level_size(level), 0, comp, type, data);
allocated |= 1<<level;
- if(auto_gen_mipmap && level==0)
- {
- generate_mipmap();
- allocated |= (1<<levels)-1;
- }
}
void Texture1D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
if(level>=levels)
throw out_of_range("Texture1D::sub_image");
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
- allocate(level);
+ bool direct = (ARB_direct_state_access && (ARB_texture_storage || (allocated&(1<<level))));
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
glTexSubImage1D(target, level, x, wd, comp, type, data);
if(auto_gen_mipmap && level==0)
- generate_mipmap();
+ generate_mipmap_();
+
+ if(!direct)
+ glBindTexture(target, 0);
}
void Texture1D::sub_image(unsigned level, int x, unsigned wd, PixelComponents comp, DataType type, const void *data)
{ storage(make_pixelformat(c, UNSIGNED_BYTE), w, l); }
void allocate(unsigned);
+private:
+ void allocate_(unsigned);
+public:
void image(unsigned, const void *);
+private:
+ void image_(unsigned, const void *);
+public:
DEPRECATED void image(unsigned, PixelComponents, DataType, const void *);
void sub_image(unsigned, int, unsigned, const void *);
DEPRECATED void sub_image(unsigned, int, unsigned, PixelComponents, DataType, const void *);
throw invalid_operation("Texture2D::allocate");
if(level>=levels)
throw invalid_argument("Texture2D::allocate");
+
+ bool direct = ARB_texture_storage && ARB_direct_state_access;
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
+
+ if(!direct)
+ glBindTexture(target, 0);
+}
+
+void Texture2D::allocate_(unsigned level)
+{
if(allocated&(1<<level))
return;
if(ARB_texture_storage)
{
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
if(ARB_direct_state_access)
glTextureStorage2D(id, levels, storage_fmt, width, height);
else
allocated |= (1<<levels)-1;
}
else
- image(level, 0);
+ image_(level, 0);
}
void Texture2D::image(unsigned level, const void *data)
if(level>=levels)
throw out_of_range("Texture2D::image");
- LinAl::Vector<unsigned, 2> size = get_level_size(level);
-
if(ARB_texture_storage)
+ {
+ LinAl::Vector<unsigned, 2> size = get_level_size(level);
return sub_image(level, 0, 0, size.x, size.y, data);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+
+ image_(level, data);
+
+ if(auto_gen_mipmap && level==0)
+ {
+ generate_mipmap_();
+ allocated |= (1<<levels)-1;
+ }
- BindRestore _bind(this);
+ glBindTexture(target, 0);
+}
+void Texture2D::image_(unsigned level, const void *data)
+{
if(!allocated)
{
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
apply_swizzle();
}
+ LinAl::Vector<unsigned, 2> size = get_level_size(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
glTexImage2D(target, level, storage_fmt, size.x, size.y, 0, comp, type, data);
allocated |= 1<<level;
- if(auto_gen_mipmap && level==0)
- {
- generate_mipmap();
- allocated |= (1<<levels)-1;
- }
}
void Texture2D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
if(level>=levels)
throw out_of_range("Texture2D::sub_image");
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
- allocate(level);
+ bool direct = (ARB_direct_state_access && (ARB_texture_storage || (allocated&(1<<level))));
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
if(auto_gen_mipmap && level==0)
- generate_mipmap();
+ generate_mipmap_();
+
+ if(!direct)
+ glBindTexture(target, 0);
}
void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
image.load_into(*img_loader, mapped_address);
else if(phase==3)
{
- Bind _bind_buf(pixel_buffer, PIXEL_UNPACK_BUFFER);
mapped_address = 0;
if(!pixel_buffer.unmap())
{
if(!texture.id)
texture.generate_id();
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer.get_id());
texture.image(image, 0, true);
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
++phase;
undefined. If storage has already been allocated, does nothing. */
void allocate(unsigned level);
+private:
+ void allocate_(unsigned level);
+
+public:
/** Updates the contents of the entire texture. Storage must be defined
beforehand. The image data must have dimensions and format matching the
defined storage. */
virtual void image(unsigned level, const void *data);
+private:
+ void image_(unsigned level, const void *data);
+
+public:
DEPRECATED void image(unsigned level, PixelComponents fmt, DataType type, const void *data);
/** Updates a rectangular region of the texture. Storage must be defined
if(allocated&(1<<level))
return;
+ bool direct = ARB_texture_storage && ARB_direct_state_access;
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
+
+ if(!direct)
+ glBindTexture(target, 0);
+}
+
+void Texture3D::allocate_(unsigned level)
+{
if(ARB_texture_storage)
{
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
if(ARB_direct_state_access)
glTextureStorage3D(id, levels, storage_fmt, width, height, depth);
else
allocated |= (1<<levels)-1;
}
else
- image(level, 0);
+ image_(level, 0);
}
void Texture3D::image(unsigned level, const void *data)
if(level>=levels)
throw out_of_range("Texture3D::image");
- LinAl::Vector<unsigned, 3> size = get_level_size(level);
-
if(ARB_texture_storage)
+ {
+ LinAl::Vector<unsigned, 3> size = get_level_size(level);
return sub_image(level, 0, 0, 0, size.x, size.y, size.z, data);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
- BindRestore _bind(this);
+ image_(level, data);
+ if(auto_gen_mipmap && level==0)
+ {
+ generate_mipmap_();
+ allocated |= (1<<levels)-1;
+ }
+
+ glBindTexture(target, 0);
+}
+
+void Texture3D::image_(unsigned level, const void *data)
+{
if(!allocated)
{
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
apply_swizzle();
}
+ LinAl::Vector<unsigned, 3> size = get_level_size(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
glTexImage3D(target, level, storage_fmt, size.x, size.y, size.z, 0, comp, type, data);
allocated |= 1<<level;
- if(auto_gen_mipmap && level==0)
- {
- generate_mipmap();
- allocated |= (1<<levels)-1;
- }
}
void Texture3D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
if(level>=levels)
throw out_of_range("Texture3D::sub_image");
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
- allocate(level);
+ bool direct = (ARB_direct_state_access && (ARB_texture_storage || (allocated&(1<<level))));
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data);
if(auto_gen_mipmap && level==0)
- generate_mipmap();
+ generate_mipmap_();
+
+ if(!direct)
+ glBindTexture(target, 0);
}
void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, PixelComponents comp, DataType type, const void *data)
undefined. If storage has already been allocated, does nothing. */
void allocate(unsigned level);
+private:
+ void allocate_(unsigned);
+
+public:
/** Updates the contents of the entire texture. Storage must be defined
beforehand. The image data must have dimensions and format matching the
defined storage. */
void image(unsigned level, const void *data);
+private:
+ void image_(unsigned, const void *);
+
+public:
DEPRECATED void image(unsigned level, PixelComponents comp, DataType type, const void *data);
/** Updates a cuboid-shaped region of the texture. Storage must be defined
if(allocated&(64<<level))
return;
+ bool direct = ARB_texture_storage && ARB_direct_state_access;
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
+
+ if(!direct)
+ glBindTexture(target, 0);
+}
+
+void TextureCube::allocate_(unsigned level)
+{
if(ARB_texture_storage)
{
- Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
if(ARB_direct_state_access)
glTextureStorage2D(id, levels, storage_fmt, size, size);
else
else
{
for(unsigned i=0; i<6; ++i)
- image(enumerate_faces(i), level, 0);
+ image_(enumerate_faces(i), level, 0);
}
}
if(level>=levels)
throw out_of_range("TextureCube::image");
- unsigned s = get_level_size(level);
-
if(ARB_texture_storage)
- return sub_image(face, level, 0, 0, s, s, data);
+ {
+ unsigned lsz = get_level_size(level);
+ return sub_image(face, level, 0, 0, lsz, lsz, data);
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
- BindRestore _bind(this);
+ image_(face, level, data);
+ if(auto_gen_mipmap && level==0 && (allocated&63)==63)
+ {
+ generate_mipmap_();
+ allocated |= (64<<levels)-1;
+ }
+
+ glBindTexture(target, 0);
+}
+
+void TextureCube::image_(TextureCubeFace face, unsigned level, const void *data)
+{
if(!allocated)
{
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
apply_swizzle();
}
+ unsigned lsz = get_level_size(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
- glTexImage2D(face, level, storage_fmt, s, s, 0, comp, type, data);
+ glTexImage2D(face, level, storage_fmt, lsz, lsz, 0, comp, type, data);
if(level==0)
{
allocated |= 1<<get_face_index(face);
if((allocated&63)==63)
- {
allocated |= 64;
- if(auto_gen_mipmap)
- {
- generate_mipmap();
- allocated |= (64<<levels)-1;
- }
- }
}
else if(!(allocated&(64<<level)))
{
for(unsigned i=0; i<6; ++i)
if(enumerate_faces(i)!=face)
- glTexImage2D(enumerate_faces(i), level, storage_fmt, s, s, 0, comp, type, 0);
+ glTexImage2D(enumerate_faces(i), level, storage_fmt, lsz, lsz, 0, comp, type, 0);
allocated |= 64<<level;
}
if(level>=levels)
throw out_of_range("TextureCube::sub_image");
- Conditional<BindRestore> _bind(!ARB_direct_state_acess, this);
- allocate(level);
+ bool direct = (ARB_direct_state_access && (ARB_texture_storage || (allocated&(1<<level))));
+ if(!direct)
+ {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(target, id);
+ }
+
+ allocate_(level);
PixelComponents comp = get_components(storage_fmt);
GLenum type = get_gl_type(get_component_type(storage_fmt));
glTexSubImage2D(face, level, x, y, wd, ht, comp, type, data);
if(auto_gen_mipmap && level==0)
- generate_mipmap();
+ generate_mipmap_();
+
+ if(!direct)
+ glBindTexture(target, 0);
}
void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
undefined. If storage has already been allocated, does nothing. */
void allocate(unsigned level);
+private:
+ void allocate_(unsigned);
+
+public:
/** Updates the contents of a face. Storage must be defined beforehand.
The image data must have dimensions and format matching the defined
storage. */
void image(TextureCubeFace face, unsigned level, const void *data);
+private:
+ void image_(TextureCubeFace, unsigned, const void *);
+
+public:
DEPRECATED void image(TextureCubeFace face, unsigned level,
PixelComponents comp, DataType type, const void *data);
+++ /dev/null
-#include <stdexcept>
-#include <msp/gl/extensions/arb_multitexture.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include "deviceinfo.h"
-#include "gl.h"
-#include "misc.h"
-#include "texture.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-vector<TexUnit> TexUnit::units;
-TexUnit *TexUnit::cur_unit = 0;
-
-TexUnit::TexUnit():
- texture(0),
- sampler(0)
-{ }
-
-bool TexUnit::set_texture(const Texture *tex)
-{
- bool result = (tex!=texture);
- texture = tex;
- return result;
-}
-
-bool TexUnit::set_sampler(const Sampler *samp)
-{
- bool result = (samp!=sampler);
- sampler = samp;
- return result;
-}
-
-void TexUnit::bind()
-{
- if(cur_unit!=this && (cur_unit || index))
- glActiveTexture(GL_TEXTURE0+index);
- cur_unit = this;
-}
-
-unsigned TexUnit::get_n_units()
-{
- return Limits::get_global().max_texture_bindings;
-}
-
-TexUnit &TexUnit::get_unit(unsigned n)
-{
- if(n>0)
- static Require _req(ARB_multitexture);
- if(n>=Limits::get_global().max_texture_bindings)
- throw out_of_range("TexUnit::get_unit");
-
- if(units.size()<=n)
- {
- unsigned i = units.size();
- units.resize(n+1, TexUnit());
- for(; i<units.size(); ++i)
- units[i].index = i;
- }
-
- return units[n];
-}
-
-TexUnit &TexUnit::current()
-{
- if(!cur_unit)
- get_unit(0).bind();
- return *cur_unit;
-}
-
-TexUnit *TexUnit::find_unit(const Texture *tex)
-{
- for(vector<TexUnit>::iterator i=units.begin(); i!=units.end(); ++i)
- if(i->texture==tex)
- return &*i;
- return 0;
-}
-
-} // namespace GL
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_GL_TEXUNIT_H_
-#define MSP_GL_TEXUNIT_H_
-
-#include <vector>
-
-namespace Msp {
-namespace GL {
-
-class Sampler;
-class Texture;
-
-/**
-Keeps track of texture unit related state. Intended for internal use.
-*/
-class TexUnit
-{
-private:
- unsigned index;
- const Texture *texture;
- const Sampler *sampler;
-
- static std::vector<TexUnit> units;
- static TexUnit *cur_unit;
-
- TexUnit();
-
-public:
- unsigned get_index() const { return index; }
- bool set_texture(const Texture *);
- const Texture *get_texture() const { return texture; }
- bool set_sampler(const Sampler *);
- const Sampler *get_sampler() const { return sampler; }
- void bind();
-
- DEPRECATED static unsigned get_n_units();
- static TexUnit &get_unit(unsigned);
- static TexUnit ¤t();
- static TexUnit *find_unit(const Texture *);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
uniforms[index] = &uni;
}
-void DefaultUniformBlock::apply(int index) const
+void DefaultUniformBlock::apply() const
{
- if(index>=0)
- throw invalid_argument("DefaultUniformBlock::apply");
-
for(unsigned i=0; i<uniforms.size(); ++i)
if(uniforms[i])
uniforms[i]->apply(i);
BufferBackedUniformBlock::BufferBackedUniformBlock(unsigned s):
size(s),
- data(size),
- buf_range(0)
+ data(size)
{
static Require _req(ARB_shader_objects);
static Require _req2(ARB_uniform_buffer_object);
throw invalid_argument("BufferBackedUniformBlock::BufferBackedUniformBlock");
}
-BufferBackedUniformBlock::~BufferBackedUniformBlock()
-{
- delete buf_range;
-}
-
unsigned BufferBackedUniformBlock::get_alignment() const
{
return Limits::get_global().uniform_buffer_alignment;
}
-void BufferBackedUniformBlock::location_changed(Buffer *buf, unsigned off, unsigned) const
-{
- delete buf_range;
- buf_range = buf->create_range(off, size);
-}
-
void BufferBackedUniformBlock::attach(const Program::UniformInfo &info, const Uniform &uni)
{
if(info.block->bind_point<0)
dirty = true;
}
-void BufferBackedUniformBlock::apply(int index) const
-{
- if(index<0)
- throw invalid_argument("BufferBackedUniformBlock::apply");
- if(!get_buffer())
- throw invalid_operation("UniformBlock::apply");
-
- refresh();
- buf_range->bind_to(UNIFORM_BUFFER, index);
-}
-
} // namespace GL
} // namespace Msp
virtual ~UniformBlock() { }
virtual void attach(const Program::UniformInfo &, const Uniform &) = 0;
- virtual void apply(int) const = 0;
};
/** Stores uniforms for the default uniform block. Uniforms are associated
virtual void attach(const Program::UniformInfo &, const Uniform &);
void attach(int, const Uniform &);
- virtual void apply(int) const;
+ void apply() const;
};
/** Stores uniforms for a buffer-backed uniform block. Uniform values are
private:
unsigned size;
std::vector<char> data;
- mutable BufferRange *buf_range;
public:
BufferBackedUniformBlock(unsigned);
- virtual ~BufferBackedUniformBlock();
-private:
virtual unsigned get_data_size() const { return size; }
+private:
virtual const void *get_data_pointer() const { return &data[0]; }
virtual unsigned get_alignment() const;
- virtual void location_changed(Buffer *, unsigned, unsigned) const;
public:
void attach(const Program::UniformInfo &, const Uniform &);
- virtual void apply(int) const;
};
} // namespace GL
VertexSetup::~VertexSetup()
{
- if(current()==this)
- unbind();
glDeleteVertexArrays(1, &id);
}
throw invalid_argument("VertexSetup::set_vertex_array");
vertex_array = &a;
- update(VERTEX_ARRAY);
+ dirty |= VERTEX_ARRAY;
}
void VertexSetup::set_instance_array(const VertexArray &a)
static Require req(ARB_instanced_arrays);
inst_array = &a;
- update(INSTANCE_ARRAY);
+ dirty |= INSTANCE_ARRAY;
}
void VertexSetup::set_index_buffer(const Buffer &ibuf)
{
index_buffer = &ibuf;
- update(INDEX_BUFFER);
+ dirty |= INDEX_BUFFER;
}
bool VertexSetup::verify_format(const VertexFormat &fmt)
return true;
}
-void VertexSetup::update(unsigned mask) const
+void VertexSetup::update() const
{
static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
- if(!direct && current()!=this)
- {
- dirty |= mask;
- return;
- }
- if(mask&VERTEX_ARRAY)
+ if(dirty&VERTEX_ARRAY)
update_vertex_array(*vertex_array, 0, 0, direct);
- if((mask&INSTANCE_ARRAY) && inst_array)
+ if(dirty&INSTANCE_ARRAY)
update_vertex_array(*inst_array, 1, 1, direct);
- if(mask&INDEX_BUFFER)
+ if(dirty&INDEX_BUFFER)
{
if(direct)
glVertexArrayElementBuffer(id, index_buffer->get_id());
else
- glBindBuffer(ELEMENT_ARRAY_BUFFER, index_buffer->get_id());
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer->get_id());
}
+
+ dirty = 0;
}
void VertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
{
- Conditional<Bind> bind_vbuf(!direct, array.get_buffer(), ARRAY_BUFFER);
+ if(!direct)
+ glBindBuffer(GL_ARRAY_BUFFER, array.get_buffer()->get_id());
const VertexFormat &fmt = array.get_format();
unsigned stride = fmt.stride()*sizeof(float);
}
offset += sz*sizeof(float);
}
-}
-void VertexSetup::bind() const
-{
- if(!vertex_array || !index_buffer)
- throw invalid_operation("VertexSetup::bind");
-
- if(set_current(this))
- {
- vertex_array->refresh();
- if(inst_array)
- inst_array->refresh();
- glBindVertexArray(id);
- if(dirty)
- {
- update(dirty);
- dirty = 0;
- }
- }
-}
-
-void VertexSetup::unbind()
-{
- if(set_current(0))
- glBindVertexArray(0);
+ if(!direct)
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void VertexSetup::unload()
}
else
{
- BindRestore _bind(*this);
- Buffer::unbind_from(ARRAY_BUFFER);
+ glBindVertexArray(id);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
for(const unsigned char *a=vertex_format.begin(); a!=vertex_format.end(); ++a)
{
glVertexAttribPointer(sem, 1, GL_FLOAT, false, 0, 0);
}
- glBindBuffer(ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
vertex_array = 0;
namespace Msp {
namespace GL {
+class Buffer;
class VertexArray;
/**
Combines a VertexArray with an index buffer. This wraps OpenGL's vertex array
objects. Intended for internal use.
*/
-class VertexSetup: public Bindable<VertexSetup>
+class VertexSetup
{
private:
enum ComponentMask
private:
static bool verify_format(const VertexFormat &);
- void update(unsigned) const;
+ void update() const;
void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const;
public:
- void bind() const;
- static void unbind();
+ void refresh() const { if(dirty) update(); }
+
+ unsigned get_id() const { return id; }
void unload();
WindingTest();
WindingTest(FaceWinding);
+ FaceWinding get_winding() const { return winding; }
+
void bind() const;
static void unbind();
#include "resources.h"
#include "shader.h"
#include "tests.h"
-#include "texunit.h"
using namespace std;
#include "mesh.h"
#include "renderer.h"
#include "resources.h"
-#include "texunit.h"
using namespace std;
#include "color.h"
#include "error.h"
#include "matrix.h"
+#include "pipelinestate.h"
#include "program.h"
#include "programdata.h"
#include "uniform.h"
}
}
-void ProgramData::apply() const
+vector<ProgramData::ProgramBlock>::const_iterator ProgramData::prepare_program(const Program &prog) const
{
- const Program *prog = Program::current();
- if(!prog)
- throw invalid_operation("ProgramData::apply");
-
BufferBackedUniformBlock *old_last_block = last_buffer_block;
- vector<ProgramBlock>::iterator prog_begin = get_program(*prog);
- Program::LayoutHash prog_hash = prog->get_uniform_layout_hash();
+ vector<ProgramBlock>::iterator prog_begin = get_program(prog);
Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
Mask affected = (dirty&prog_begin->masks.used) | force_dirty;
dirty = 0;
}
- const vector<Program::UniformBlockInfo> &block_infos = prog->get_uniform_blocks();
+ const vector<Program::UniformBlockInfo> &block_infos = prog.get_uniform_blocks();
if(prog_begin->masks.dirty==ALL_ONES)
{
}
}
- for(vector<ProgramBlock>::iterator i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i)
+ return prog_begin;
+}
+
+void ProgramData::apply(const Program &prog, PipelineState &state) const
+{
+ vector<ProgramBlock>::const_iterator prog_begin = prepare_program(prog);
+ Program::LayoutHash prog_hash = prog_begin->prog_hash;
+ for(vector<ProgramBlock>::const_iterator i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i)
if(i->block)
- i->block->apply(i->bind_point);
+ {
+ if(i->bind_point<0)
+ state.set_uniforms(static_cast<const DefaultUniformBlock *>(i->block));
+ else
+ {
+ const BufferBackedUniformBlock *block = static_cast<const BufferBackedUniformBlock *>(i->block);
+ block->refresh();
+ state.set_uniform_block(i->bind_point, block);
+ }
+ }
}
void ProgramData::set_debug_name(const string &name)
class Buffer;
class BufferBackedUniformBlock;
+class PipelineState;
class UniformBlock;
struct Color;
void update_block_uniform_indices(SharedBlock &, const Program::UniformBlockInfo &) const;
void update_block(SharedBlock &, const Program::UniformBlockInfo &) const;
+ std::vector<ProgramBlock>::const_iterator prepare_program(const Program &) const;
public:
- /** Applies uniform blocks for the currently bound program, creating them
- if needed. */
- void apply() const;
+ void apply(const Program &, PipelineState &) const;
void set_debug_name(const std::string &);
};
#include "programdata.h"
#include "renderable.h"
#include "renderer.h"
+#include "resourcemanager.h"
#include "sampler.h"
#include "texture.h"
-#include "texunit.h"
#include "vertexarray.h"
#include "vertexsetup.h"
#include "windingtest.h"
void Renderer::set_texture(Tag tag, const Texture *tex, const Sampler *samp)
{
+ if(tex)
+ if(ResourceManager *res_mgr = tex->get_manager())
+ res_mgr->resource_used(*tex);
+
if(texture_stack.size()>state->texture_count)
{
BoundTexture &bt = texture_stack[state->texture_count];
void Renderer::flush_textures()
{
- for(unsigned i=0; i<texture_stack.size(); ++i)
- {
- BoundTexture &bt = texture_stack[i];
- if(i>=state->texture_count && bt.unit>=0)
- {
- Texture::unbind_from(bt.unit);
- Sampler::unbind_from(bt.unit);
- }
- else if(bt.replaced>=static_cast<int>(state->texture_count))
- bt.replaced = -1;
- }
+ for(unsigned i=0; i<state->texture_count; ++i)
+ if(texture_stack[i].replaced>=static_cast<int>(state->texture_count))
+ texture_stack[i].replaced = -1;
texture_stack.erase(texture_stack.begin()+state->texture_count, texture_stack.end());
}
*state = State();
shdata_stack.clear();
+ add_shader_data(standard_shdata);
excluded.clear();
- for(vector<BoundTexture>::iterator i=texture_stack.begin(); i!=texture_stack.end(); ++i)
- if(i->unit>=0)
- {
- Texture::unbind_from(i->unit);
- Sampler::unbind_from(i->unit);
- }
- Clipping::unbind();
- Program::unbind();
- VertexSetup::unbind();
- Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
- WindingTest::unbind();
+ PipelineState::clear();
}
void Renderer::exclude(const Renderable &renderable)
if(!state->shprog)
throw invalid_operation("Renderer::apply_state");
- /* We (mostly) let the objects themselves figure out if the binding has
- changed */
+ if(changed&MATRIX)
+ {
+ standard_shdata.uniform("world_obj_matrix", state->model_matrix);
+ LinAl::SquareMatrix<float, 3> nm = state->model_matrix.block<3, 3>(0, 0);
+ nm = transpose(invert(nm));
+ standard_shdata.uniform("world_obj_normal_matrix", nm);
+ changed &= ~MATRIX;
+ }
- if(state->texture_count<texture_stack.size())
- flush_textures();
+ bool shprog_changed = (state->shprog!=pipeline_state.get_shader_program());
+ pipeline_state.set_shader_program(state->shprog);
- for(vector<BoundTexture>::const_iterator i=texture_stack.begin(); i!=texture_stack.end(); ++i)
+ if(state->vertex_setup)
{
- int unit = state->shprog->get_uniform_binding(i->tag);
- if(unit>=0)
- {
- if(i->texture)
- i->texture->bind_to(unit);
- if(i->sampler)
- i->sampler->bind_to(unit);
- i->unit = unit;
- }
+ if(const VertexArray *array = state->vertex_setup->get_vertex_array())
+ array->refresh();
+ if(const VertexArray *array = state->vertex_setup->get_instance_array())
+ array->refresh();
}
+ pipeline_state.set_vertex_setup(state->vertex_setup);
- if(state->clipping)
- state->clipping->bind();
- else
- Clipping::unbind();
+ pipeline_state.set_winding_test((state->winding_test && state->reverse_winding) ? &state->winding_test->get_reverse() : state->winding_test);
+ pipeline_state.set_enabled_clip_planes(state->clipping ? (1<<state->clipping->get_n_planes())-1 : 0);
- bool shprog_changed = (state->shprog!=Program::current());
- state->shprog->bind();
+ if(state->texture_count<texture_stack.size())
+ flush_textures();
- if(changed&MATRIX)
+ for(vector<BoundTexture>::const_iterator i=texture_stack.begin(); i!=texture_stack.end(); ++i)
{
- standard_shdata.uniform("world_obj_matrix", state->model_matrix);
- LinAl::SquareMatrix<float, 3> nm = state->model_matrix.block<3, 3>(0, 0);
- nm = transpose(invert(nm));
- standard_shdata.uniform("world_obj_normal_matrix", nm);
- changed &= ~MATRIX;
+ int unit = (i->tag.id ? state->shprog->get_uniform_binding(i->tag) : i->unit);
+ if(unit>=0)
+ pipeline_state.set_texture(unit, i->texture, i->sampler);
}
bool shdata_changed = changed&SHADER_DATA;
shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
for(vector<BoundProgramData>::const_iterator i=shdata_stack.begin(); i!=shdata_stack.end(); ++i)
{
- i->shdata->apply();
+ i->shdata->apply(*state->shprog, pipeline_state);
i->generation = i->shdata->get_generation();
}
changed &= ~SHADER_DATA;
}
- if(state->vertex_setup)
- state->vertex_setup->bind();
- else
- VertexSetup::unbind();
-
- if(state->winding_test)
- {
- if(state->reverse_winding)
- state->winding_test->get_reverse().bind();
- else
- state->winding_test->bind();
- }
- else
- WindingTest::unbind();
+ pipeline_state.apply();
}
#include <set>
#include <vector>
#include "matrix.h"
+#include "pipelinestate.h"
#include "programdata.h"
#include "tag.h"
ProgramData standard_shdata;
std::vector<BoundProgramData> shdata_stack;
std::set<const Renderable *> excluded;
+ PipelineState pipeline_state;
public:
Renderer();
if(!internal_renderer)
internal_renderer = new Renderer;
render(*internal_renderer);
+ internal_renderer->end();
}
void View::render(Renderer &renderer)