]> git.tdb.fi Git - libs/gl.git/blob - source/bufferable.cpp
Add an asynchronous update interface to Bufferable
[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         update_offset();
44 }
45
46 Bufferable::AsyncUpdater *Bufferable::refresh_async() const
47 {
48         return dirty ? new AsyncUpdater(*this) : 0;
49 }
50
51 void Bufferable::unlink_from_buffer()
52 {
53         if(prev_in_buffer)
54                 prev_in_buffer->next_in_buffer = next_in_buffer;
55         if(next_in_buffer)
56         {
57                 next_in_buffer->prev_in_buffer = prev_in_buffer;
58                 next_in_buffer->update_offset();
59         }
60         prev_in_buffer = 0;
61         next_in_buffer = 0;
62         buffer = 0;
63 }
64
65 void Bufferable::update_offset()
66 {
67         unsigned new_offset = 0;
68         if(prev_in_buffer)
69                 new_offset = prev_in_buffer->offset+prev_in_buffer->get_data_size();
70
71         unsigned align = get_alignment();
72         new_offset += align-1;
73         new_offset -= new_offset%align;
74         if(new_offset!=offset)
75         {
76                 offset = new_offset;
77                 dirty = true;
78                 offset_changed();
79         }
80
81         if(next_in_buffer)
82                 next_in_buffer->update_offset();
83
84         /* Do not resize the buffer here, as more bufferables may be added before
85         the buffer is actually used. */
86 }
87
88 bool Bufferable::resize_buffer() const
89 {
90         if(offset+get_data_size()>=buffer->get_size())
91         {
92                 const Bufferable *last = this;
93                 for(; last->next_in_buffer; last=last->next_in_buffer) ;
94
95                 unsigned total_size = last->offset+last->get_data_size();
96
97                 if(total_size>buffer->get_size())
98                 {
99                         buffer->data(total_size, 0);
100                         return true;
101                 }
102         }
103
104         return false;
105 }
106
107 void Bufferable::update_buffer() const
108 {
109         BindRestore bind(buffer, buffer->get_type());
110         if(resize_buffer())
111         {
112                 /* Resizing the buffer invalidates its contents.  Non-dirty data may
113                 be in use, so reupload it. */
114                 for(const Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer)
115                         if(!b->dirty)
116                                 b->upload_data(0);
117                 for(const Bufferable *b=next_in_buffer; b; b=b->next_in_buffer)
118                         if(!b->dirty)
119                                 b->upload_data(0);
120         }
121
122         upload_data(0);
123         dirty = false;
124 }
125
126 void Bufferable::upload_data(char *target) const
127 {
128         if(target)
129         {
130                 const char *source = reinterpret_cast<const char *>(get_data_pointer());
131                 copy(source, source+get_data_size(), target);
132         }
133         else
134                 buffer->sub_data(offset, get_data_size(), get_data_pointer());
135 }
136
137
138 Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
139         bufferable(b)
140 {
141         buffer_resized = bufferable.resize_buffer();
142         mapped_address = reinterpret_cast<char *>(bufferable.buffer->map(WRITE_ONLY));
143 }
144
145 Bufferable::AsyncUpdater::~AsyncUpdater()
146 {
147         bufferable.buffer->unmap();
148 }
149
150 void Bufferable::AsyncUpdater::upload_data()
151 {
152         bufferable.upload_data(mapped_address+bufferable.offset);
153         // Update all bufferables in the same buffer at once
154         for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer)
155                 if(b->dirty || buffer_resized)
156                 {
157                         b->upload_data(mapped_address+b->offset);
158                         b->dirty = false;
159                 }
160         for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer)
161                 if(b->dirty || buffer_resized)
162                 {
163                         b->upload_data(mapped_address+b->offset);
164                         b->dirty = false;
165                 }
166 }
167
168 } // namespace GL
169 } // namespace Msp