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