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/. */
5 
6 #include "WebGLContext.h"
7 
8 #include "GLContext.h"
9 #include "GLScreenBuffer.h"
10 #include "WebGLFormats.h"
11 #include "WebGLFramebuffer.h"
12 #include "WebGLRenderbuffer.h"
13 #include "WebGLTexture.h"
14 
15 namespace mozilla {
16 
Clear(GLbitfield mask)17 void WebGLContext::Clear(GLbitfield mask) {
18   const FuncScope funcScope(*this, "clear");
19   if (IsContextLost()) return;
20 
21   uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT |
22                        LOCAL_GL_STENCIL_BUFFER_BIT);
23   if (mask != m) return ErrorInvalidValue("Invalid mask bits.");
24 
25   if (mask == 0) {
26     GenerateWarning("Calling gl.clear(0) has no effect.");
27   } else if (mRasterizerDiscardEnabled) {
28     GenerateWarning(
29         "Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
30   }
31 
32   if (mask & LOCAL_GL_COLOR_BUFFER_BIT && mBoundDrawFramebuffer) {
33     for (const auto& cur : mBoundDrawFramebuffer->ColorDrawBuffers()) {
34       const auto imageInfo = cur->GetImageInfo();
35       if (!imageInfo || !imageInfo->mFormat) continue;
36 
37       if (imageInfo->mFormat->format->baseType !=
38           webgl::TextureBaseType::Float) {
39         ErrorInvalidOperation(
40             "Color draw buffers must be floating-point"
41             " or fixed-point. (normalized (u)ints)");
42         return;
43       }
44     }
45   }
46 
47   if (!BindCurFBForDraw()) return;
48 
49   auto driverMask = mask;
50   if (!mBoundDrawFramebuffer) {
51     if (mNeedsFakeNoDepth) {
52       driverMask &= ~LOCAL_GL_DEPTH_BUFFER_BIT;
53     }
54     if (mNeedsFakeNoStencil) {
55       driverMask &= ~LOCAL_GL_STENCIL_BUFFER_BIT;
56     }
57   }
58 
59   const ScopedDrawCallWrapper wrapper(*this);
60   gl->fClear(driverMask);
61 }
62 
GLClampFloat(GLfloat val)63 static GLfloat GLClampFloat(GLfloat val) {
64   if (val < 0.0) return 0.0;
65 
66   if (val > 1.0) return 1.0;
67 
68   return val;
69 }
70 
ClearColor(GLfloat r,GLfloat g,GLfloat b,GLfloat a)71 void WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
72   const FuncScope funcScope(*this, "clearColor");
73   if (IsContextLost()) return;
74 
75   if (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_float)) {
76     MOZ_ASSERT(IsExtensionExplicit(WebGLExtensionID::EXT_color_buffer_float));
77 
78   } else if (IsExtensionEnabled(
79                  WebGLExtensionID::EXT_color_buffer_half_float) ||
80              IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) {
81     const bool explict =
82         (IsExtensionExplicit(WebGLExtensionID::EXT_color_buffer_half_float) ||
83          IsExtensionExplicit(WebGLExtensionID::WEBGL_color_buffer_float));
84     const bool wouldHaveClamped =
85         (r != GLClampFloat(r) || g != GLClampFloat(g) || b != GLClampFloat(b) ||
86          a != GLClampFloat(a));
87     if (!explict && wouldHaveClamped) {
88       if (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)) {
89         WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float);
90       } else if (IsExtensionEnabled(
91                      WebGLExtensionID::WEBGL_color_buffer_float)) {
92         WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float);
93       }
94     }
95   } else {
96     r = GLClampFloat(r);
97     g = GLClampFloat(g);
98     b = GLClampFloat(b);
99     a = GLClampFloat(a);
100   }
101 
102   gl->fClearColor(r, g, b, a);
103 
104   mColorClearValue[0] = r;
105   mColorClearValue[1] = g;
106   mColorClearValue[2] = b;
107   mColorClearValue[3] = a;
108 }
109 
ClearDepth(GLclampf v)110 void WebGLContext::ClearDepth(GLclampf v) {
111   const FuncScope funcScope(*this, "clearDepth");
112   if (IsContextLost()) return;
113 
114   mDepthClearValue = GLClampFloat(v);
115   gl->fClearDepth(mDepthClearValue);
116 }
117 
ClearStencil(GLint v)118 void WebGLContext::ClearStencil(GLint v) {
119   const FuncScope funcScope(*this, "clearStencil");
120   if (IsContextLost()) return;
121 
122   mStencilClearValue = v;
123   gl->fClearStencil(v);
124 }
125 
ColorMask(WebGLboolean r,WebGLboolean g,WebGLboolean b,WebGLboolean a)126 void WebGLContext::ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b,
127                              WebGLboolean a) {
128   const FuncScope funcScope(*this, "colorMask");
129   if (IsContextLost()) return;
130 
131   mColorWriteMask = uint8_t(bool(r) << 0) | uint8_t(bool(g) << 1) |
132                     uint8_t(bool(b) << 2) | uint8_t(bool(a) << 3);
133 }
134 
DepthMask(WebGLboolean b)135 void WebGLContext::DepthMask(WebGLboolean b) {
136   const FuncScope funcScope(*this, "depthMask");
137   if (IsContextLost()) return;
138 
139   mDepthWriteMask = b;
140   gl->fDepthMask(b);
141 }
142 
DrawBuffers(const std::vector<GLenum> & buffers)143 void WebGLContext::DrawBuffers(const std::vector<GLenum>& buffers) {
144   const FuncScope funcScope(*this, "drawBuffers");
145   if (IsContextLost()) return;
146 
147   if (mBoundDrawFramebuffer) {
148     mBoundDrawFramebuffer->DrawBuffers(buffers);
149     return;
150   }
151 
152   // GLES 3.0.4 p186:
153   // "If the GL is bound to the default framebuffer, then `n` must be 1 and the
154   //  constant must be BACK or NONE. [...] If DrawBuffers is supplied with a
155   //  constant other than BACK and NONE, or with a value of `n` other than 1,
156   //  the error INVALID_OPERATION is generated."
157   if (buffers.size() != 1) {
158     ErrorInvalidOperation(
159         "For the default framebuffer, `buffers` must have a"
160         " length of 1.");
161     return;
162   }
163 
164   switch (buffers[0]) {
165     case LOCAL_GL_NONE:
166     case LOCAL_GL_BACK:
167       break;
168 
169     default:
170       ErrorInvalidOperation(
171           "For the default framebuffer, `buffers[0]` must be"
172           " BACK or NONE.");
173       return;
174   }
175 
176   mDefaultFB_DrawBuffer0 = buffers[0];
177   // Don't actually set it.
178 }
179 
StencilMaskSeparate(GLenum face,GLuint mask)180 void WebGLContext::StencilMaskSeparate(GLenum face, GLuint mask) {
181   const FuncScope funcScope(*this, "stencilMaskSeparate");
182   if (IsContextLost()) return;
183 
184   if (!ValidateFaceEnum(face)) return;
185 
186   switch (face) {
187     case LOCAL_GL_FRONT_AND_BACK:
188       mStencilWriteMaskFront = mask;
189       mStencilWriteMaskBack = mask;
190       break;
191     case LOCAL_GL_FRONT:
192       mStencilWriteMaskFront = mask;
193       break;
194     case LOCAL_GL_BACK:
195       mStencilWriteMaskBack = mask;
196       break;
197   }
198 
199   gl->fStencilMaskSeparate(face, mask);
200 }
201 
202 }  // namespace mozilla
203