]> git.tdb.fi Git - libs/gl.git/blob - source/bufferable.cpp
Miscellaneous fixes
[libs/gl.git] / source / bufferable.cpp
1 #include <stdexcept>
2 #include "bindable.h"
3 #include "buffer.h"
4 #include "bufferable.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace GL {
10
11 Bufferable::Bufferable():
12         buffer(0),
13         offset(0),
14         next_in_buffer(0),
15         prev_in_buffer(0),
16         dirty(false)
17 { }
18
19 Bufferable::~Bufferable()
20 {
21         unlink_from_buffer();
22 }
23
24 void Bufferable::use_buffer(Buffer *buf, Bufferable *prev)
25 {
26         if(prev && buf!=prev->buffer)
27                 throw invalid_argument("Bufferable::use_buffer");
28
29         if(buffer)
30                 unlink_from_buffer();
31
32         buffer = buf;
33         if(buffer)
34         {
35                 prev_in_buffer = prev;
36                 if(prev_in_buffer)
37                 {
38                         next_in_buffer = prev_in_buffer->next_in_buffer;
39                         prev_in_buffer->next_in_buffer = this;
40                 }
41         }
42
43         dirty = true;
44         update_offset();
45 }
46
47 Bufferable::AsyncUpdater *Bufferable::refresh_async() const
48 {
49         return dirty ? new AsyncUpdater(*this) : 0;
50 }
51
52 void Bufferable::unlink_from_buffer()
53 {
54         if(prev_in_buffer)
55                 prev_in_buffer->next_in_buffer = next_in_buffer;
56         if(next_in_buffer)
57         {
58                 next_in_buffer->prev_in_buffer = prev_in_buffer;
59                 next_in_buffer->update_offset();
60         }
61         prev_in_buffer = 0;
62         next_in_buffer = 0;
63         buffer = 0;
64 }
65
66 void Bufferable::update_offset()
67 {
68         unsigned new_offset = 0;
69         if(prev_in_buffer)
70                 new_offset = prev_in_buffer->offset+prev_in_buffer->get_data_size();
71
72         unsigned align = get_alignment();
73         new_offset += align-1;
74         new_offset -= new_offset%align;
75         if(new_offset!=offset)
76         {
77                 offset = new_offset;
78                 dirty = true;
79                 offset_changed();
80         }
81
82         if(next_in_buffer)
83                 next_in_buffer->update_offset();
84
85         /* Do not resize the buffer here, as more bufferables may be added before
86         the buffer is actually used. */
87 }
88
89 bool Bufferable::resize_buffer() const
90 {
91         if(offset+get_data_size()>=buffer->get_size())
92         {
93                 const Bufferable *last = this;
94                 for(; last->next_in_buffer; last=last->next_in_buffer) ;
95
96                 unsigned total_size = last->offset+last->get_data_size();
97
98                 if(total_size>buffer->get_size())
99                 {
100                         buffer->data(total_size, 0);
101                         return true;
102                 }
103         }
104
105         return false;
106 }
107
108 void Bufferable::update_buffer() const
109 {
110         BindRestore bind(buffer, buffer->get_type());
111         if(resize_buffer())
112         {
113                 /* Resizing the buffer invalidates its contents.  Non-dirty data may
114                 be in use, so reupload it. */
115                 for(const Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer)
116                         if(!b->dirty)
117                                 b->upload_data(0);
118                 for(const Bufferable *b=next_in_buffer; b; b=b->next_in_buffer)
119                         if(!b->dirty)
120                                 b->upload_data(0);
121         }
122
123         upload_data(0);
124         dirty = false;
125 }
126
127 void Bufferable::upload_data(char *target) const
128 {
129         if(target)
130         {
131                 const char *source = reinterpret_cast<const char *>(get_data_pointer());
132                 copy(source, source+get_data_size(), target);
133         }
134         else
135                 buffer->sub_data(offset, get_data_size(), get_data_pointer());
136 }
137
138
139 Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
140         bufferable(b)
141 {
142         buffer_resized = bufferable.resize_buffer();
143         mapped_address = reinterpret_cast<char *>(bufferable.buffer->map(WRITE_ONLY));
144 }
145
146 Bufferable::AsyncUpdater::~AsyncUpdater()
147 {
148         bufferable.buffer->unmap();
149 }
150
151 void Bufferable::AsyncUpdater::upload_data()
152 {
153         bufferable.upload_data(mapped_address+bufferable.offset);
154         // Update all bufferables in the same buffer at once
155         for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer)
156                 if(b->dirty || buffer_resized)
157                 {
158                         b->upload_data(mapped_address+b->offset);
159                         b->dirty = false;
160                 }
161         for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer)
162                 if(b->dirty || buffer_resized)
163                 {
164                         b->upload_data(mapped_address+b->offset);
165                         b->dirty = false;
166                 }
167 }
168
169 } // namespace GL
170 } // namespace Msp