goat3dgfx

annotate src/rtarg.cc @ 13:25bf39105c82

lalal
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 27 Nov 2013 08:08:59 +0200
parents
children 7d6b667821cf
rev   line source
nuclear@0 1 #include <stack>
nuclear@0 2 #include "rtarg.h"
nuclear@0 3 #include "texture.h"
nuclear@0 4 #include "logger.h"
nuclear@0 5
nuclear@0 6 RenderTarget::RenderTarget()
nuclear@0 7 {
nuclear@0 8 width = height = 0;
nuclear@0 9 fbo = 0;
nuclear@0 10 rbuf_zstencil = 0;
nuclear@0 11 color_tex = 0;
nuclear@0 12 tex_face = 0;
nuclear@0 13 tex_targ = 0;
nuclear@0 14 }
nuclear@0 15
nuclear@0 16 RenderTarget::~RenderTarget()
nuclear@0 17 {
nuclear@0 18 cleanup();
nuclear@0 19 }
nuclear@0 20
nuclear@0 21 bool RenderTarget::create(unsigned int fmt)
nuclear@0 22 {
nuclear@0 23 int vp[4];
nuclear@0 24 glGetIntegerv(GL_VIEWPORT, vp);
nuclear@0 25
nuclear@0 26 return create(vp[2] - vp[0], vp[3] - vp[1], fmt);
nuclear@0 27 }
nuclear@0 28
nuclear@0 29 bool RenderTarget::create(int width, int height, unsigned int fmt)
nuclear@0 30 {
nuclear@0 31 debug_log("RenderTarget::create(%d, %d)\n", width, height);
nuclear@0 32 cleanup();
nuclear@0 33
nuclear@0 34 tex_targ = GL_TEXTURE_2D;
nuclear@0 35 this->width = width;
nuclear@0 36 this->height = height;
nuclear@0 37 int tex_width = next_pow2(width);
nuclear@0 38 int tex_height = next_pow2(height);
nuclear@0 39
nuclear@0 40 CHECKGLERR;
nuclear@0 41 color_tex = new Texture2D;
nuclear@0 42 color_tex->create(tex_width, tex_height, fmt);
nuclear@0 43 CHECKGLERR;
nuclear@0 44 tex_face = 0;
nuclear@0 45
nuclear@0 46 glGenFramebuffers(1, &fbo);
nuclear@0 47 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@0 48
nuclear@0 49 glBindTexture(GL_TEXTURE_2D, color_tex->get_id());
nuclear@0 50 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0);
nuclear@0 51 glBindTexture(GL_TEXTURE_2D, 0);
nuclear@0 52
nuclear@0 53 glGenRenderbuffers(1, &rbuf_zstencil);
nuclear@0 54 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 55 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
nuclear@0 56 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 57 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 58
nuclear@0 59 glBindFramebuffer(GL_FRAMEBUFFER, 0);
nuclear@0 60 CHECKGLERR;
nuclear@0 61 return true;
nuclear@0 62 }
nuclear@0 63
nuclear@0 64 bool RenderTarget::create(Texture *tex, int face)
nuclear@0 65 {
nuclear@0 66 debug_log("RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0),
nuclear@0 67 tex->get_size(1), face);
nuclear@0 68
nuclear@0 69 tex_targ = GL_TEXTURE_2D;
nuclear@0 70 if(dynamic_cast<TextureCube*>(tex)) {
nuclear@0 71 if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
nuclear@0 72 tex_targ = face;
nuclear@0 73 } else if(face >= 0 && face < 6) {
nuclear@0 74 tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
nuclear@0 75 } else {
nuclear@0 76 error_log("invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face);
nuclear@0 77 return false;
nuclear@0 78 }
nuclear@0 79 }
nuclear@0 80
nuclear@0 81 cleanup();
nuclear@0 82
nuclear@0 83 width = tex->get_size(0);
nuclear@0 84 height = tex->get_size(1);
nuclear@0 85
nuclear@0 86 color_tex = tex;
nuclear@0 87
nuclear@0 88 glGenFramebuffers(1, &fbo);
nuclear@0 89 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@0 90
nuclear@0 91 glBindTexture(tex_targ, color_tex->get_id());
nuclear@0 92 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
nuclear@0 93 glBindTexture(tex_targ, 0);
nuclear@0 94
nuclear@0 95 glGenRenderbuffers(1, &rbuf_zstencil);
nuclear@0 96 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 97 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
nuclear@0 98 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 99 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 100
nuclear@0 101 glBindFramebuffer(GL_FRAMEBUFFER, 0);
nuclear@0 102 CHECKGLERR;
nuclear@0 103 return true;
nuclear@0 104 }
nuclear@0 105
nuclear@0 106 void RenderTarget::cleanup()
nuclear@0 107 {
nuclear@0 108 delete color_tex;
nuclear@0 109 color_tex = 0;
nuclear@0 110
nuclear@0 111 if(fbo) {
nuclear@0 112 glDeleteFramebuffers(1, &fbo);
nuclear@0 113 }
nuclear@0 114 if(rbuf_zstencil) {
nuclear@0 115 glDeleteRenderbuffers(1, &rbuf_zstencil);
nuclear@0 116 }
nuclear@0 117
nuclear@0 118 fbo = rbuf_zstencil = 0;
nuclear@0 119 width = height = 0;
nuclear@0 120 tex_face = 0;
nuclear@0 121 tex_targ = 0;
nuclear@0 122 }
nuclear@0 123
nuclear@0 124 bool RenderTarget::resize(int width, int height)
nuclear@0 125 {
nuclear@0 126 this->width = width;
nuclear@0 127 this->height = height;
nuclear@0 128 int tex_width = next_pow2(width);
nuclear@0 129 int tex_height = next_pow2(height);
nuclear@0 130
nuclear@0 131 debug_log("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height);
nuclear@0 132
nuclear@0 133 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@0 134
nuclear@0 135 color_tex->create(tex_width, tex_height, color_tex->get_format());
nuclear@0 136 color_tex->bind();
nuclear@0 137 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
nuclear@0 138 glBindTexture(tex_targ, 0);
nuclear@0 139
nuclear@0 140 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
nuclear@0 141 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
nuclear@0 142
nuclear@0 143 glBindFramebuffer(GL_FRAMEBUFFER, 0);
nuclear@0 144 return true;
nuclear@0 145 }
nuclear@0 146
nuclear@0 147 int RenderTarget::get_width() const
nuclear@0 148 {
nuclear@0 149 return width;
nuclear@0 150 }
nuclear@0 151
nuclear@0 152 int RenderTarget::get_height() const
nuclear@0 153 {
nuclear@0 154 return height;
nuclear@0 155 }
nuclear@0 156
nuclear@0 157 Texture *RenderTarget::get_texture() const
nuclear@0 158 {
nuclear@0 159 return color_tex;
nuclear@0 160 }
nuclear@0 161
nuclear@0 162 Matrix4x4 RenderTarget::get_texture_matrix() const
nuclear@0 163 {
nuclear@0 164 float sx = (float)width / (float)color_tex->get_size(0);
nuclear@0 165 float sy = (float)height / (float)color_tex->get_size(1);
nuclear@0 166
nuclear@0 167 // counting on RVO to optimize this away
nuclear@0 168 return Matrix4x4(sx, 0, 0, 0,
nuclear@0 169 0, sy, 0, 0,
nuclear@0 170 0, 0, 1, 0,
nuclear@0 171 0, 0, 0, 1);
nuclear@0 172 }
nuclear@0 173
nuclear@0 174
nuclear@0 175 static const char *fbstname[] = {
nuclear@0 176 "GL_FRAMEBUFFER_COMPLETE",
nuclear@0 177 "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
nuclear@0 178 "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
nuclear@0 179 "no such fbo error",
nuclear@0 180 "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
nuclear@0 181 "GL_FRAMEBUFFER_INCOMPLETE_FORMATS",
nuclear@0 182 "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
nuclear@0 183 "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
nuclear@0 184 "GL_FRAMEBUFFER_UNSUPPORTED"
nuclear@0 185 };
nuclear@0 186
nuclear@0 187 bool RenderTarget::check() const
nuclear@0 188 {
nuclear@0 189 bool res = true;
nuclear@0 190
nuclear@0 191 #ifndef GL_ES_VERSION_2_0
nuclear@0 192 int prev_fb = 0;
nuclear@0 193 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb);
nuclear@0 194 #endif
nuclear@0 195
nuclear@0 196 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@0 197
nuclear@0 198 int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
nuclear@0 199 if(status != GL_FRAMEBUFFER_COMPLETE) {
nuclear@0 200 error_log("RenderTarget::check: incomplete FBO %u: %s\n", fbo,
nuclear@0 201 fbstname[status - GL_FRAMEBUFFER_COMPLETE]);
nuclear@0 202 res = false;
nuclear@0 203 goto end;
nuclear@0 204 }
nuclear@0 205
nuclear@0 206 end:
nuclear@0 207 #ifndef GL_ES_VERSION_2_0
nuclear@0 208 glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
nuclear@0 209 #endif
nuclear@0 210 return res;
nuclear@0 211 }
nuclear@0 212
nuclear@0 213 void RenderTarget::bind() const
nuclear@0 214 {
nuclear@0 215 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@0 216
nuclear@0 217 glViewport(0, 0, width, height);
nuclear@0 218 }
nuclear@0 219
nuclear@0 220 struct Viewport { int vp[4]; };
nuclear@0 221 static std::stack<Viewport> vpstack;
nuclear@0 222
nuclear@0 223 void set_render_target(const RenderTarget *rtarg)
nuclear@0 224 {
nuclear@0 225 Viewport vp;
nuclear@0 226
nuclear@0 227 if(rtarg) {
nuclear@0 228 glGetIntegerv(GL_VIEWPORT, vp.vp);
nuclear@0 229 vpstack.push(vp);
nuclear@0 230
nuclear@0 231 rtarg->bind();
nuclear@0 232 } else {
nuclear@0 233 #ifdef GL_ES_VERSION_2_0
nuclear@0 234 extern unsigned int default_fbo;
nuclear@0 235 glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
nuclear@0 236 #else
nuclear@0 237 glBindFramebuffer(GL_FRAMEBUFFER, 0);
nuclear@0 238 #endif
nuclear@0 239
nuclear@0 240 if(vpstack.empty()) {
nuclear@0 241 return;
nuclear@0 242 }
nuclear@0 243 vp = vpstack.top();
nuclear@0 244 vpstack.pop();
nuclear@0 245 glViewport(vp.vp[0], vp.vp[1], vp.vp[2], vp.vp[3]);
nuclear@0 246 }
nuclear@0 247 }
nuclear@0 248
nuclear@0 249 int next_pow2(int x)
nuclear@0 250 {
nuclear@0 251 x--;
nuclear@0 252 x = (x >> 1) | x;
nuclear@0 253 x = (x >> 2) | x;
nuclear@0 254 x = (x >> 4) | x;
nuclear@0 255 x = (x >> 8) | x;
nuclear@0 256 x = (x >> 16) | x;
nuclear@0 257 return x + 1;
nuclear@0 258 }
nuclear@0 259