WebGLRenderbuffer.cpp (8418B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "WebGLRenderbuffer.h" 7 8 #include "GLContext.h" 9 #include "ScopedGLHelpers.h" 10 #include "WebGLContext.h" 11 #include "WebGLFormats.h" 12 #include "WebGLStrongTypes.h" 13 #include "WebGLTexture.h" 14 #include "mozilla/dom/WebGLRenderingContextBinding.h" 15 16 namespace mozilla { 17 18 static GLenum DepthFormatForDepthStencilEmu(gl::GLContext* gl) { 19 // We might not be able to get 24-bit, so let's pretend! 20 if (gl->IsGLES() && !gl->IsExtensionSupported(gl::GLContext::OES_depth24)) 21 return LOCAL_GL_DEPTH_COMPONENT16; 22 23 return LOCAL_GL_DEPTH_COMPONENT24; 24 } 25 26 static GLuint DoCreateRenderbuffer(gl::GLContext* gl) { 27 GLuint ret = 0; 28 gl->fGenRenderbuffers(1, &ret); 29 return ret; 30 } 31 32 static bool EmulatePackedDepthStencil(gl::GLContext* gl) { 33 return !gl->IsSupported(gl::GLFeature::packed_depth_stencil); 34 } 35 36 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl) 37 : WebGLContextBoundObject(webgl), 38 mPrimaryRB(DoCreateRenderbuffer(webgl->gl)), 39 mEmulatePackedDepthStencil(EmulatePackedDepthStencil(webgl->gl)), 40 mSecondaryRB(0) { 41 // Bind our RB, or we might end up calling FramebufferRenderbuffer before we 42 // ever call BindRenderbuffer, since webgl.bindRenderbuffer doesn't actually 43 // call glBindRenderbuffer anymore. 44 mContext->gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB); 45 mContext->gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, 0); 46 } 47 48 WebGLRenderbuffer::~WebGLRenderbuffer() { 49 mImageInfo = webgl::ImageInfo(); 50 51 if (!mContext) return; 52 53 mContext->gl->fDeleteRenderbuffers(1, &mPrimaryRB); 54 if (mSecondaryRB) { 55 mContext->gl->fDeleteRenderbuffers(1, &mSecondaryRB); 56 } 57 } 58 59 static GLenum DoRenderbufferStorageMaybeMultisample(gl::GLContext* gl, 60 GLsizei samples, 61 GLenum internalFormat, 62 GLsizei width, 63 GLsizei height) { 64 MOZ_ASSERT_IF(samples >= 1, 65 gl->IsSupported(gl::GLFeature::framebuffer_multisample)); 66 67 // Certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL. 68 switch (internalFormat) { 69 case LOCAL_GL_RGBA4: 70 case LOCAL_GL_RGB5_A1: 71 // 16-bit RGBA formats are not supported on desktop GL. 72 if (!gl->IsGLES()) internalFormat = LOCAL_GL_RGBA8; 73 break; 74 75 case LOCAL_GL_RGB565: 76 // RGB565 is not supported on desktop GL. 77 if (!gl->IsGLES()) internalFormat = LOCAL_GL_RGB8; 78 break; 79 80 case LOCAL_GL_DEPTH_COMPONENT16: 81 if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24)) 82 internalFormat = LOCAL_GL_DEPTH_COMPONENT24; 83 else if (gl->IsSupported(gl::GLFeature::packed_depth_stencil)) 84 internalFormat = LOCAL_GL_DEPTH24_STENCIL8; 85 break; 86 87 case LOCAL_GL_DEPTH_STENCIL: 88 MOZ_CRASH("GFX: GL_DEPTH_STENCIL is not valid here."); 89 break; 90 91 default: 92 break; 93 } 94 95 gl::GLContext::LocalErrorScope errorScope(*gl); 96 97 if (samples > 0) { 98 gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples, 99 internalFormat, width, height); 100 } else { 101 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width, 102 height); 103 } 104 105 return errorScope.GetError(); 106 } 107 108 GLenum WebGLRenderbuffer::DoRenderbufferStorage( 109 uint32_t samples, const webgl::FormatUsageInfo* format, uint32_t width, 110 uint32_t height) { 111 gl::GLContext* gl = mContext->gl; 112 MOZ_ASSERT(samples <= 256); // Sanity check. 113 114 GLenum primaryFormat = format->format->sizedFormat; 115 GLenum secondaryFormat = 0; 116 117 if (mEmulatePackedDepthStencil && 118 primaryFormat == LOCAL_GL_DEPTH24_STENCIL8) { 119 primaryFormat = DepthFormatForDepthStencilEmu(gl); 120 secondaryFormat = LOCAL_GL_STENCIL_INDEX8; 121 } 122 123 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB); 124 GLenum error = DoRenderbufferStorageMaybeMultisample( 125 gl, samples, primaryFormat, width, height); 126 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, 0); 127 if (error) return error; 128 129 if (secondaryFormat) { 130 if (!mSecondaryRB) { 131 gl->fGenRenderbuffers(1, &mSecondaryRB); 132 } 133 134 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mSecondaryRB); 135 error = DoRenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, 136 width, height); 137 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, 0); 138 if (error) return error; 139 } else if (mSecondaryRB) { 140 gl->fDeleteRenderbuffers(1, &mSecondaryRB); 141 mSecondaryRB = 0; 142 } 143 144 return 0; 145 } 146 147 void WebGLRenderbuffer::RenderbufferStorage(uint32_t samples, 148 GLenum internalFormat, 149 uint32_t width, uint32_t height) { 150 const auto usage = mContext->mFormatUsage->GetRBUsage(internalFormat); 151 if (!usage) { 152 mContext->ErrorInvalidEnum("Invalid `internalFormat`: 0x%04x.", 153 internalFormat); 154 return; 155 } 156 157 if (width > mContext->mGLMaxRenderbufferSize || 158 height > mContext->mGLMaxRenderbufferSize) { 159 mContext->ErrorInvalidValue( 160 "Width or height exceeds maximum renderbuffer" 161 " size."); 162 return; 163 } 164 165 const auto maxSamples = usage->MaxSamples(*mContext->gl); 166 if (samples > maxSamples) { 167 mContext->ErrorInvalidOperation("`samples` is out of the valid range."); 168 return; 169 } 170 171 // Validation complete. 172 173 const GLenum error = DoRenderbufferStorage(samples, usage, width, height); 174 if (error) { 175 mContext->GenerateWarning("Unexpected error %s", EnumString(error).c_str()); 176 if (error == LOCAL_GL_OUT_OF_MEMORY) { 177 // Truncate. 178 mImageInfo = {}; 179 InvalidateCaches(); 180 } 181 return; 182 } 183 184 mContext->OnDataAllocCall(); 185 186 const uint32_t depth = 1; 187 std::vector<bool> uninitializedSlices(depth, true); 188 mImageInfo = { 189 usage, width, height, depth, std::move(uninitializedSlices), 190 uint8_t(samples)}; 191 InvalidateCaches(); 192 } 193 194 void WebGLRenderbuffer::DoFramebufferRenderbuffer( 195 const GLenum attachment) const { 196 gl::GLContext* gl = mContext->gl; 197 198 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { 199 const GLuint stencilRB = (mSecondaryRB ? mSecondaryRB : mPrimaryRB); 200 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, 201 LOCAL_GL_DEPTH_ATTACHMENT, 202 LOCAL_GL_RENDERBUFFER, mPrimaryRB); 203 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, 204 LOCAL_GL_STENCIL_ATTACHMENT, 205 LOCAL_GL_RENDERBUFFER, stencilRB); 206 return; 207 } 208 209 gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment, 210 LOCAL_GL_RENDERBUFFER, mPrimaryRB); 211 } 212 213 GLint WebGLRenderbuffer::GetRenderbufferParameter(RBParam pname) const { 214 gl::GLContext* gl = mContext->gl; 215 216 switch (pname.get()) { 217 case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: 218 if (!mImageInfo.mFormat) return 0; 219 220 if (!mImageInfo.mFormat->format->s) return 0; 221 222 return 8; 223 224 case LOCAL_GL_RENDERBUFFER_SAMPLES: 225 case LOCAL_GL_RENDERBUFFER_WIDTH: 226 case LOCAL_GL_RENDERBUFFER_HEIGHT: 227 case LOCAL_GL_RENDERBUFFER_RED_SIZE: 228 case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: 229 case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: 230 case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: 231 case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: { 232 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB); 233 GLint i = 0; 234 gl->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER, pname.get(), &i); 235 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, 0); 236 return i; 237 } 238 239 case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: { 240 GLenum ret = LOCAL_GL_RGBA4; 241 if (mImageInfo.mFormat) { 242 ret = mImageInfo.mFormat->format->sizedFormat; 243 244 if (!mContext->IsWebGL2() && ret == LOCAL_GL_DEPTH24_STENCIL8) { 245 ret = LOCAL_GL_DEPTH_STENCIL; 246 } 247 } 248 return ret; 249 } 250 } 251 252 MOZ_ASSERT(false, "This function should only be called with valid `pname`."); 253 return 0; 254 } 255 256 } // namespace mozilla