blob: 86c6269715643e0413e783a54f2128366ea0e8f6 [file] [log] [blame]
shannon.woods@transgaming.combdf2d802013-02-28 23:16:20 +00001#include "precompiled.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00002//
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +00003// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00004// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7
8// Framebuffer.cpp: Implements the gl::Framebuffer class. Implements GL framebuffer
9// objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
10
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000011#include "libGLESv2/Framebuffer.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000012
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000013#include "libGLESv2/main.h"
shannonwoods@chromium.orga2ecfcc2013-05-30 00:11:59 +000014#include "common/utilities.h"
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +000015#include "libGLESv2/formatutils.h"
shannon.woods@transgaming.com486d9e92013-02-28 23:15:41 +000016#include "libGLESv2/Texture.h"
17#include "libGLESv2/Context.h"
18#include "libGLESv2/renderer/Renderer.h"
19#include "libGLESv2/Renderbuffer.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000020
21namespace gl
22{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000023
daniel@transgaming.com16418b12012-11-28 19:32:22 +000024Framebuffer::Framebuffer(rx::Renderer *renderer)
25 : mRenderer(renderer)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000026{
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +000027 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
28 {
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +000029 mDrawBufferStates[colorAttachment] = GL_NONE;
30 }
31 mDrawBufferStates[0] = GL_COLOR_ATTACHMENT0_EXT;
32 mReadBufferState = GL_COLOR_ATTACHMENT0_EXT;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000033}
34
35Framebuffer::~Framebuffer()
36{
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +000037 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
38 {
Geoff Langc90d73a2013-07-22 16:39:23 -040039 mColorbuffers[colorAttachment].set(NULL, GL_NONE, 0, 0);
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +000040 }
Geoff Langc90d73a2013-07-22 16:39:23 -040041 mDepthbuffer.set(NULL, GL_NONE, 0, 0);
42 mStencilbuffer.set(NULL, GL_NONE, 0, 0);
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000043}
44
Geoff Lang309c92a2013-07-25 16:23:19 -040045Renderbuffer *Framebuffer::lookupRenderbuffer(GLenum type, GLuint handle, GLint level, GLint layer) const
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000046{
47 gl::Context *context = gl::getContext();
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000048
Geoff Lang309c92a2013-07-25 16:23:19 -040049 switch (type)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000050 {
Geoff Lang309c92a2013-07-25 16:23:19 -040051 case GL_NONE:
52 return NULL;
53
54 case GL_RENDERBUFFER:
55 return context->getRenderbuffer(handle);
56
57 case GL_TEXTURE_2D:
58 {
59 Texture *texture = context->getTexture(handle);
60 if (texture && texture->getTarget() == GL_TEXTURE_2D)
61 {
Geoff Lang8040f572013-07-25 16:49:54 -040062 return static_cast<Texture2D*>(texture)->getRenderbuffer(level);
Geoff Lang309c92a2013-07-25 16:23:19 -040063 }
64 else
65 {
66 return NULL;
67 }
68 }
69
70 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
71 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
72 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
73 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
74 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
75 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
76 {
77 Texture *texture = context->getTexture(handle);
78 if (texture && texture->getTarget() == GL_TEXTURE_CUBE_MAP)
79 {
Geoff Lang8040f572013-07-25 16:49:54 -040080 return static_cast<TextureCubeMap*>(texture)->getRenderbuffer(type, level);
Geoff Lang309c92a2013-07-25 16:23:19 -040081 }
82 else
83 {
84 return NULL;
85 }
86 }
87
88 case GL_TEXTURE_3D:
89 {
90 Texture *texture = context->getTexture(handle);
91 if (texture && texture->getTarget() == GL_TEXTURE_3D)
92 {
Geoff Lang8040f572013-07-25 16:49:54 -040093 return static_cast<Texture3D*>(texture)->getRenderbuffer(level, layer);
Geoff Lang309c92a2013-07-25 16:23:19 -040094 }
95 else
96 {
97 return NULL;
98 }
99 }
100
101 case GL_TEXTURE_2D_ARRAY:
102 {
103 Texture *texture = context->getTexture(handle);
104 if (texture && texture->getTarget() == GL_TEXTURE_2D_ARRAY)
105 {
Geoff Lang8040f572013-07-25 16:49:54 -0400106 return static_cast<Texture2DArray*>(texture)->getRenderbuffer(level, layer);
Geoff Lang309c92a2013-07-25 16:23:19 -0400107 }
108 else
109 {
110 return NULL;
111 }
112 }
113
114 default:
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000115 UNREACHABLE();
Geoff Lang309c92a2013-07-25 16:23:19 -0400116 return NULL;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000117 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000118}
119
Geoff Lang309c92a2013-07-25 16:23:19 -0400120void Framebuffer::setColorbuffer(unsigned int colorAttachment, GLenum type, GLuint colorbuffer, GLint level, GLint layer)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000121{
122 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
Geoff Lang309c92a2013-07-25 16:23:19 -0400123 Renderbuffer *renderBuffer = lookupRenderbuffer(type, colorbuffer, level, layer);
Geoff Langc90d73a2013-07-22 16:39:23 -0400124 if (renderBuffer)
125 {
Geoff Lang309c92a2013-07-25 16:23:19 -0400126 mColorbuffers[colorAttachment].set(renderBuffer, type, level, layer);
Geoff Langc90d73a2013-07-22 16:39:23 -0400127 }
128 else
129 {
130 mColorbuffers[colorAttachment].set(NULL, GL_NONE, 0, 0);
131 }
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000132}
133
Geoff Lang309c92a2013-07-25 16:23:19 -0400134void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer, GLint level, GLint layer)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000135{
Geoff Lang309c92a2013-07-25 16:23:19 -0400136 Renderbuffer *renderBuffer = lookupRenderbuffer(type, depthbuffer, level, layer);
Geoff Langc90d73a2013-07-22 16:39:23 -0400137 if (renderBuffer)
138 {
Geoff Lang309c92a2013-07-25 16:23:19 -0400139 mDepthbuffer.set(renderBuffer, type, level, layer);
Geoff Langc90d73a2013-07-22 16:39:23 -0400140 }
141 else
142 {
143 mDepthbuffer.set(NULL, GL_NONE, 0, 0);
144 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000145}
146
Geoff Lang309c92a2013-07-25 16:23:19 -0400147void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer, GLint level, GLint layer)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148{
Geoff Lang309c92a2013-07-25 16:23:19 -0400149 Renderbuffer *renderBuffer = lookupRenderbuffer(type, stencilbuffer, level, layer);
Geoff Langc90d73a2013-07-22 16:39:23 -0400150 if (renderBuffer)
151 {
Geoff Lang309c92a2013-07-25 16:23:19 -0400152 mStencilbuffer.set(renderBuffer, type, level, layer);
Geoff Langc90d73a2013-07-22 16:39:23 -0400153 }
154 else
155 {
156 mStencilbuffer.set(NULL, GL_NONE, 0, 0);
157 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000158}
159
Geoff Lang309c92a2013-07-25 16:23:19 -0400160void Framebuffer::setDepthStencilBuffer(GLenum type, GLuint depthStencilBuffer, GLint level, GLint layer)
Geoff Lang55ba29c2013-07-11 16:57:53 -0400161{
Geoff Lang309c92a2013-07-25 16:23:19 -0400162 Renderbuffer *renderBuffer = lookupRenderbuffer(type, depthStencilBuffer, level, layer);
Geoff Lang55ba29c2013-07-11 16:57:53 -0400163 if (renderBuffer && renderBuffer->getDepthSize() > 0 && renderBuffer->getStencilSize() > 0)
164 {
Geoff Lang309c92a2013-07-25 16:23:19 -0400165 mDepthbuffer.set(renderBuffer, type, level, layer);
166 mStencilbuffer.set(renderBuffer, type, level, layer);
Geoff Lang55ba29c2013-07-11 16:57:53 -0400167 }
168 else
169 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400170 mDepthbuffer.set(NULL, GL_NONE, 0, 0);
171 mStencilbuffer.set(NULL, GL_NONE, 0, 0);
Geoff Lang55ba29c2013-07-11 16:57:53 -0400172 }
173}
174
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000175void Framebuffer::detachTexture(GLuint texture)
176{
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000177 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000178 {
Geoff Lang309c92a2013-07-25 16:23:19 -0400179 if (mColorbuffers[colorAttachment].id() == texture &&
Geoff Lang0fe19492013-07-25 17:04:31 -0400180 IsInternalTextureTarget(mColorbuffers[colorAttachment].type(), mRenderer->getCurrentClientVersion()))
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000181 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400182 mColorbuffers[colorAttachment].set(NULL, GL_NONE, 0, 0);
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000183 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000184 }
daniel@transgaming.comfbc09532010-04-26 15:33:41 +0000185
Geoff Lang0fe19492013-07-25 17:04:31 -0400186 if (mDepthbuffer.id() == texture && IsInternalTextureTarget(mDepthbuffer.type(), mRenderer->getCurrentClientVersion()))
daniel@transgaming.comfbc09532010-04-26 15:33:41 +0000187 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400188 mDepthbuffer.set(NULL, GL_NONE, 0, 0);
daniel@transgaming.comfbc09532010-04-26 15:33:41 +0000189 }
190
Geoff Lang0fe19492013-07-25 17:04:31 -0400191 if (mStencilbuffer.id() == texture && IsInternalTextureTarget(mStencilbuffer.type(), mRenderer->getCurrentClientVersion()))
daniel@transgaming.comfbc09532010-04-26 15:33:41 +0000192 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400193 mStencilbuffer.set(NULL, GL_NONE, 0, 0);
daniel@transgaming.comfbc09532010-04-26 15:33:41 +0000194 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000195}
196
197void Framebuffer::detachRenderbuffer(GLuint renderbuffer)
198{
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000199 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000200 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400201 if (mColorbuffers[colorAttachment].id() == renderbuffer && mColorbuffers[colorAttachment].type() == GL_RENDERBUFFER)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000202 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400203 mColorbuffers[colorAttachment].set(NULL, GL_NONE, 0, 0);
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000204 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000205 }
206
Geoff Langc90d73a2013-07-22 16:39:23 -0400207 if (mDepthbuffer.id() == renderbuffer && mDepthbuffer.type() == GL_RENDERBUFFER)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000208 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400209 mDepthbuffer.set(NULL, GL_NONE, 0, 0);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000210 }
211
Geoff Langc90d73a2013-07-22 16:39:23 -0400212 if (mStencilbuffer.id() == renderbuffer && mStencilbuffer.type() == GL_RENDERBUFFER)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000213 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400214 mStencilbuffer.set(NULL, GL_NONE, 0, 0);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000215 }
216}
217
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000218unsigned int Framebuffer::getRenderTargetSerial(unsigned int colorAttachment) const
daniel@transgaming.com092bd482010-05-12 03:39:36 +0000219{
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000220 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
221
Geoff Langc90d73a2013-07-22 16:39:23 -0400222 Renderbuffer *colorbuffer = mColorbuffers[colorAttachment].get();
daniel@transgaming.com092bd482010-05-12 03:39:36 +0000223
224 if (colorbuffer)
225 {
226 return colorbuffer->getSerial();
227 }
228
229 return 0;
230}
231
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000232unsigned int Framebuffer::getDepthbufferSerial() const
daniel@transgaming.com339ae702010-05-12 03:40:20 +0000233{
Geoff Langc90d73a2013-07-22 16:39:23 -0400234 Renderbuffer *depthbuffer = mDepthbuffer.get();
daniel@transgaming.com339ae702010-05-12 03:40:20 +0000235
236 if (depthbuffer)
237 {
238 return depthbuffer->getSerial();
239 }
240
241 return 0;
242}
243
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000244unsigned int Framebuffer::getStencilbufferSerial() const
daniel@transgaming.com4cbc5902010-08-24 19:20:26 +0000245{
Geoff Langc90d73a2013-07-22 16:39:23 -0400246 Renderbuffer *stencilbuffer = mStencilbuffer.get();
daniel@transgaming.com4cbc5902010-08-24 19:20:26 +0000247
248 if (stencilbuffer)
249 {
250 return stencilbuffer->getSerial();
251 }
252
253 return 0;
254}
255
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000256Renderbuffer *Framebuffer::getColorbuffer(unsigned int colorAttachment) const
257{
258 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
Geoff Langc90d73a2013-07-22 16:39:23 -0400259 return mColorbuffers[colorAttachment].get();
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000260}
261
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000262Renderbuffer *Framebuffer::getDepthbuffer() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000263{
Geoff Langc90d73a2013-07-22 16:39:23 -0400264 return mDepthbuffer.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000265}
266
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000267Renderbuffer *Framebuffer::getStencilbuffer() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000268{
Geoff Langc90d73a2013-07-22 16:39:23 -0400269 return mStencilbuffer.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000270}
271
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000272Renderbuffer *Framebuffer::getDepthOrStencilbuffer() const
daniel@transgaming.comd2b47022012-11-28 19:40:10 +0000273{
Geoff Langc90d73a2013-07-22 16:39:23 -0400274 Renderbuffer *depthstencilbuffer = mDepthbuffer.get();
daniel@transgaming.comd2b47022012-11-28 19:40:10 +0000275
276 if (!depthstencilbuffer)
277 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400278 depthstencilbuffer = mStencilbuffer.get();
daniel@transgaming.comd2b47022012-11-28 19:40:10 +0000279 }
280
281 return depthstencilbuffer;
282}
283
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000284Renderbuffer *Framebuffer::getReadColorbuffer() const
285{
286 // Will require more logic if glReadBuffers is supported
Geoff Langc90d73a2013-07-22 16:39:23 -0400287 return mColorbuffers[0].get();
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000288}
289
shannon.woods%transgaming.com@gtempaccount.comf6863e02013-04-13 03:34:00 +0000290GLenum Framebuffer::getReadColorbufferType() const
291{
292 // Will require more logic if glReadBuffers is supported
Geoff Langc90d73a2013-07-22 16:39:23 -0400293 return mColorbuffers[0].type();
shannon.woods%transgaming.com@gtempaccount.comf6863e02013-04-13 03:34:00 +0000294}
295
shannon.woods%transgaming.com@gtempaccount.com882434c2013-04-13 03:34:14 +0000296Renderbuffer *Framebuffer::getFirstColorbuffer() const
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000297{
298 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
299 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400300 if (mColorbuffers[colorAttachment].type() != GL_NONE)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000301 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400302 return mColorbuffers[colorAttachment].get();
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000303 }
304 }
305
306 return NULL;
307}
308
309GLenum Framebuffer::getColorbufferType(unsigned int colorAttachment) const
310{
311 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
Geoff Langc90d73a2013-07-22 16:39:23 -0400312 return mColorbuffers[colorAttachment].type();
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000313}
314
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000315GLenum Framebuffer::getDepthbufferType() const
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000316{
Geoff Langc90d73a2013-07-22 16:39:23 -0400317 return mDepthbuffer.type();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000318}
319
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000320GLenum Framebuffer::getStencilbufferType() const
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000321{
Geoff Langc90d73a2013-07-22 16:39:23 -0400322 return mStencilbuffer.type();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000323}
324
Geoff Lang55ba29c2013-07-11 16:57:53 -0400325GLenum Framebuffer::getDepthStencilbufferType() const
326{
Geoff Langc90d73a2013-07-22 16:39:23 -0400327 return (mDepthbuffer.id() == mStencilbuffer.id()) ? mDepthbuffer.type() : GL_NONE;
Geoff Lang55ba29c2013-07-11 16:57:53 -0400328}
329
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000330GLuint Framebuffer::getColorbufferHandle(unsigned int colorAttachment) const
331{
332 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
Geoff Langc90d73a2013-07-22 16:39:23 -0400333 return mColorbuffers[colorAttachment].id();
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000334}
335
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000336GLuint Framebuffer::getDepthbufferHandle() const
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000337{
Geoff Langc90d73a2013-07-22 16:39:23 -0400338 return mDepthbuffer.id();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000339}
340
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000341GLuint Framebuffer::getStencilbufferHandle() const
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000342{
Geoff Langc90d73a2013-07-22 16:39:23 -0400343 return mStencilbuffer.id();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000344}
345
Geoff Lang55ba29c2013-07-11 16:57:53 -0400346GLenum Framebuffer::getDepthStencilbufferHandle() const
347{
Geoff Langc90d73a2013-07-22 16:39:23 -0400348 return (mDepthbuffer.id() == mStencilbuffer.id()) ? mDepthbuffer.id() : 0;
349}
350
351GLenum Framebuffer::getColorbufferMipLevel(unsigned int colorAttachment) const
352{
353 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
354 return mColorbuffers[colorAttachment].mipLevel();
355}
356
357GLenum Framebuffer::getDepthbufferMipLevel() const
358{
359 return mDepthbuffer.mipLevel();
360}
361
362GLenum Framebuffer::getStencilbufferMipLevel() const
363{
364 return mStencilbuffer.mipLevel();
365}
366
367GLenum Framebuffer::getDepthStencilbufferMipLevel() const
368{
369 return (mDepthbuffer.id() == mStencilbuffer.id()) ? mDepthbuffer.mipLevel() : 0;
370}
371
372GLenum Framebuffer::getColorbufferLayer(unsigned int colorAttachment) const
373{
374 ASSERT(colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS);
375 return mColorbuffers[colorAttachment].layer();
376}
377
378GLenum Framebuffer::getDepthbufferLayer() const
379{
380 return mDepthbuffer.layer();
381}
382
383GLenum Framebuffer::getStencilbufferLayer() const
384{
385 return mStencilbuffer.layer();
386}
387
388GLenum Framebuffer::getDepthStencilbufferLayer() const
389{
390 return (mDepthbuffer.id() == mStencilbuffer.id()) ? mDepthbuffer.layer() : 0;
Geoff Lang55ba29c2013-07-11 16:57:53 -0400391}
392
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000393GLenum Framebuffer::getDrawBufferState(unsigned int colorAttachment) const
394{
395 return mDrawBufferStates[colorAttachment];
396}
397
398void Framebuffer::setDrawBufferState(unsigned int colorAttachment, GLenum drawBuffer)
399{
400 mDrawBufferStates[colorAttachment] = drawBuffer;
401}
402
shannon.woods%transgaming.com@gtempaccount.comdae24092013-04-13 03:31:31 +0000403bool Framebuffer::isEnabledColorAttachment(unsigned int colorAttachment) const
404{
Geoff Langc90d73a2013-07-22 16:39:23 -0400405 return (mColorbuffers[colorAttachment].type() != GL_NONE && mDrawBufferStates[colorAttachment] != GL_NONE);
shannon.woods%transgaming.com@gtempaccount.comdae24092013-04-13 03:31:31 +0000406}
407
408bool Framebuffer::hasEnabledColorAttachment() const
409{
410 for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
411 {
412 if (isEnabledColorAttachment(colorAttachment))
413 {
414 return true;
415 }
416 }
417
418 return false;
419}
420
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000421bool Framebuffer::hasStencil() const
daniel@transgaming.coma27ff1e2010-08-24 19:20:11 +0000422{
Geoff Langc90d73a2013-07-22 16:39:23 -0400423 if (mStencilbuffer.type() != GL_NONE)
daniel@transgaming.coma27ff1e2010-08-24 19:20:11 +0000424 {
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000425 const Renderbuffer *stencilbufferObject = getStencilbuffer();
daniel@transgaming.coma27ff1e2010-08-24 19:20:11 +0000426
427 if (stencilbufferObject)
428 {
429 return stencilbufferObject->getStencilSize() > 0;
430 }
431 }
432
433 return false;
434}
435
shannonwoods@chromium.org24ac8502013-05-30 00:01:37 +0000436bool Framebuffer::usingExtendedDrawBuffers() const
437{
438 for (unsigned int colorAttachment = 1; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
439 {
440 if (isEnabledColorAttachment(colorAttachment))
441 {
442 return true;
443 }
444 }
445
446 return false;
447}
448
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000449GLenum Framebuffer::completeness() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000450{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000451 int width = 0;
452 int height = 0;
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000453 unsigned int colorbufferSize = 0;
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000454 int samples = -1;
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000455 bool missingAttachment = true;
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000456 GLuint clientVersion = mRenderer->getCurrentClientVersion();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000457
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000458 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000459 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400460 if (mColorbuffers[colorAttachment].type() != GL_NONE)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000461 {
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000462 const Renderbuffer *colorbuffer = getColorbuffer(colorAttachment);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000463
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000464 if (!colorbuffer)
daniel@transgaming.comedc19182010-10-15 17:57:55 +0000465 {
466 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
467 }
daniel@transgaming.comd885df02012-05-31 01:14:36 +0000468
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000469 if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0)
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000470 {
471 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
472 }
daniel@transgaming.com01868132010-08-24 19:21:17 +0000473
Geoff Langc90d73a2013-07-22 16:39:23 -0400474 if (mColorbuffers[colorAttachment].type() == GL_RENDERBUFFER)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000475 {
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000476 if (!gl::IsColorRenderingSupported(colorbuffer->getInternalFormat(), mRenderer))
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000477 {
478 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
479 }
480 }
Geoff Lang0fe19492013-07-25 17:04:31 -0400481 else if (IsInternalTextureTarget(mColorbuffers[colorAttachment].type(), mRenderer->getCurrentClientVersion()))
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000482 {
483 GLint internalformat = colorbuffer->getInternalFormat();
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000484
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000485 if (!gl::IsColorRenderingSupported(internalformat, mRenderer))
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000486 {
487 return GL_FRAMEBUFFER_UNSUPPORTED;
488 }
489
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000490 if (gl::GetDepthBits(internalformat, clientVersion) > 0 ||
491 gl::GetStencilBits(internalformat, clientVersion) > 0)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000492 {
493 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
494 }
495 }
496 else
497 {
498 UNREACHABLE();
499 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
500 }
501
502 if (!missingAttachment)
503 {
504 // all color attachments must have the same width and height
505 if (colorbuffer->getWidth() != width || colorbuffer->getHeight() != height)
506 {
507 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
508 }
509
510 // APPLE_framebuffer_multisample, which EXT_draw_buffers refers to, requires that
511 // all color attachments have the same number of samples for the FBO to be complete.
512 if (colorbuffer->getSamples() != samples)
513 {
514 return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT;
515 }
516
shannon.woods%transgaming.com@gtempaccount.comc3471522013-04-13 03:34:52 +0000517 // in GLES 2.0, all color attachments attachments must have the same number of bitplanes
518 // in GLES 3.0, there is no such restriction
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000519 if (clientVersion < 3)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000520 {
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000521 if (gl::GetPixelBytes(colorbuffer->getInternalFormat(), clientVersion) != colorbufferSize)
shannon.woods%transgaming.com@gtempaccount.comc3471522013-04-13 03:34:52 +0000522 {
523 return GL_FRAMEBUFFER_UNSUPPORTED;
524 }
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000525 }
526
527 // D3D11 does not allow for overlapping RenderTargetViews, so ensure uniqueness
528 for (unsigned int previousColorAttachment = 0; previousColorAttachment < colorAttachment; previousColorAttachment++)
529 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400530 if (mColorbuffers[colorAttachment].get() == mColorbuffers[previousColorAttachment].get())
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000531 {
532 return GL_FRAMEBUFFER_UNSUPPORTED;
533 }
534 }
535 }
536 else
537 {
538 width = colorbuffer->getWidth();
539 height = colorbuffer->getHeight();
540 samples = colorbuffer->getSamples();
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000541 colorbufferSize = gl::GetPixelBytes(colorbuffer->getInternalFormat(), clientVersion);
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000542 missingAttachment = false;
543 }
544 }
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000545 }
546
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000547 const Renderbuffer *depthbuffer = NULL;
548 const Renderbuffer *stencilbuffer = NULL;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000549
Geoff Langc90d73a2013-07-22 16:39:23 -0400550 if (mDepthbuffer.type() != GL_NONE)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000551 {
552 depthbuffer = getDepthbuffer();
553
554 if (!depthbuffer)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000555 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000556 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000557 }
558
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000559 if (depthbuffer->getWidth() == 0 || depthbuffer->getHeight() == 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000560 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000561 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
562 }
563
Geoff Langc90d73a2013-07-22 16:39:23 -0400564 if (mDepthbuffer.type() == GL_RENDERBUFFER)
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000565 {
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000566 if (!gl::IsDepthRenderingSupported(depthbuffer->getInternalFormat(), mRenderer))
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000567 {
568 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
569 }
570 }
Geoff Lang0fe19492013-07-25 17:04:31 -0400571 else if (IsInternalTextureTarget(mDepthbuffer.type(), mRenderer->getCurrentClientVersion()))
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000572 {
daniel@transgaming.com8e91d252012-10-17 18:22:44 +0000573 GLint internalformat = depthbuffer->getInternalFormat();
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000574
575 // depth texture attachments require OES/ANGLE_depth_texture
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000576 if (!mRenderer->getDepthTextureSupport())
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000577 {
578 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
579 }
580
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000581 if (gl::GetDepthBits(internalformat, clientVersion) == 0)
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000582 {
583 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
584 }
585 }
586 else
587 {
588 UNREACHABLE();
589 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
590 }
591
592 if (missingAttachment)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000593 {
594 width = depthbuffer->getWidth();
595 height = depthbuffer->getHeight();
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000596 samples = depthbuffer->getSamples();
597 missingAttachment = false;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000598 }
599 else if (width != depthbuffer->getWidth() || height != depthbuffer->getHeight())
600 {
601 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
602 }
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000603 else if (samples != depthbuffer->getSamples())
604 {
605 return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
606 }
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000607 }
608
Geoff Langc90d73a2013-07-22 16:39:23 -0400609 if (mStencilbuffer.type() != GL_NONE)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000610 {
611 stencilbuffer = getStencilbuffer();
612
613 if (!stencilbuffer)
614 {
615 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
616 }
617
618 if (stencilbuffer->getWidth() == 0 || stencilbuffer->getHeight() == 0)
619 {
620 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
621 }
622
Geoff Langc90d73a2013-07-22 16:39:23 -0400623 if (mStencilbuffer.type() == GL_RENDERBUFFER)
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000624 {
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000625 if (!gl::IsStencilRenderingSupported(stencilbuffer->getInternalFormat(), mRenderer))
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000626 {
627 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
628 }
629 }
Geoff Lang0fe19492013-07-25 17:04:31 -0400630 else if (IsInternalTextureTarget(mStencilbuffer.type(), mRenderer->getCurrentClientVersion()))
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000631 {
daniel@transgaming.com8e91d252012-10-17 18:22:44 +0000632 GLint internalformat = stencilbuffer->getInternalFormat();
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000633
634 // texture stencil attachments come along as part
635 // of OES_packed_depth_stencil + OES/ANGLE_depth_texture
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000636 if (!mRenderer->getDepthTextureSupport())
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000637 {
638 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
639 }
640
shannonwoods@chromium.orgf6fb9592013-05-30 00:09:40 +0000641 if (gl::GetStencilBits(internalformat, clientVersion) == 0)
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000642 {
643 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
644 }
645 }
646 else
647 {
648 UNREACHABLE();
649 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
650 }
651
652 if (missingAttachment)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000653 {
654 width = stencilbuffer->getWidth();
655 height = stencilbuffer->getHeight();
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000656 samples = stencilbuffer->getSamples();
657 missingAttachment = false;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000658 }
659 else if (width != stencilbuffer->getWidth() || height != stencilbuffer->getHeight())
660 {
661 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
662 }
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000663 else if (samples != stencilbuffer->getSamples())
664 {
665 return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
666 }
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000667 }
668
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000669 // if we have both a depth and stencil buffer, they must refer to the same object
670 // since we only support packed_depth_stencil and not separate depth and stencil
671 if (depthbuffer && stencilbuffer && (depthbuffer != stencilbuffer))
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000672 {
daniel@transgaming.com6b7c84c2012-05-31 01:14:39 +0000673 return GL_FRAMEBUFFER_UNSUPPORTED;
674 }
675
676 // we need to have at least one attachment to be complete
677 if (missingAttachment)
678 {
679 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
daniel@transgaming.comcdacc8e2010-07-28 19:20:50 +0000680 }
681
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000682 return GL_FRAMEBUFFER_COMPLETE;
683}
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000684
daniel@transgaming.com16418b12012-11-28 19:32:22 +0000685DefaultFramebuffer::DefaultFramebuffer(rx::Renderer *renderer, Colorbuffer *colorbuffer, DepthStencilbuffer *depthStencil)
686 : Framebuffer(renderer)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000687{
Geoff Langc90d73a2013-07-22 16:39:23 -0400688 mColorbuffers[0].set(new Renderbuffer(mRenderer, 0, colorbuffer), GL_RENDERBUFFER, 0, 0);
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000689
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000690 Renderbuffer *depthStencilRenderbuffer = new Renderbuffer(mRenderer, 0, depthStencil);
Geoff Langc90d73a2013-07-22 16:39:23 -0400691 mDepthbuffer.set(depthStencilRenderbuffer, (depthStencilRenderbuffer->getDepthSize() != 0) ? GL_RENDERBUFFER : GL_NONE, 0, 0);
692 mStencilbuffer.set(depthStencilRenderbuffer, (depthStencilRenderbuffer->getStencilSize() != 0) ? GL_RENDERBUFFER : GL_NONE, 0, 0);
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000693
694 mDrawBufferStates[0] = GL_BACK;
695 mReadBufferState = GL_BACK;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000696}
697
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000698int Framebuffer::getSamples() const
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000699{
700 if (completeness() == GL_FRAMEBUFFER_COMPLETE)
701 {
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000702 // for a complete framebuffer, all attachments must have the same sample count
703 // in this case return the first nonzero sample size
704 for (unsigned int colorAttachment = 0; colorAttachment < IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
705 {
Geoff Langc90d73a2013-07-22 16:39:23 -0400706 if (mColorbuffers[colorAttachment].type() != GL_NONE)
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000707 {
708 return getColorbuffer(colorAttachment)->getSamples();
709 }
710 }
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000711 }
shannon.woods%transgaming.com@gtempaccount.comf30ccc22013-04-13 03:28:36 +0000712
713 return 0;
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000714}
715
shannon.woods%transgaming.com@gtempaccount.com3b57b4f2013-04-13 03:28:29 +0000716GLenum DefaultFramebuffer::completeness() const
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000717{
shannon.woods@transgaming.com3e3da582013-02-28 23:09:03 +0000718 // The default framebuffer *must* always be complete, though it may not be
719 // subject to the same rules as application FBOs. ie, it could have 0x0 size.
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000720 return GL_FRAMEBUFFER_COMPLETE;
721}
722
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000723}