1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
use super::error::*;
use super::texture::*;
use crate::action::*;
use gl;
use std::ops::{Deref};
///
/// An OpenGL render target
///
pub struct RenderTarget {
/// The frame buffer for this render target
frame_buffer: gl::types::GLuint,
/// The render buffer for this render target
render_buffer: Option<gl::types::GLuint>,
/// The texture attached to the framebuffer (if we're tracking it)
texture: Option<Texture>,
/// The target type of this render surface
_render_type: RenderTargetType,
/// Set to true if this should drop its frame buffer when done
drop_frame_buffer: bool,
/// The size in pixels
size: (u16, u16)
}
impl RenderTarget {
///
/// Creates a new OpenGL render target
///
pub fn new(width: u16, height: u16, render_type: RenderTargetType) -> RenderTarget {
unsafe {
// Find the currently bound frame buffer (so we can rebind it)
let mut old_frame_buffer = 0;
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut old_frame_buffer);
panic_on_gl_error("Get old framebuffer");
// Create the frame buffer
let mut frame_buffer = 0;
gl::GenFramebuffers(1, &mut frame_buffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, frame_buffer);
panic_on_gl_error("Bind new framebuffer");
// Generate the texture or the render buffer for the render target
let texture;
let render_buffer;
match render_type {
RenderTargetType::Standard | RenderTargetType::StandardForReading => {
// Use a backing texture for the rendering
let mut backing_texture = Texture::new();
backing_texture.create_empty(width, height);
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, *backing_texture, 0);
// This type of render target uses a backing texture
texture = Some(backing_texture);
render_buffer = None;
}
RenderTargetType::Multisampled => {
// Uses a render buffer for the backing layer
let mut backing_renderbuffer = 0;
gl::GenRenderbuffers(1, &mut backing_renderbuffer);
let mut old_renderbuffer = 0;
gl::GetIntegerv(gl::RENDERBUFFER_BINDING, &mut old_renderbuffer);
// Define as a MSAA renderbuffer
gl::BindRenderbuffer(gl::RENDERBUFFER, backing_renderbuffer);
gl::RenderbufferStorageMultisample(gl::RENDERBUFFER, 4, gl::RGBA8, width as gl::types::GLsizei, height as gl::types::GLsizei);
gl::FramebufferRenderbuffer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::RENDERBUFFER, backing_renderbuffer);
gl::BindRenderbuffer(gl::RENDERBUFFER, old_renderbuffer as u32);
// This type of render target uses a render buffer
texture = None;
render_buffer = Some(backing_renderbuffer);
},
RenderTargetType::MultisampledTexture => {
// Use a backing texture for the rendering
let mut backing_texture = Texture::new();
backing_texture.create_empty_multisampled(width, height, 4);
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D_MULTISAMPLE, *backing_texture, 0);
// This type of render target uses a backing texture
texture = Some(backing_texture);
render_buffer = None;
}
RenderTargetType::Monochrome => {
// Use a backing texture for the rendering
let mut backing_texture = Texture::new();
backing_texture.create_monochrome(width, height);
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, *backing_texture, 0);
// This type of render target uses a backing texture
texture = Some(backing_texture);
render_buffer = None;
}
RenderTargetType::MonochromeMultisampledTexture => {
// Use a backing texture for the rendering
let mut backing_texture = Texture::new();
backing_texture.create_monochrome_multisampled(width, height, 4);
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D_MULTISAMPLE, *backing_texture, 0);
// This type of render target uses a backing texture
texture = Some(backing_texture);
render_buffer = None;
}
};
panic_on_gl_error("Create framebuffer");
// Bind back to the original framebuffer
gl::BindFramebuffer(gl::FRAMEBUFFER, old_frame_buffer as _);
// Create the render target
RenderTarget {
frame_buffer: frame_buffer,
texture: texture,
render_buffer: render_buffer,
size: (width, height),
_render_type: render_type,
drop_frame_buffer: true
}
}
}
///
/// Creates a render target from an existing texture
///
pub fn from_texture(texture: &Texture) -> Option<RenderTarget> {
unsafe {
// Clone the texture for later
let texture = texture.clone();
let width = texture.width;
let height = texture.height;
// Find the currently bound frame buffer (so we can rebind it)
let mut old_frame_buffer = 0;
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut old_frame_buffer);
panic_on_gl_error("Get old framebuffer");
// Create the frame buffer
let mut frame_buffer = 0;
gl::GenFramebuffers(1, &mut frame_buffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, frame_buffer);
panic_on_gl_error("Bind new framebuffer");
// We're always using a texture and not a render buffer here
let render_buffer = None;
let render_type = RenderTargetType::Standard;
// Bind the texture to the frame buffer
match texture.texture_target {
gl::TEXTURE_1D => {
gl::FramebufferTexture1D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_1D, *texture, 0);
}
gl::TEXTURE_2D => {
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, *texture, 0);
}
_ => { return None }
}
// Bind back to the original framebuffer
gl::BindFramebuffer(gl::FRAMEBUFFER, old_frame_buffer as _);
// Generate the final render target
Some(RenderTarget {
frame_buffer: frame_buffer,
texture: Some(texture),
render_buffer: render_buffer,
size: (width as _, height as _),
_render_type: render_type,
drop_frame_buffer: true
})
}
}
///
/// Creates a render target that's a reference to the currently active framebuffer
///
/// (This won't be freed when the render target is dropped. It also has no way to know how
/// long the current framebuffer will exist for, so is marked as 'unsafe')
///
pub unsafe fn reference_to_current() -> RenderTarget {
let mut current_frame_buffer = 0;
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut current_frame_buffer);
RenderTarget {
frame_buffer: current_frame_buffer as u32,
texture: None,
render_buffer: None,
drop_frame_buffer: false,
_render_type: RenderTargetType::Standard,
size: (0, 0)
}
}
///
/// Returns the texture associated with this render target
///
pub fn texture(&self) -> Option<Texture> {
self.texture.clone()
}
///
/// Returns the size of this render target (might be 0,0 if we can't read it)
///
pub fn get_size(&self) -> (u16, u16) {
self.size
}
}
impl Drop for RenderTarget {
fn drop(&mut self) {
unsafe {
if self.drop_frame_buffer {
gl::DeleteFramebuffers(1, &self.frame_buffer);
}
if let Some(render_buffer) = self.render_buffer {
gl::DeleteRenderbuffers(1, &render_buffer);
}
}
}
}
///
/// Deref returns the frame buffer ID
///
impl Deref for RenderTarget {
type Target = gl::types::GLuint;
fn deref(&self) -> &gl::types::GLuint {
&self.frame_buffer
}
}