Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / canvas / WebGLContextState.cpp
blobe29c69656a98ca41c5b16db1cb203a79372f885b
1 /* -*- Mode: C++; tab-width: 4; 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/. */
6 #include "WebGLContext.h"
8 #include "GLContext.h"
9 #include "GLScreenBuffer.h"
10 #include "mozilla/Maybe.h"
11 #include "mozilla/Preferences.h"
12 #include "MozFramebuffer.h"
13 #include "nsString.h"
14 #include "WebGLBuffer.h"
15 #include "WebGLContextUtils.h"
16 #include "WebGLFramebuffer.h"
17 #include "WebGLProgram.h"
18 #include "WebGLRenderbuffer.h"
19 #include "WebGLShader.h"
20 #include "WebGLTexture.h"
21 #include "WebGLVertexArray.h"
23 namespace mozilla {
25 void WebGLContext::SetEnabled(const GLenum cap, const Maybe<GLuint> i,
26 const bool enabled) {
27 const FuncScope funcScope(*this, "enable(i)/disable(i)");
28 if (IsContextLost()) return;
30 if (!ValidateCapabilityEnum(cap)) return;
32 if (i) {
33 if (cap != LOCAL_GL_BLEND) {
34 ErrorInvalidEnumArg("cap", cap);
35 return;
38 const auto limit = MaxValidDrawBuffers();
39 if (*i >= limit) {
40 ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i,
41 "MAX_DRAW_BUFFERS", limit);
42 return;
46 const auto slot = GetStateTrackingSlot(cap, i ? *i : 0);
47 if (slot) {
48 *slot = enabled;
49 } else if (cap == LOCAL_GL_BLEND) {
50 if (i) {
51 mBlendEnabled[*i] = enabled;
52 } else {
53 if (enabled) {
54 mBlendEnabled.set();
55 } else {
56 mBlendEnabled.reset();
61 switch (cap) {
62 case LOCAL_GL_DEPTH_TEST:
63 case LOCAL_GL_STENCIL_TEST:
64 break; // Lazily applied, so don't tell GL yet or we will desync.
66 default:
67 // Non-lazy caps.
68 if (i) {
69 if (enabled) {
70 gl->fEnablei(cap, *i);
71 } else {
72 gl->fDisablei(cap, *i);
74 } else {
75 gl->SetEnabled(cap, enabled);
77 break;
81 bool WebGLContext::GetStencilBits(GLint* const out_stencilBits) const {
82 *out_stencilBits = 0;
83 if (mBoundDrawFramebuffer) {
84 if (!mBoundDrawFramebuffer->IsCheckFramebufferStatusComplete()) {
85 // Error, we don't know which stencil buffer's bits to use
86 ErrorInvalidFramebufferOperation(
87 "getParameter: framebuffer has two stencil buffers bound");
88 return false;
91 if (mBoundDrawFramebuffer->StencilAttachment().HasAttachment() ||
92 mBoundDrawFramebuffer->DepthStencilAttachment().HasAttachment()) {
93 *out_stencilBits = 8;
95 } else if (mOptions.stencil) {
96 *out_stencilBits = 8;
99 return true;
102 Maybe<double> WebGLContext::GetParameter(const GLenum pname) {
103 const FuncScope funcScope(*this, "getParameter");
104 if (IsContextLost()) return {};
106 if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
107 if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
108 return Some(MaxValidDrawBuffers());
110 } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
111 return Some(MaxValidDrawBuffers());
113 } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
114 pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + MaxValidDrawBuffers())) {
115 const auto slotId = pname - LOCAL_GL_DRAW_BUFFER0;
116 GLenum ret = LOCAL_GL_NONE;
117 if (!mBoundDrawFramebuffer) {
118 if (slotId == 0) {
119 ret = mDefaultFB_DrawBuffer0;
121 } else {
122 const auto& fb = *mBoundDrawFramebuffer;
123 const auto& bs = fb.DrawBufferEnabled();
124 if (bs[slotId]) {
125 ret = LOCAL_GL_COLOR_ATTACHMENT0 + slotId;
128 return Some(ret);
132 if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
133 switch (pname) {
134 case LOCAL_GL_TIMESTAMP_EXT: {
135 uint64_t val = 0;
136 if (Has64BitTimestamps()) {
137 gl->fGetInteger64v(pname, (GLint64*)&val);
138 } else {
139 gl->fGetIntegerv(pname, (GLint*)&val);
141 // TODO: JS doesn't support 64-bit integers. Be lossy and
142 // cast to double (53 bits)
143 return Some(val);
146 case LOCAL_GL_GPU_DISJOINT_EXT: {
147 realGLboolean val = false; // Not disjoint by default.
148 if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
149 gl->fGetBooleanv(pname, &val);
151 return Some(bool(val));
154 default:
155 break;
159 if (IsWebGL2() ||
160 IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
161 if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) {
162 GLint i = 0;
163 gl->fGetIntegerv(pname, &i);
164 return Some(i);
168 if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
169 if (pname == LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) {
170 GLfloat f = 0.f;
171 gl->fGetFloatv(pname, &f);
172 return Some(f);
176 if (IsExtensionEnabled(WebGLExtensionID::MOZ_debug)) {
177 if (pname == dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION) {
178 return Some(mNeedsIndexValidation);
182 switch (pname) {
183 ////////////////////////////////
184 // Single-value params
186 // unsigned int
187 case LOCAL_GL_CULL_FACE_MODE:
188 case LOCAL_GL_FRONT_FACE:
189 case LOCAL_GL_ACTIVE_TEXTURE:
190 case LOCAL_GL_STENCIL_FUNC:
191 case LOCAL_GL_STENCIL_FAIL:
192 case LOCAL_GL_STENCIL_PASS_DEPTH_FAIL:
193 case LOCAL_GL_STENCIL_PASS_DEPTH_PASS:
194 case LOCAL_GL_STENCIL_BACK_FUNC:
195 case LOCAL_GL_STENCIL_BACK_FAIL:
196 case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL:
197 case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS:
198 case LOCAL_GL_DEPTH_FUNC:
199 case LOCAL_GL_BLEND_SRC_RGB:
200 case LOCAL_GL_BLEND_SRC_ALPHA:
201 case LOCAL_GL_BLEND_DST_RGB:
202 case LOCAL_GL_BLEND_DST_ALPHA:
203 case LOCAL_GL_BLEND_EQUATION_RGB:
204 case LOCAL_GL_BLEND_EQUATION_ALPHA: {
205 GLint i = 0;
206 gl->fGetIntegerv(pname, &i);
207 return Some(i);
210 case LOCAL_GL_GENERATE_MIPMAP_HINT:
211 return Some(mGenerateMipmapHint);
213 case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT:
214 case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
215 const webgl::FormatUsageInfo* usage;
216 uint32_t width, height;
217 if (!BindCurFBForColorRead(&usage, &width, &height,
218 LOCAL_GL_INVALID_OPERATION))
219 return Nothing();
221 const auto implPI = ValidImplementationColorReadPI(usage);
223 GLenum ret;
224 if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
225 ret = implPI.format;
226 } else {
227 ret = implPI.type;
229 return Some(ret);
232 // int
233 case LOCAL_GL_STENCIL_REF:
234 case LOCAL_GL_STENCIL_BACK_REF: {
235 GLint stencilBits = 0;
236 if (!GetStencilBits(&stencilBits)) return Nothing();
238 // Assuming stencils have 8 bits
239 const GLint stencilMask = (1 << stencilBits) - 1;
241 GLint refValue = 0;
242 gl->fGetIntegerv(pname, &refValue);
244 return Some(refValue & stencilMask);
247 case LOCAL_GL_SAMPLE_BUFFERS:
248 case LOCAL_GL_SAMPLES: {
249 const auto& fb = mBoundDrawFramebuffer;
250 auto samples = [&]() -> Maybe<uint32_t> {
251 if (!fb) {
252 if (!EnsureDefaultFB()) return Nothing();
253 return Some(mDefaultFB->mSamples);
256 if (!fb->IsCheckFramebufferStatusComplete()) return Some(0);
258 DoBindFB(fb, LOCAL_GL_FRAMEBUFFER);
259 return Some(gl->GetIntAs<uint32_t>(LOCAL_GL_SAMPLES));
260 }();
261 if (samples && pname == LOCAL_GL_SAMPLE_BUFFERS) {
262 samples = Some(uint32_t(bool(samples.value())));
264 if (!samples) return Nothing();
265 return Some(samples.value());
268 case LOCAL_GL_STENCIL_CLEAR_VALUE:
269 case LOCAL_GL_SUBPIXEL_BITS: {
270 GLint i = 0;
271 gl->fGetIntegerv(pname, &i);
272 return Some(i);
275 case LOCAL_GL_RED_BITS:
276 case LOCAL_GL_GREEN_BITS:
277 case LOCAL_GL_BLUE_BITS:
278 case LOCAL_GL_ALPHA_BITS:
279 case LOCAL_GL_DEPTH_BITS:
280 case LOCAL_GL_STENCIL_BITS: {
281 const auto format = [&]() -> const webgl::FormatInfo* {
282 const auto& fb = mBoundDrawFramebuffer;
283 if (fb) {
284 if (!fb->IsCheckFramebufferStatusComplete()) return nullptr;
286 const auto& attachment = [&]() -> const auto& {
287 switch (pname) {
288 case LOCAL_GL_DEPTH_BITS:
289 if (fb->DepthStencilAttachment().HasAttachment())
290 return fb->DepthStencilAttachment();
291 return fb->DepthAttachment();
293 case LOCAL_GL_STENCIL_BITS:
294 if (fb->DepthStencilAttachment().HasAttachment())
295 return fb->DepthStencilAttachment();
296 return fb->StencilAttachment();
298 default:
299 return fb->ColorAttachment0();
304 const auto imageInfo = attachment.GetImageInfo();
305 if (!imageInfo) return nullptr;
306 return imageInfo->mFormat->format;
309 auto effFormat = webgl::EffectiveFormat::RGB8;
310 switch (pname) {
311 case LOCAL_GL_DEPTH_BITS:
312 if (mOptions.depth) {
313 effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8;
315 break;
317 case LOCAL_GL_STENCIL_BITS:
318 if (mOptions.stencil) {
319 effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8;
321 break;
323 default:
324 if (mOptions.alpha) {
325 effFormat = webgl::EffectiveFormat::RGBA8;
327 break;
329 return webgl::GetFormat(effFormat);
330 }();
331 int32_t ret = 0;
332 if (format) {
333 switch (pname) {
334 case LOCAL_GL_RED_BITS:
335 ret = format->r;
336 break;
337 case LOCAL_GL_GREEN_BITS:
338 ret = format->g;
339 break;
340 case LOCAL_GL_BLUE_BITS:
341 ret = format->b;
342 break;
343 case LOCAL_GL_ALPHA_BITS:
344 ret = format->a;
345 break;
346 case LOCAL_GL_DEPTH_BITS:
347 ret = format->d;
348 break;
349 case LOCAL_GL_STENCIL_BITS:
350 ret = format->s;
351 break;
354 return Some(ret);
357 case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
358 return Some(mGLMaxRenderbufferSize);
360 case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
361 return Some(mGLMaxVertexTextureImageUnits);
363 case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
364 return Some(mGLMaxFragmentTextureImageUnits);
366 case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
367 return Some(mGLMaxVertexUniformVectors);
369 case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
370 return Some(mGLMaxFragmentUniformVectors);
372 case LOCAL_GL_MAX_VARYING_VECTORS:
373 return Some(mGLMaxFragmentInputVectors);
375 // unsigned int. here we may have to return very large values like 2^32-1
376 // that can't be represented as javascript integer values. We just return
377 // them as doubles and javascript doesn't care.
378 case LOCAL_GL_STENCIL_BACK_VALUE_MASK:
379 return Some(mStencilValueMaskBack);
380 // pass as FP value to allow large values such as 2^32-1.
382 case LOCAL_GL_STENCIL_BACK_WRITEMASK:
383 return Some(mStencilWriteMaskBack);
385 case LOCAL_GL_STENCIL_VALUE_MASK:
386 return Some(mStencilValueMaskFront);
388 case LOCAL_GL_STENCIL_WRITEMASK:
389 return Some(mStencilWriteMaskFront);
391 case LOCAL_GL_COLOR_WRITEMASK:
392 return Some(mColorWriteMask0);
394 // float
395 case LOCAL_GL_LINE_WIDTH:
396 return Some((double)mLineWidth);
398 case LOCAL_GL_DEPTH_CLEAR_VALUE:
399 case LOCAL_GL_POLYGON_OFFSET_FACTOR:
400 case LOCAL_GL_POLYGON_OFFSET_UNITS:
401 case LOCAL_GL_SAMPLE_COVERAGE_VALUE: {
402 GLfloat f = 0.f;
403 gl->fGetFloatv(pname, &f);
404 return Some(f);
407 // bool
408 case LOCAL_GL_DEPTH_TEST:
409 return Some((bool)mDepthTestEnabled);
410 case LOCAL_GL_STENCIL_TEST:
411 return Some((bool)mStencilTestEnabled);
413 case LOCAL_GL_BLEND:
414 case LOCAL_GL_CULL_FACE:
415 case LOCAL_GL_DITHER:
416 case LOCAL_GL_POLYGON_OFFSET_FILL:
417 case LOCAL_GL_SCISSOR_TEST:
418 case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
419 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
420 case LOCAL_GL_SAMPLE_COVERAGE:
421 case LOCAL_GL_DEPTH_WRITEMASK: {
422 realGLboolean b = 0;
423 gl->fGetBooleanv(pname, &b);
424 return Some(bool(b));
427 default:
428 break;
431 ErrorInvalidEnumInfo("pname", pname);
432 return Nothing();
435 bool WebGLContext::IsEnabled(GLenum cap) {
436 const FuncScope funcScope(*this, "isEnabled");
437 if (IsContextLost()) return false;
439 if (!ValidateCapabilityEnum(cap)) return false;
441 const auto& slot = GetStateTrackingSlot(cap, 0);
442 if (slot) return *slot;
444 return gl->fIsEnabled(cap);
447 bool WebGLContext::ValidateCapabilityEnum(GLenum cap) {
448 switch (cap) {
449 case LOCAL_GL_BLEND:
450 case LOCAL_GL_CULL_FACE:
451 case LOCAL_GL_DEPTH_TEST:
452 case LOCAL_GL_DITHER:
453 case LOCAL_GL_POLYGON_OFFSET_FILL:
454 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
455 case LOCAL_GL_SAMPLE_COVERAGE:
456 case LOCAL_GL_SCISSOR_TEST:
457 case LOCAL_GL_STENCIL_TEST:
458 return true;
459 case LOCAL_GL_RASTERIZER_DISCARD:
460 return IsWebGL2();
461 default:
462 ErrorInvalidEnumInfo("cap", cap);
463 return false;
467 bool* WebGLContext::GetStateTrackingSlot(GLenum cap, GLuint i) {
468 switch (cap) {
469 case LOCAL_GL_DEPTH_TEST:
470 return &mDepthTestEnabled;
471 case LOCAL_GL_DITHER:
472 return &mDitherEnabled;
473 case LOCAL_GL_RASTERIZER_DISCARD:
474 return &mRasterizerDiscardEnabled;
475 case LOCAL_GL_SCISSOR_TEST:
476 return &mScissorTestEnabled;
477 case LOCAL_GL_STENCIL_TEST:
478 return &mStencilTestEnabled;
481 return nullptr;
484 } // namespace mozilla