1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 28 #if ENABLE(WEBGL) 29 30 #include "WebGLFramebuffer.h" 31 32 #include "WebGLRenderingContext.h" 33 34 namespace WebCore { 35 36 namespace { 37 38 // This function is only for depth/stencil/depth_stencil attachment. 39 // Currently we assume these attachments are all renderbuffers. 40 GC3Denum getInternalFormat(WebGLObject* buffer) 41 { 42 ASSERT(buffer && buffer->isRenderbuffer()); 43 return (reinterpret_cast<WebGLRenderbuffer*>(buffer))->getInternalFormat(); 44 } 45 46 bool isUninitialized(WebGLObject* attachedObject) 47 { 48 if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer() 49 && !(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isInitialized()) 50 return true; 51 return false; 52 } 53 54 void setInitialized(WebGLObject* attachedObject) 55 { 56 if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) 57 (reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->setInitialized(); 58 } 59 60 bool isValid(WebGLObject* attachedObject) 61 { 62 if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) { 63 if (!(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isValid()) 64 return false; 65 } 66 return true; 67 } 68 69 } // anonymous namespace 70 71 PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx) 72 { 73 return adoptRef(new WebGLFramebuffer(ctx)); 74 } 75 76 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx) 77 : WebGLObject(ctx) 78 , m_hasEverBeenBound(false) 79 , m_texTarget(0) 80 , m_texLevel(-1) 81 { 82 setObject(context()->graphicsContext3D()->createFramebuffer()); 83 } 84 85 void WebGLFramebuffer::setAttachment(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level) 86 { 87 if (!object()) 88 return; 89 if (texture && !texture->object()) 90 texture = 0; 91 switch (attachment) { 92 case GraphicsContext3D::COLOR_ATTACHMENT0: 93 m_colorAttachment = texture; 94 if (texture) { 95 m_texTarget = texTarget; 96 m_texLevel = level; 97 } 98 break; 99 case GraphicsContext3D::DEPTH_ATTACHMENT: 100 m_depthAttachment = texture; 101 break; 102 case GraphicsContext3D::STENCIL_ATTACHMENT: 103 m_stencilAttachment = texture; 104 break; 105 case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: 106 m_depthStencilAttachment = texture; 107 break; 108 default: 109 return; 110 } 111 } 112 113 void WebGLFramebuffer::setAttachment(GC3Denum attachment, WebGLRenderbuffer* renderbuffer) 114 { 115 if (!object()) 116 return; 117 if (renderbuffer && !renderbuffer->object()) 118 renderbuffer = 0; 119 switch (attachment) { 120 case GraphicsContext3D::COLOR_ATTACHMENT0: 121 m_colorAttachment = renderbuffer; 122 break; 123 case GraphicsContext3D::DEPTH_ATTACHMENT: 124 m_depthAttachment = renderbuffer; 125 break; 126 case GraphicsContext3D::STENCIL_ATTACHMENT: 127 m_stencilAttachment = renderbuffer; 128 break; 129 case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: 130 m_depthStencilAttachment = renderbuffer; 131 break; 132 default: 133 return; 134 } 135 } 136 137 WebGLObject* WebGLFramebuffer::getAttachment(GC3Denum attachment) const 138 { 139 if (!object()) 140 return 0; 141 switch (attachment) { 142 case GraphicsContext3D::COLOR_ATTACHMENT0: 143 return m_colorAttachment.get(); 144 case GraphicsContext3D::DEPTH_ATTACHMENT: 145 return m_depthAttachment.get(); 146 case GraphicsContext3D::STENCIL_ATTACHMENT: 147 return m_stencilAttachment.get(); 148 case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: 149 return m_depthStencilAttachment.get(); 150 default: 151 return 0; 152 } 153 } 154 155 void WebGLFramebuffer::removeAttachment(WebGLObject* attachment) 156 { 157 if (!object()) 158 return; 159 if (attachment == m_colorAttachment.get()) 160 m_colorAttachment = 0; 161 else if (attachment == m_depthAttachment.get()) 162 m_depthAttachment = 0; 163 else if (attachment == m_stencilAttachment.get()) 164 m_stencilAttachment = 0; 165 else if (attachment == m_depthStencilAttachment.get()) 166 m_depthStencilAttachment = 0; 167 else 168 return; 169 } 170 171 GC3Dsizei WebGLFramebuffer::getWidth() const 172 { 173 if (!object() || !isColorAttached()) 174 return 0; 175 if (m_colorAttachment->isRenderbuffer()) 176 return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getWidth(); 177 if (m_colorAttachment->isTexture()) 178 return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getWidth(m_texTarget, m_texLevel); 179 ASSERT_NOT_REACHED(); 180 return 0; 181 } 182 183 GC3Dsizei WebGLFramebuffer::getHeight() const 184 { 185 if (!object() || !isColorAttached()) 186 return 0; 187 if (m_colorAttachment->isRenderbuffer()) 188 return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getHeight(); 189 if (m_colorAttachment->isTexture()) 190 return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getHeight(m_texTarget, m_texLevel); 191 ASSERT_NOT_REACHED(); 192 return 0; 193 } 194 195 GC3Denum WebGLFramebuffer::getColorBufferFormat() const 196 { 197 if (!object() || !isColorAttached()) 198 return 0; 199 if (m_colorAttachment->isRenderbuffer()) { 200 unsigned long format = (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getInternalFormat(); 201 switch (format) { 202 case GraphicsContext3D::RGBA4: 203 case GraphicsContext3D::RGB5_A1: 204 return GraphicsContext3D::RGBA; 205 case GraphicsContext3D::RGB565: 206 return GraphicsContext3D::RGB; 207 } 208 return 0; 209 } 210 if (m_colorAttachment->isTexture()) 211 return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getInternalFormat(m_texTarget, m_texLevel); 212 ASSERT_NOT_REACHED(); 213 return 0; 214 } 215 216 bool WebGLFramebuffer::isIncomplete(bool checkInternalFormat) const 217 { 218 unsigned int count = 0; 219 if (isDepthAttached()) { 220 if (checkInternalFormat && getInternalFormat(m_depthAttachment.get()) != GraphicsContext3D::DEPTH_COMPONENT16) 221 return true; 222 count++; 223 } 224 if (isStencilAttached()) { 225 if (checkInternalFormat && getInternalFormat(m_stencilAttachment.get()) != GraphicsContext3D::STENCIL_INDEX8) 226 return true; 227 count++; 228 } 229 if (isDepthStencilAttached()) { 230 if (checkInternalFormat && getInternalFormat(m_depthStencilAttachment.get()) != GraphicsContext3D::DEPTH_STENCIL) 231 return true; 232 if (!isValid(m_depthStencilAttachment.get())) 233 return true; 234 count++; 235 } 236 if (count > 1) 237 return true; 238 return false; 239 } 240 241 bool WebGLFramebuffer::onAccess(bool needToInitializeRenderbuffers) 242 { 243 if (isIncomplete(true)) 244 return false; 245 if (needToInitializeRenderbuffers) 246 return initializeRenderbuffers(); 247 return true; 248 } 249 250 void WebGLFramebuffer::deleteObjectImpl(Platform3DObject object) 251 { 252 context()->graphicsContext3D()->deleteFramebuffer(object); 253 m_colorAttachment = 0; 254 m_depthAttachment = 0; 255 m_stencilAttachment = 0; 256 m_depthStencilAttachment = 0; 257 } 258 259 bool WebGLFramebuffer::initializeRenderbuffers() 260 { 261 ASSERT(object()); 262 bool initColor = false, initDepth = false, initStencil = false; 263 GC3Dbitfield mask = 0; 264 if (isUninitialized(m_colorAttachment.get())) { 265 initColor = true; 266 mask |= GraphicsContext3D::COLOR_BUFFER_BIT; 267 } 268 if (isUninitialized(m_depthAttachment.get())) { 269 initDepth = true; 270 mask |= GraphicsContext3D::DEPTH_BUFFER_BIT; 271 } 272 if (isUninitialized(m_stencilAttachment.get())) { 273 initStencil = true; 274 mask |= GraphicsContext3D::STENCIL_BUFFER_BIT; 275 } 276 if (isUninitialized(m_depthStencilAttachment.get())) { 277 initDepth = true; 278 initStencil = true; 279 mask |= (GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT); 280 } 281 if (!initColor && !initDepth && !initStencil) 282 return true; 283 284 // We only clear un-initialized renderbuffers when they are ready to be 285 // read, i.e., when the framebuffer is complete. 286 GraphicsContext3D* g3d = context()->graphicsContext3D(); 287 if (g3d->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) 288 return false; 289 290 GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0; 291 GC3Dint stencilClearValue = 0; 292 GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0; 293 GC3Duint stencilMask = 0xffffffff; 294 GC3Dboolean isScissorEnabled = 0; 295 GC3Dboolean isDitherEnabled = 0; 296 if (initColor) { 297 g3d->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, colorClearValue); 298 g3d->getBooleanv(GraphicsContext3D::COLOR_WRITEMASK, colorMask); 299 g3d->clearColor(0, 0, 0, 0); 300 g3d->colorMask(true, true, true, true); 301 } 302 if (initDepth) { 303 g3d->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &depthClearValue); 304 g3d->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask); 305 g3d->clearDepth(0); 306 g3d->depthMask(true); 307 } 308 if (initStencil) { 309 g3d->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &stencilClearValue); 310 g3d->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<GC3Dint*>(&stencilMask)); 311 g3d->clearStencil(0); 312 g3d->stencilMask(0xffffffff); 313 } 314 isScissorEnabled = g3d->isEnabled(GraphicsContext3D::SCISSOR_TEST); 315 g3d->disable(GraphicsContext3D::SCISSOR_TEST); 316 isDitherEnabled = g3d->isEnabled(GraphicsContext3D::DITHER); 317 g3d->disable(GraphicsContext3D::DITHER); 318 319 g3d->clear(mask); 320 321 if (initColor) { 322 g3d->clearColor(colorClearValue[0], colorClearValue[1], colorClearValue[2], colorClearValue[3]); 323 g3d->colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); 324 } 325 if (initDepth) { 326 g3d->clearDepth(depthClearValue); 327 g3d->depthMask(depthMask); 328 } 329 if (initStencil) { 330 g3d->clearStencil(stencilClearValue); 331 g3d->stencilMask(stencilMask); 332 } 333 if (isScissorEnabled) 334 g3d->enable(GraphicsContext3D::SCISSOR_TEST); 335 else 336 g3d->disable(GraphicsContext3D::SCISSOR_TEST); 337 if (isDitherEnabled) 338 g3d->enable(GraphicsContext3D::DITHER); 339 else 340 g3d->disable(GraphicsContext3D::DITHER); 341 342 if (initColor) 343 setInitialized(m_colorAttachment.get()); 344 if (initDepth && initStencil && m_depthStencilAttachment) 345 setInitialized(m_depthStencilAttachment.get()); 346 else { 347 if (initDepth) 348 setInitialized(m_depthAttachment.get()); 349 if (initStencil) 350 setInitialized(m_stencilAttachment.get()); 351 } 352 return true; 353 } 354 355 } 356 357 #endif // ENABLE(WEBGL) 358