1/*
2 * Copyright (C) 2015-2017 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 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 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#include "WebGLRenderingContextBase.h"
28
29#if ENABLE(WEBGL)
30
31#include "ANGLEInstancedArrays.h"
32#include "CachedImage.h"
33#include "DOMWindow.h"
34#include "DiagnosticLoggingClient.h"
35#include "DiagnosticLoggingKeys.h"
36#include "Document.h"
37#include "EXTBlendMinMax.h"
38#include "EXTFragDepth.h"
39#include "EXTShaderTextureLOD.h"
40#include "EXTTextureFilterAnisotropic.h"
41#include "EXTsRGB.h"
42#include "EventNames.h"
43#include "Extensions3D.h"
44#include "Frame.h"
45#include "FrameLoader.h"
46#include "FrameLoaderClient.h"
47#include "FrameView.h"
48#include "GraphicsContext.h"
49#include "HTMLCanvasElement.h"
50#include "HTMLImageElement.h"
51#include "HTMLVideoElement.h"
52#include "ImageBuffer.h"
53#include "ImageData.h"
54#include "InspectorInstrumentation.h"
55#include "IntSize.h"
56#include "JSExecState.h"
57#include "Logging.h"
58#include "NotImplemented.h"
59#include "OESElementIndexUint.h"
60#include "OESStandardDerivatives.h"
61#include "OESTextureFloat.h"
62#include "OESTextureFloatLinear.h"
63#include "OESTextureHalfFloat.h"
64#include "OESTextureHalfFloatLinear.h"
65#include "OESVertexArrayObject.h"
66#include "OffscreenCanvas.h"
67#include "Page.h"
68#include "RenderBox.h"
69#include "RuntimeEnabledFeatures.h"
70#include "Settings.h"
71#include "WebGL2RenderingContext.h"
72#include "WebGLActiveInfo.h"
73#include "WebGLBuffer.h"
74#include "WebGLCompressedTextureASTC.h"
75#include "WebGLCompressedTextureATC.h"
76#include "WebGLCompressedTexturePVRTC.h"
77#include "WebGLCompressedTextureS3TC.h"
78#include "WebGLContextAttributes.h"
79#include "WebGLContextEvent.h"
80#include "WebGLContextGroup.h"
81#include "WebGLDebugRendererInfo.h"
82#include "WebGLDebugShaders.h"
83#include "WebGLDepthTexture.h"
84#include "WebGLDrawBuffers.h"
85#include "WebGLFramebuffer.h"
86#include "WebGLLoseContext.h"
87#include "WebGLProgram.h"
88#include "WebGLRenderbuffer.h"
89#include "WebGLRenderingContext.h"
90#include "WebGLShader.h"
91#include "WebGLShaderPrecisionFormat.h"
92#include "WebGLTexture.h"
93#include "WebGLUniformLocation.h"
94#include <JavaScriptCore/ConsoleMessage.h>
95#include <JavaScriptCore/JSCInlines.h>
96#include <JavaScriptCore/ScriptCallStack.h>
97#include <JavaScriptCore/ScriptCallStackFactory.h>
98#include <JavaScriptCore/TypedArrayInlines.h>
99#include <JavaScriptCore/Uint32Array.h>
100#include <wtf/CheckedArithmetic.h>
101#include <wtf/HashMap.h>
102#include <wtf/HexNumber.h>
103#include <wtf/IsoMallocInlines.h>
104#include <wtf/Lock.h>
105#include <wtf/StdLibExtras.h>
106#include <wtf/UniqueArray.h>
107#include <wtf/text/CString.h>
108#include <wtf/text/StringBuilder.h>
109
110namespace WebCore {
111
112WTF_MAKE_ISO_ALLOCATED_IMPL(WebGLRenderingContextBase);
113
114static const Seconds secondsBetweenRestoreAttempts { 1_s };
115const int maxGLErrorsAllowedToConsole = 256;
116static const Seconds checkContextLossHandlingDelay { 3_s };
117
118namespace {
119
120 Platform3DObject objectOrZero(WebGLObject* object)
121 {
122 return object ? object->object() : 0;
123 }
124
125 GC3Dint clamp(GC3Dint value, GC3Dint min, GC3Dint max)
126 {
127 if (value < min)
128 value = min;
129 if (value > max)
130 value = max;
131 return value;
132 }
133
134 // Return true if a character belongs to the ASCII subset as defined in
135 // GLSL ES 1.0 spec section 3.1.
136 bool validateCharacter(unsigned char c)
137 {
138 // Printing characters are valid except " $ ` @ \ ' DEL.
139 if (c >= 32 && c <= 126
140 && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'')
141 return true;
142 // Horizontal tab, line feed, vertical tab, form feed, carriage return
143 // are also valid.
144 if (c >= 9 && c <= 13)
145 return true;
146 return false;
147 }
148
149 bool isPrefixReserved(const String& name)
150 {
151 if (name.startsWith("gl_") || name.startsWith("webgl_") || name.startsWith("_webgl_"))
152 return true;
153 return false;
154 }
155
156 // Strips comments from shader text. This allows non-ASCII characters
157 // to be used in comments without potentially breaking OpenGL
158 // implementations not expecting characters outside the GLSL ES set.
159 class StripComments {
160 public:
161 StripComments(const String& str)
162 : m_parseState(BeginningOfLine)
163 , m_sourceString(str)
164 , m_length(str.length())
165 , m_position(0)
166 {
167 parse();
168 }
169
170 String result()
171 {
172 return m_builder.toString();
173 }
174
175 private:
176 bool hasMoreCharacters() const
177 {
178 return (m_position < m_length);
179 }
180
181 void parse()
182 {
183 while (hasMoreCharacters()) {
184 process(current());
185 // process() might advance the position.
186 if (hasMoreCharacters())
187 advance();
188 }
189 }
190
191 void process(UChar);
192
193 bool peek(UChar& character) const
194 {
195 if (m_position + 1 >= m_length)
196 return false;
197 character = m_sourceString[m_position + 1];
198 return true;
199 }
200
201 UChar current() const
202 {
203 ASSERT_WITH_SECURITY_IMPLICATION(m_position < m_length);
204 return m_sourceString[m_position];
205 }
206
207 void advance()
208 {
209 ++m_position;
210 }
211
212 bool isNewline(UChar character) const
213 {
214 // Don't attempt to canonicalize newline related characters.
215 return (character == '\n' || character == '\r');
216 }
217
218 void emit(UChar character)
219 {
220 m_builder.append(character);
221 }
222
223 enum ParseState {
224 // Have not seen an ASCII non-whitespace character yet on
225 // this line. Possible that we might see a preprocessor
226 // directive.
227 BeginningOfLine,
228
229 // Have seen at least one ASCII non-whitespace character
230 // on this line.
231 MiddleOfLine,
232
233 // Handling a preprocessor directive. Passes through all
234 // characters up to the end of the line. Disables comment
235 // processing.
236 InPreprocessorDirective,
237
238 // Handling a single-line comment. The comment text is
239 // replaced with a single space.
240 InSingleLineComment,
241
242 // Handling a multi-line comment. Newlines are passed
243 // through to preserve line numbers.
244 InMultiLineComment
245 };
246
247 ParseState m_parseState;
248 String m_sourceString;
249 unsigned m_length;
250 unsigned m_position;
251 StringBuilder m_builder;
252 };
253
254 void StripComments::process(UChar c)
255 {
256 if (isNewline(c)) {
257 // No matter what state we are in, pass through newlines
258 // so we preserve line numbers.
259 emit(c);
260
261 if (m_parseState != InMultiLineComment)
262 m_parseState = BeginningOfLine;
263
264 return;
265 }
266
267 UChar temp = 0;
268 switch (m_parseState) {
269 case BeginningOfLine:
270 if (WTF::isASCIISpace(c)) {
271 emit(c);
272 break;
273 }
274
275 if (c == '#') {
276 m_parseState = InPreprocessorDirective;
277 emit(c);
278 break;
279 }
280
281 // Transition to normal state and re-handle character.
282 m_parseState = MiddleOfLine;
283 process(c);
284 break;
285
286 case MiddleOfLine:
287 if (c == '/' && peek(temp)) {
288 if (temp == '/') {
289 m_parseState = InSingleLineComment;
290 emit(' ');
291 advance();
292 break;
293 }
294
295 if (temp == '*') {
296 m_parseState = InMultiLineComment;
297 // Emit the comment start in case the user has
298 // an unclosed comment and we want to later
299 // signal an error.
300 emit('/');
301 emit('*');
302 advance();
303 break;
304 }
305 }
306
307 emit(c);
308 break;
309
310 case InPreprocessorDirective:
311 // No matter what the character is, just pass it
312 // through. Do not parse comments in this state. This
313 // might not be the right thing to do long term, but it
314 // should handle the #error preprocessor directive.
315 emit(c);
316 break;
317
318 case InSingleLineComment:
319 // The newline code at the top of this function takes care
320 // of resetting our state when we get out of the
321 // single-line comment. Swallow all other characters.
322 break;
323
324 case InMultiLineComment:
325 if (c == '*' && peek(temp) && temp == '/') {
326 emit('*');
327 emit('/');
328 m_parseState = MiddleOfLine;
329 advance();
330 break;
331 }
332
333 // Swallow all other characters. Unclear whether we may
334 // want or need to just emit a space per character to try
335 // to preserve column numbers for debugging purposes.
336 break;
337 }
338 }
339} // namespace anonymous
340
341// Returns false if no clipping is necessary, i.e., x, y, width, height stay the same.
342static bool clip2D(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height,
343 GC3Dsizei sourceWidth, GC3Dsizei sourceHeight,
344 GC3Dint* clippedX, GC3Dint* clippedY, GC3Dsizei* clippedWidth, GC3Dsizei*clippedHeight)
345{
346 ASSERT(clippedX && clippedY && clippedWidth && clippedHeight);
347
348 GC3Dint left = std::max(x, 0);
349 GC3Dint top = std::max(y, 0);
350 GC3Dint right = 0;
351 GC3Dint bottom = 0;
352
353 Checked<GC3Dint, RecordOverflow> checkedInputRight = Checked<GC3Dint>(x) + Checked<GC3Dsizei>(width);
354 Checked<GC3Dint, RecordOverflow> checkedInputBottom = Checked<GC3Dint>(y) + Checked<GC3Dsizei>(height);
355 if (!checkedInputRight.hasOverflowed() && !checkedInputBottom.hasOverflowed()) {
356 right = std::min(checkedInputRight.unsafeGet(), sourceWidth);
357 bottom = std::min(checkedInputBottom.unsafeGet(), sourceHeight);
358 }
359
360 if (left >= right || top >= bottom) {
361 *clippedX = 0;
362 *clippedY = 0;
363 *clippedWidth = 0;
364 *clippedHeight = 0;
365 return true;
366 }
367
368 *clippedX = left;
369 *clippedY = top;
370 *clippedWidth = right - left;
371 *clippedHeight = bottom - top;
372
373 return (*clippedX != x || *clippedY != y || *clippedWidth != width || *clippedHeight != height);
374}
375
376class WebGLRenderingContextLostCallback : public GraphicsContext3D::ContextLostCallback {
377 WTF_MAKE_FAST_ALLOCATED;
378public:
379 explicit WebGLRenderingContextLostCallback(WebGLRenderingContextBase* cb) : m_context(cb) { }
380 virtual ~WebGLRenderingContextLostCallback() = default;
381
382 void onContextLost() override { m_context->forceLostContext(WebGLRenderingContext::RealLostContext); }
383private:
384 WebGLRenderingContextBase* m_context;
385};
386
387class WebGLRenderingContextErrorMessageCallback : public GraphicsContext3D::ErrorMessageCallback {
388 WTF_MAKE_FAST_ALLOCATED;
389public:
390 explicit WebGLRenderingContextErrorMessageCallback(WebGLRenderingContextBase* cb) : m_context(cb) { }
391 virtual ~WebGLRenderingContextErrorMessageCallback() = default;
392
393 void onErrorMessage(const String& message, GC3Dint) override
394 {
395 if (m_context->m_synthesizedErrorsToConsole)
396 m_context->printToConsole(MessageLevel::Error, message);
397 }
398private:
399 WebGLRenderingContextBase* m_context;
400};
401
402class InspectorScopedShaderProgramHighlight {
403public:
404 InspectorScopedShaderProgramHighlight(WebGLRenderingContextBase& context, WebGLProgram* program)
405 : m_context(context)
406 , m_program(program)
407 {
408 showHightlight();
409 }
410
411 ~InspectorScopedShaderProgramHighlight()
412 {
413 hideHighlight();
414 }
415
416private:
417 void showHightlight()
418 {
419 if (!m_program || LIKELY(!InspectorInstrumentation::isShaderProgramHighlighted(m_context, *m_program)))
420 return;
421
422 if (hasBufferBinding(GraphicsContext3D::FRAMEBUFFER_BINDING)) {
423 if (!hasBufferBinding(GraphicsContext3D::RENDERBUFFER_BINDING))
424 return;
425 if (hasFramebufferParameterAttachment(GraphicsContext3D::DEPTH_ATTACHMENT))
426 return;
427 if (hasFramebufferParameterAttachment(GraphicsContext3D::STENCIL_ATTACHMENT))
428 return;
429#if ENABLE(WEBGL2)
430 if (hasFramebufferParameterAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT))
431 return;
432#endif
433 }
434
435 saveBlendValue(GraphicsContext3D::BLEND_COLOR, m_savedBlend.color);
436 saveBlendValue(GraphicsContext3D::BLEND_EQUATION_RGB, m_savedBlend.equationRGB);
437 saveBlendValue(GraphicsContext3D::BLEND_EQUATION_ALPHA, m_savedBlend.equationAlpha);
438 saveBlendValue(GraphicsContext3D::BLEND_SRC_RGB, m_savedBlend.srcRGB);
439 saveBlendValue(GraphicsContext3D::BLEND_SRC_ALPHA, m_savedBlend.srcAlpha);
440 saveBlendValue(GraphicsContext3D::BLEND_DST_RGB, m_savedBlend.dstRGB);
441 saveBlendValue(GraphicsContext3D::BLEND_DST_ALPHA, m_savedBlend.dstAlpha);
442 saveBlendValue(GraphicsContext3D::BLEND, m_savedBlend.enabled);
443
444 static const GC3Dfloat red = 111.0 / 255.0;
445 static const GC3Dfloat green = 168.0 / 255.0;
446 static const GC3Dfloat blue = 220.0 / 255.0;
447 static const GC3Dfloat alpha = 2.0 / 3.0;
448
449 m_context.enable(GraphicsContext3D::BLEND);
450 m_context.blendColor(red, green, blue, alpha);
451 m_context.blendEquation(GraphicsContext3D::FUNC_ADD);
452 m_context.blendFunc(GraphicsContext3D::CONSTANT_COLOR, GraphicsContext3D::ONE_MINUS_SRC_ALPHA);
453
454 m_didApply = true;
455 }
456
457 void hideHighlight()
458 {
459 if (!m_didApply)
460 return;
461
462 if (!m_savedBlend.enabled)
463 m_context.disable(GraphicsContext3D::BLEND);
464
465 const RefPtr<Float32Array>& color = m_savedBlend.color;
466 m_context.blendColor(color->item(0), color->item(1), color->item(2), color->item(3));
467 m_context.blendEquationSeparate(m_savedBlend.equationRGB, m_savedBlend.equationAlpha);
468 m_context.blendFuncSeparate(m_savedBlend.srcRGB, m_savedBlend.dstRGB, m_savedBlend.srcAlpha, m_savedBlend.dstAlpha);
469
470 m_savedBlend.color = nullptr;
471
472 m_didApply = false;
473 }
474
475 template <typename T>
476 void saveBlendValue(GC3Denum attachment, T& destination)
477 {
478 WebGLAny param = m_context.getParameter(attachment);
479 if (WTF::holds_alternative<T>(param))
480 destination = WTF::get<T>(param);
481 }
482
483 bool hasBufferBinding(GC3Denum pname)
484 {
485 WebGLAny binding = m_context.getParameter(pname);
486 if (pname == GraphicsContext3D::FRAMEBUFFER_BINDING)
487 return WTF::holds_alternative<RefPtr<WebGLFramebuffer>>(binding) && WTF::get<RefPtr<WebGLFramebuffer>>(binding);
488 if (pname == GraphicsContext3D::RENDERBUFFER_BINDING)
489 return WTF::holds_alternative<RefPtr<WebGLRenderbuffer>>(binding) && WTF::get<RefPtr<WebGLRenderbuffer>>(binding);
490 return false;
491 }
492
493 bool hasFramebufferParameterAttachment(GC3Denum attachment)
494 {
495 WebGLAny attachmentParameter = m_context.getFramebufferAttachmentParameter(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
496 if (!WTF::holds_alternative<unsigned>(attachmentParameter))
497 return false;
498 if (WTF::get<unsigned>(attachmentParameter) != static_cast<unsigned>(GraphicsContext3D::RENDERBUFFER))
499 return false;
500 return true;
501 }
502
503 struct {
504 RefPtr<Float32Array> color;
505 unsigned equationRGB { 0 };
506 unsigned equationAlpha { 0 };
507 unsigned srcRGB { 0 };
508 unsigned srcAlpha { 0 };
509 unsigned dstRGB { 0 };
510 unsigned dstAlpha { 0 };
511 bool enabled { false };
512 } m_savedBlend;
513
514 WebGLRenderingContextBase& m_context;
515 WebGLProgram* m_program { nullptr };
516 bool m_didApply { false };
517};
518
519static bool isHighPerformanceContext(const RefPtr<GraphicsContext3D>& context)
520{
521 return context->powerPreferenceUsedForCreation() == WebGLPowerPreference::HighPerformance;
522}
523
524std::unique_ptr<WebGLRenderingContextBase> WebGLRenderingContextBase::create(CanvasBase& canvas, WebGLContextAttributes& attributes, const String& type)
525{
526#if ENABLE(WEBGL2)
527 if (type == "webgl2" && !RuntimeEnabledFeatures::sharedFeatures().webGL2Enabled())
528 return nullptr;
529#else
530 UNUSED_PARAM(type);
531#endif
532
533 bool isPendingPolicyResolution = false;
534 HostWindow* hostWindow = nullptr;
535
536 auto* canvasElement = is<HTMLCanvasElement>(canvas) ? &downcast<HTMLCanvasElement>(canvas) : nullptr;
537
538 if (canvasElement) {
539 Document& document = canvasElement->document();
540 RefPtr<Frame> frame = document.frame();
541 if (!frame)
542 return nullptr;
543
544 // The FrameLoaderClient might block creation of a new WebGL context despite the page settings; in
545 // particular, if WebGL contexts were lost one or more times via the GL_ARB_robustness extension.
546 if (!frame->loader().client().allowWebGL(frame->settings().webGLEnabled())) {
547 canvasElement->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextcreationerrorEvent,
548 Event::CanBubble::No, Event::IsCancelable::Yes, "Web page was not allowed to create a WebGL context."));
549 return nullptr;
550 }
551
552 Document& topDocument = document.topDocument();
553 Page* page = topDocument.page();
554 bool forcingPendingPolicy = frame->settings().isForcePendingWebGLPolicy();
555
556 if (forcingPendingPolicy || (page && !topDocument.url().isLocalFile())) {
557 WebGLLoadPolicy policy = forcingPendingPolicy ? WebGLPendingCreation : page->mainFrame().loader().client().webGLPolicyForURL(topDocument.url());
558
559 if (policy == WebGLBlockCreation) {
560 LOG(WebGL, "The policy for this URL (%s) is to block WebGL.", topDocument.url().host().utf8().data());
561 return nullptr;
562 }
563
564 if (policy == WebGLPendingCreation) {
565 LOG(WebGL, "WebGL policy is pending. May need to be resolved later.");
566 isPendingPolicyResolution = true;
567 }
568 }
569
570 if (frame->settings().forceSoftwareWebGLRendering())
571 attributes.forceSoftwareRenderer = true;
572
573 if (frame->settings().forceWebGLUsesLowPower()) {
574 if (attributes.powerPreference == GraphicsContext3DPowerPreference::HighPerformance)
575 LOG(WebGL, "Overriding powerPreference from high-performance to low-power.");
576 attributes.powerPreference = GraphicsContext3DPowerPreference::LowPower;
577 }
578
579 if (page)
580 attributes.devicePixelRatio = page->deviceScaleFactor();
581
582 hostWindow = document.view()->root()->hostWindow();
583 }
584
585 attributes.noExtensions = true;
586 attributes.shareResources = false;
587
588 attributes.initialPowerPreference = attributes.powerPreference;
589
590
591#if ENABLE(WEBGL2)
592 if (type == "webgl2")
593 attributes.isWebGL2 = true;
594#endif
595
596 if (isPendingPolicyResolution) {
597 LOG(WebGL, "Create a WebGL context that looks real, but will require a policy resolution if used.");
598 std::unique_ptr<WebGLRenderingContextBase> renderingContext = nullptr;
599#if ENABLE(WEBGL2)
600 if (type == "webgl2")
601 renderingContext = WebGL2RenderingContext::create(canvas, attributes);
602 else
603#endif
604 renderingContext = WebGLRenderingContext::create(canvas, attributes);
605 renderingContext->suspendIfNeeded();
606 return renderingContext;
607 }
608
609 auto context = GraphicsContext3D::create(attributes, hostWindow);
610 if (!context || !context->makeContextCurrent()) {
611 if (canvasElement) {
612 canvasElement->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextcreationerrorEvent,
613 Event::CanBubble::No, Event::IsCancelable::Yes, "Could not create a WebGL context."));
614 }
615 return nullptr;
616 }
617
618 auto& extensions = context->getExtensions();
619 if (extensions.supports("GL_EXT_debug_marker"_s))
620 extensions.pushGroupMarkerEXT("WebGLRenderingContext"_s);
621
622#if ENABLE(WEBGL2) && PLATFORM(MAC)
623 // glTexStorage() was only added to Core in OpenGL 4.2.
624 // However, according to https://developer.apple.com/opengl/capabilities/ all Apple GPUs support this extension.
625 if (attributes.isWebGL2 && !extensions.supports("GL_ARB_texture_storage"))
626 return nullptr;
627#endif
628
629 std::unique_ptr<WebGLRenderingContextBase> renderingContext;
630#if ENABLE(WEBGL2)
631 if (type == "webgl2")
632 renderingContext = WebGL2RenderingContext::create(canvas, context.releaseNonNull(), attributes);
633 else
634#endif
635 renderingContext = WebGLRenderingContext::create(canvas, context.releaseNonNull(), attributes);
636 renderingContext->suspendIfNeeded();
637
638 return renderingContext;
639}
640
641WebGLRenderingContextBase::WebGLRenderingContextBase(CanvasBase& canvas, WebGLContextAttributes attributes)
642 : GPUBasedCanvasRenderingContext(canvas)
643 , m_dispatchContextLostEventTimer(*this, &WebGLRenderingContextBase::dispatchContextLostEvent)
644 , m_restoreTimer(*this, &WebGLRenderingContextBase::maybeRestoreContext)
645 , m_attributes(attributes)
646 , m_numGLErrorsToConsoleAllowed(maxGLErrorsAllowedToConsole)
647 , m_isPendingPolicyResolution(true)
648 , m_checkForContextLossHandlingTimer(*this, &WebGLRenderingContextBase::checkForContextLossHandling)
649{
650 registerWithWebGLStateTracker();
651 m_checkForContextLossHandlingTimer.startOneShot(checkContextLossHandlingDelay);
652}
653
654WebGLRenderingContextBase::WebGLRenderingContextBase(CanvasBase& canvas, Ref<GraphicsContext3D>&& context, WebGLContextAttributes attributes)
655 : GPUBasedCanvasRenderingContext(canvas)
656 , m_context(WTFMove(context))
657 , m_dispatchContextLostEventTimer(*this, &WebGLRenderingContextBase::dispatchContextLostEvent)
658 , m_restoreTimer(*this, &WebGLRenderingContextBase::maybeRestoreContext)
659 , m_generatedImageCache(4)
660 , m_attributes(attributes)
661 , m_numGLErrorsToConsoleAllowed(maxGLErrorsAllowedToConsole)
662 , m_checkForContextLossHandlingTimer(*this, &WebGLRenderingContextBase::checkForContextLossHandling)
663{
664 m_contextGroup = WebGLContextGroup::create();
665 m_contextGroup->addContext(*this);
666
667 m_context->addClient(*this);
668
669 m_context->getIntegerv(GraphicsContext3D::MAX_VIEWPORT_DIMS, m_maxViewportDims);
670
671 setupFlags();
672 initializeNewContext();
673 registerWithWebGLStateTracker();
674 m_checkForContextLossHandlingTimer.startOneShot(checkContextLossHandlingDelay);
675
676 addActivityStateChangeObserverIfNecessary();
677}
678
679WebGLCanvas WebGLRenderingContextBase::canvas()
680{
681 auto& base = canvasBase();
682 if (is<OffscreenCanvas>(base))
683 return &downcast<OffscreenCanvas>(base);
684 return &downcast<HTMLCanvasElement>(base);
685}
686
687HTMLCanvasElement* WebGLRenderingContextBase::htmlCanvas()
688{
689 auto& base = canvasBase();
690 if (!is<HTMLCanvasElement>(base))
691 return nullptr;
692 return &downcast<HTMLCanvasElement>(base);
693}
694
695OffscreenCanvas* WebGLRenderingContextBase::offscreenCanvas()
696{
697 auto& base = canvasBase();
698 if (!is<OffscreenCanvas>(base))
699 return nullptr;
700 return &downcast<OffscreenCanvas>(base);
701}
702
703// We check for context loss handling after a few seconds to give the JS a chance to register the event listeners
704// and to discard temporary GL contexts (e.g. feature detection).
705void WebGLRenderingContextBase::checkForContextLossHandling()
706{
707 auto canvas = htmlCanvas();
708 if (!canvas)
709 return;
710
711 if (!canvas->renderer())
712 return;
713
714 auto* page = canvas->document().page();
715 if (!page)
716 return;
717
718 bool handlesContextLoss = canvas->hasEventListeners(eventNames().webglcontextlostEvent) && canvas->hasEventListeners(eventNames().webglcontextrestoredEvent);
719 page->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::pageHandlesWebGLContextLossKey(), handlesContextLoss ? DiagnosticLoggingKeys::yesKey() : DiagnosticLoggingKeys::noKey(), ShouldSample::No);
720}
721
722void WebGLRenderingContextBase::registerWithWebGLStateTracker()
723{
724 auto canvas = htmlCanvas();
725 if (!canvas)
726 return;
727
728 auto* page = canvas->document().page();
729 if (!page)
730 return;
731
732 auto* tracker = page->webGLStateTracker();
733 if (!tracker)
734 return;
735
736 m_trackerToken = tracker->token(m_attributes.initialPowerPreference);
737}
738
739void WebGLRenderingContextBase::initializeNewContext()
740{
741 ASSERT(!m_contextLost);
742 m_needsUpdate = true;
743 m_markedCanvasDirty = false;
744 m_activeTextureUnit = 0;
745 m_packAlignment = 4;
746 m_unpackAlignment = 4;
747 m_unpackFlipY = false;
748 m_unpackPremultiplyAlpha = false;
749 m_unpackColorspaceConversion = GraphicsContext3D::BROWSER_DEFAULT_WEBGL;
750 m_boundArrayBuffer = nullptr;
751 m_currentProgram = nullptr;
752 m_framebufferBinding = nullptr;
753 m_readFramebufferBinding = nullptr;
754 m_renderbufferBinding = nullptr;
755 m_depthMask = true;
756 m_stencilEnabled = false;
757 m_stencilMask = 0xFFFFFFFF;
758 m_stencilMaskBack = 0xFFFFFFFF;
759 m_stencilFuncRef = 0;
760 m_stencilFuncRefBack = 0;
761 m_stencilFuncMask = 0xFFFFFFFF;
762 m_stencilFuncMaskBack = 0xFFFFFFFF;
763 m_layerCleared = false;
764 m_numGLErrorsToConsoleAllowed = maxGLErrorsAllowedToConsole;
765
766 m_clearColor[0] = m_clearColor[1] = m_clearColor[2] = m_clearColor[3] = 0;
767 m_scissorEnabled = false;
768 m_clearDepth = 1;
769 m_clearStencil = 0;
770 m_colorMask[0] = m_colorMask[1] = m_colorMask[2] = m_colorMask[3] = true;
771
772 GC3Dint numCombinedTextureImageUnits = 0;
773 m_context->getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits);
774 m_textureUnits.clear();
775 m_textureUnits.resize(numCombinedTextureImageUnits);
776 for (GC3Dint i = 0; i < numCombinedTextureImageUnits; ++i)
777 m_unrenderableTextureUnits.add(i);
778
779 GC3Dint numVertexAttribs = 0;
780 m_context->getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &numVertexAttribs);
781 m_maxVertexAttribs = numVertexAttribs;
782
783 m_maxTextureSize = 0;
784 m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize);
785 m_maxTextureLevel = WebGLTexture::computeLevelCount(m_maxTextureSize, m_maxTextureSize);
786 m_maxCubeMapTextureSize = 0;
787 m_context->getIntegerv(GraphicsContext3D::MAX_CUBE_MAP_TEXTURE_SIZE, &m_maxCubeMapTextureSize);
788 m_maxCubeMapTextureLevel = WebGLTexture::computeLevelCount(m_maxCubeMapTextureSize, m_maxCubeMapTextureSize);
789 m_maxRenderbufferSize = 0;
790 m_context->getIntegerv(GraphicsContext3D::MAX_RENDERBUFFER_SIZE, &m_maxRenderbufferSize);
791
792 // These two values from EXT_draw_buffers are lazily queried.
793 m_maxDrawBuffers = 0;
794 m_maxColorAttachments = 0;
795
796 m_backDrawBuffer = GraphicsContext3D::BACK;
797 m_drawBuffersWebGLRequirementsChecked = false;
798 m_drawBuffersSupported = false;
799
800 m_vertexAttribValue.resize(m_maxVertexAttribs);
801
802 if (!isGLES2NPOTStrict())
803 createFallbackBlackTextures1x1();
804
805 IntSize canvasSize = clampedCanvasSize();
806 m_context->reshape(canvasSize.width(), canvasSize.height());
807 m_context->viewport(0, 0, canvasSize.width(), canvasSize.height());
808 m_context->scissor(0, 0, canvasSize.width(), canvasSize.height());
809
810 m_context->setContextLostCallback(std::make_unique<WebGLRenderingContextLostCallback>(this));
811 m_context->setErrorMessageCallback(std::make_unique<WebGLRenderingContextErrorMessageCallback>(this));
812}
813
814void WebGLRenderingContextBase::setupFlags()
815{
816 ASSERT(m_context);
817
818 auto canvas = htmlCanvas();
819 if (canvas) {
820 if (Page* page = canvas->document().page())
821 m_synthesizedErrorsToConsole = page->settings().webGLErrorsToConsoleEnabled();
822 }
823
824 m_isGLES2Compliant = m_context->isGLES2Compliant();
825 if (m_isGLES2Compliant) {
826 m_isGLES2NPOTStrict = !m_context->getExtensions().isEnabled("GL_OES_texture_npot");
827 m_isDepthStencilSupported = m_context->getExtensions().isEnabled("GL_OES_packed_depth_stencil");
828 } else {
829 m_isGLES2NPOTStrict = !m_context->getExtensions().isEnabled("GL_ARB_texture_non_power_of_two");
830 m_isDepthStencilSupported = m_context->getExtensions().isEnabled("GL_EXT_packed_depth_stencil");
831 }
832 m_isRobustnessEXTSupported = m_context->getExtensions().isEnabled("GL_EXT_robustness");
833}
834
835void WebGLRenderingContextBase::addCompressedTextureFormat(GC3Denum format)
836{
837 if (!m_compressedTextureFormats.contains(format))
838 m_compressedTextureFormats.append(format);
839}
840
841void WebGLRenderingContextBase::addActivityStateChangeObserverIfNecessary()
842{
843 // We are only interested in visibility changes for contexts
844 // that are using the high-performance GPU.
845 if (!isHighPerformanceContext(m_context))
846 return;
847
848 auto* canvas = htmlCanvas();
849 if (!canvas)
850 return;
851
852 auto* page = canvas->document().page();
853 if (!page)
854 return;
855
856 page->addActivityStateChangeObserver(*this);
857
858 // We won't get a state change right away, so
859 // make sure the context knows if it visible or not.
860 if (m_context)
861 m_context->setContextVisibility(page->isVisible());
862}
863
864void WebGLRenderingContextBase::removeActivityStateChangeObserver()
865{
866 auto* canvas = htmlCanvas();
867 if (canvas) {
868 if (auto* page = canvas->document().page())
869 page->removeActivityStateChangeObserver(*this);
870 }
871}
872
873WebGLRenderingContextBase::~WebGLRenderingContextBase()
874{
875 // Remove all references to WebGLObjects so if they are the last reference
876 // they will be freed before the last context is removed from the context group.
877 m_boundArrayBuffer = nullptr;
878 m_defaultVertexArrayObject = nullptr;
879 m_boundVertexArrayObject = nullptr;
880 m_vertexAttrib0Buffer = nullptr;
881 m_currentProgram = nullptr;
882 m_framebufferBinding = nullptr;
883 m_readFramebufferBinding = nullptr;
884 m_renderbufferBinding = nullptr;
885
886 for (auto& textureUnit : m_textureUnits) {
887 textureUnit.texture2DBinding = nullptr;
888 textureUnit.textureCubeMapBinding = nullptr;
889 }
890
891 m_blackTexture2D = nullptr;
892 m_blackTextureCubeMap = nullptr;
893
894 if (!m_isPendingPolicyResolution) {
895 detachAndRemoveAllObjects();
896 destroyGraphicsContext3D();
897 m_contextGroup->removeContext(*this);
898 }
899
900 {
901 LockHolder lock(WebGLProgram::instancesMutex());
902 for (auto& entry : WebGLProgram::instances(lock)) {
903 if (entry.value == this) {
904 // Don't remove any WebGLProgram from the instances list, as they may still exist.
905 // Only remove the association with a WebGL context.
906 entry.value = nullptr;
907 }
908 }
909 }
910}
911
912void WebGLRenderingContextBase::destroyGraphicsContext3D()
913{
914 if (m_isPendingPolicyResolution)
915 return;
916
917 removeActivityStateChangeObserver();
918
919 if (m_context) {
920 m_context->removeClient(*this);
921 m_context->setContextLostCallback(nullptr);
922 m_context->setErrorMessageCallback(nullptr);
923 m_context = nullptr;
924 }
925}
926
927void WebGLRenderingContextBase::markContextChanged()
928{
929 if (m_framebufferBinding)
930 return;
931
932 m_context->markContextChanged();
933
934 m_layerCleared = false;
935
936 auto* canvas = htmlCanvas();
937 if (!canvas)
938 return;
939
940 RenderBox* renderBox = canvas->renderBox();
941 if (isAccelerated() && renderBox && renderBox->hasAcceleratedCompositing()) {
942 m_markedCanvasDirty = true;
943 htmlCanvas()->clearCopiedImage();
944 renderBox->contentChanged(CanvasPixelsChanged);
945 } else {
946 if (!m_markedCanvasDirty) {
947 m_markedCanvasDirty = true;
948 canvas->didDraw(FloatRect(FloatPoint(0, 0), clampedCanvasSize()));
949 }
950 }
951}
952
953void WebGLRenderingContextBase::markContextChangedAndNotifyCanvasObserver()
954{
955 markContextChanged();
956 if (!isAccelerated())
957 return;
958
959 auto* canvas = htmlCanvas();
960 if (!canvas)
961 return;
962
963 RenderBox* renderBox = canvas->renderBox();
964 if (renderBox && renderBox->hasAcceleratedCompositing())
965 canvas->notifyObserversCanvasChanged(FloatRect(FloatPoint(0, 0), clampedCanvasSize()));
966}
967
968bool WebGLRenderingContextBase::clearIfComposited(GC3Dbitfield mask)
969{
970 if (isContextLostOrPending())
971 return false;
972
973 if (!m_context->layerComposited() || m_layerCleared
974 || m_attributes.preserveDrawingBuffer || (mask && m_framebufferBinding)
975 || m_preventBufferClearForInspector)
976 return false;
977
978 auto contextAttributes = getContextAttributes();
979 ASSERT(contextAttributes);
980
981 // Determine if it's possible to combine the clear the user asked for and this clear.
982 bool combinedClear = mask && !m_scissorEnabled;
983
984 m_context->disable(GraphicsContext3D::SCISSOR_TEST);
985 if (combinedClear && (mask & GraphicsContext3D::COLOR_BUFFER_BIT))
986 m_context->clearColor(m_colorMask[0] ? m_clearColor[0] : 0,
987 m_colorMask[1] ? m_clearColor[1] : 0,
988 m_colorMask[2] ? m_clearColor[2] : 0,
989 m_colorMask[3] ? m_clearColor[3] : 0);
990 else
991 m_context->clearColor(0, 0, 0, 0);
992 m_context->colorMask(true, true, true, true);
993 GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT;
994 if (contextAttributes->depth) {
995 if (!combinedClear || !m_depthMask || !(mask & GraphicsContext3D::DEPTH_BUFFER_BIT))
996 m_context->clearDepth(1.0f);
997 clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
998 m_context->depthMask(true);
999 }
1000 if (contextAttributes->stencil) {
1001 if (combinedClear && (mask & GraphicsContext3D::STENCIL_BUFFER_BIT))
1002 m_context->clearStencil(m_clearStencil & m_stencilMask);
1003 else
1004 m_context->clearStencil(0);
1005 clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
1006 m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF);
1007 }
1008 if (m_framebufferBinding)
1009 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0);
1010 m_context->clear(clearMask);
1011
1012 restoreStateAfterClear();
1013 if (m_framebufferBinding)
1014 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, objectOrZero(m_framebufferBinding.get()));
1015 m_layerCleared = true;
1016
1017 return combinedClear;
1018}
1019
1020void WebGLRenderingContextBase::restoreStateAfterClear()
1021{
1022 // Restore the state that the context set.
1023 if (m_scissorEnabled)
1024 m_context->enable(GraphicsContext3D::SCISSOR_TEST);
1025 m_context->clearColor(m_clearColor[0], m_clearColor[1],
1026 m_clearColor[2], m_clearColor[3]);
1027 m_context->colorMask(m_colorMask[0], m_colorMask[1],
1028 m_colorMask[2], m_colorMask[3]);
1029 m_context->clearDepth(m_clearDepth);
1030 m_context->clearStencil(m_clearStencil);
1031 m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, m_stencilMask);
1032 m_context->depthMask(m_depthMask);
1033}
1034
1035void WebGLRenderingContextBase::markLayerComposited()
1036{
1037 if (isContextLostOrPending())
1038 return;
1039 m_context->markLayerComposited();
1040}
1041
1042void WebGLRenderingContextBase::paintRenderingResultsToCanvas()
1043{
1044 if (isContextLostOrPending())
1045 return;
1046
1047 auto* canvas = htmlCanvas();
1048 if (!canvas)
1049 return;
1050
1051 if (canvas->document().printing())
1052 canvas->clearPresentationCopy();
1053
1054 // Until the canvas is written to by the application, the clear that
1055 // happened after it was composited should be ignored by the compositor.
1056 if (m_context->layerComposited() && !m_attributes.preserveDrawingBuffer) {
1057 m_context->paintCompositedResultsToCanvas(canvas->buffer());
1058
1059 canvas->makePresentationCopy();
1060 } else
1061 canvas->clearPresentationCopy();
1062 clearIfComposited();
1063
1064 if (!m_markedCanvasDirty && !m_layerCleared)
1065 return;
1066
1067 canvas->clearCopiedImage();
1068 m_markedCanvasDirty = false;
1069
1070 m_context->paintRenderingResultsToCanvas(canvas->buffer());
1071}
1072
1073RefPtr<ImageData> WebGLRenderingContextBase::paintRenderingResultsToImageData()
1074{
1075 if (isContextLostOrPending())
1076 return nullptr;
1077 clearIfComposited();
1078 return m_context->paintRenderingResultsToImageData();
1079}
1080
1081WebGLTexture::TextureExtensionFlag WebGLRenderingContextBase::textureExtensionFlags() const
1082{
1083 return static_cast<WebGLTexture::TextureExtensionFlag>((m_oesTextureFloatLinear ? WebGLTexture::TextureExtensionFloatLinearEnabled : 0) | (m_oesTextureHalfFloatLinear ? WebGLTexture::TextureExtensionHalfFloatLinearEnabled : 0));
1084}
1085
1086void WebGLRenderingContextBase::reshape(int width, int height)
1087{
1088 if (isContextLostOrPending())
1089 return;
1090
1091 // This is an approximation because at WebGLRenderingContext level we don't
1092 // know if the underlying FBO uses textures or renderbuffers.
1093 GC3Dint maxSize = std::min(m_maxTextureSize, m_maxRenderbufferSize);
1094 GC3Dint maxWidth = std::min(maxSize, m_maxViewportDims[0]);
1095 GC3Dint maxHeight = std::min(maxSize, m_maxViewportDims[1]);
1096 width = clamp(width, 1, maxWidth);
1097 height = clamp(height, 1, maxHeight);
1098
1099 if (m_needsUpdate) {
1100 auto* canvas = htmlCanvas();
1101 if (canvas) {
1102 RenderBox* renderBox = htmlCanvas()->renderBox();
1103 if (renderBox && renderBox->hasAcceleratedCompositing())
1104 renderBox->contentChanged(CanvasChanged);
1105 }
1106 m_needsUpdate = false;
1107 }
1108
1109 // We don't have to mark the canvas as dirty, since the newly created image buffer will also start off
1110 // clear (and this matches what reshape will do).
1111 m_context->reshape(width, height);
1112
1113 auto& textureUnit = m_textureUnits[m_activeTextureUnit];
1114 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, objectOrZero(textureUnit.texture2DBinding.get()));
1115 if (textureUnit.texture2DBinding && textureUnit.texture2DBinding->needToUseBlackTexture(textureExtensionFlags()))
1116 m_unrenderableTextureUnits.add(m_activeTextureUnit);
1117 m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, objectOrZero(m_renderbufferBinding.get()));
1118 if (m_framebufferBinding)
1119 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, objectOrZero(m_framebufferBinding.get()));
1120}
1121
1122int WebGLRenderingContextBase::drawingBufferWidth() const
1123{
1124 if (isContextLost())
1125 return 0;
1126
1127 if (m_isPendingPolicyResolution && !m_hasRequestedPolicyResolution)
1128 return 0;
1129
1130 return m_context->getInternalFramebufferSize().width();
1131}
1132
1133int WebGLRenderingContextBase::drawingBufferHeight() const
1134{
1135 if (isContextLost())
1136 return 0;
1137
1138 if (m_isPendingPolicyResolution && !m_hasRequestedPolicyResolution)
1139 return 0;
1140
1141 return m_context->getInternalFramebufferSize().height();
1142}
1143
1144unsigned WebGLRenderingContextBase::sizeInBytes(GC3Denum type)
1145{
1146 switch (type) {
1147 case GraphicsContext3D::BYTE:
1148 return sizeof(GC3Dbyte);
1149 case GraphicsContext3D::UNSIGNED_BYTE:
1150 return sizeof(GC3Dubyte);
1151 case GraphicsContext3D::SHORT:
1152 return sizeof(GC3Dshort);
1153 case GraphicsContext3D::UNSIGNED_SHORT:
1154 return sizeof(GC3Dushort);
1155 case GraphicsContext3D::INT:
1156 return sizeof(GC3Dint);
1157 case GraphicsContext3D::UNSIGNED_INT:
1158 return sizeof(GC3Duint);
1159 case GraphicsContext3D::FLOAT:
1160 return sizeof(GC3Dfloat);
1161 }
1162 ASSERT_NOT_REACHED();
1163 return 0;
1164}
1165
1166void WebGLRenderingContextBase::activeTexture(GC3Denum texture)
1167{
1168 if (isContextLostOrPending())
1169 return;
1170 if (texture - GraphicsContext3D::TEXTURE0 >= m_textureUnits.size()) {
1171 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "activeTexture", "texture unit out of range");
1172 return;
1173 }
1174 m_activeTextureUnit = texture - GraphicsContext3D::TEXTURE0;
1175 m_context->activeTexture(texture);
1176}
1177
1178void WebGLRenderingContextBase::attachShader(WebGLProgram* program, WebGLShader* shader)
1179{
1180 if (isContextLostOrPending() || !validateWebGLObject("attachShader", program) || !validateWebGLObject("attachShader", shader))
1181 return;
1182 if (!program->attachShader(shader)) {
1183 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "attachShader", "shader attachment already has shader");
1184 return;
1185 }
1186 m_context->attachShader(objectOrZero(program), objectOrZero(shader));
1187 shader->onAttached();
1188}
1189
1190void WebGLRenderingContextBase::bindAttribLocation(WebGLProgram* program, GC3Duint index, const String& name)
1191{
1192 if (isContextLostOrPending() || !validateWebGLObject("bindAttribLocation", program))
1193 return;
1194 if (!validateLocationLength("bindAttribLocation", name))
1195 return;
1196 if (!validateString("bindAttribLocation", name))
1197 return;
1198 if (isPrefixReserved(name)) {
1199 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "bindAttribLocation", "reserved prefix");
1200 return;
1201 }
1202 if (index >= m_maxVertexAttribs) {
1203 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bindAttribLocation", "index out of range");
1204 return;
1205 }
1206 m_context->bindAttribLocation(objectOrZero(program), index, name);
1207}
1208
1209bool WebGLRenderingContextBase::checkObjectToBeBound(const char* functionName, WebGLObject* object, bool& deleted)
1210{
1211 deleted = false;
1212 if (isContextLostOrPending())
1213 return false;
1214 if (object) {
1215 if (!object->validate(contextGroup(), *this)) {
1216 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "object not from this context");
1217 return false;
1218 }
1219 deleted = !object->object();
1220 }
1221 return true;
1222}
1223
1224void WebGLRenderingContextBase::bindBuffer(GC3Denum target, WebGLBuffer* buffer)
1225{
1226 bool deleted;
1227 if (!checkObjectToBeBound("bindBuffer", buffer, deleted))
1228 return;
1229 if (deleted)
1230 buffer = nullptr;
1231 if (buffer && buffer->getTarget() && buffer->getTarget() != target) {
1232 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "bindBuffer", "buffers can not be used with multiple targets");
1233 return;
1234 }
1235 if (target == GraphicsContext3D::ARRAY_BUFFER)
1236 m_boundArrayBuffer = buffer;
1237 else if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER)
1238 m_boundVertexArrayObject->setElementArrayBuffer(buffer);
1239 else {
1240 bool success = false;
1241#if ENABLE(WEBGL2)
1242 if (isWebGL2()) {
1243 success = true;
1244 switch (target) {
1245 case GraphicsContext3D::COPY_READ_BUFFER:
1246 m_boundCopyReadBuffer = buffer;
1247 break;
1248 case GraphicsContext3D::COPY_WRITE_BUFFER:
1249 m_boundCopyWriteBuffer = buffer;
1250 break;
1251 case GraphicsContext3D::PIXEL_PACK_BUFFER:
1252 m_boundPixelPackBuffer = buffer;
1253 break;
1254 case GraphicsContext3D::PIXEL_UNPACK_BUFFER:
1255 m_boundPixelUnpackBuffer = buffer;
1256 break;
1257 case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER:
1258 m_boundTransformFeedbackBuffer = buffer;
1259 break;
1260 case GraphicsContext3D::UNIFORM_BUFFER:
1261 m_boundUniformBuffer = buffer;
1262 break;
1263 default:
1264 success = false;
1265 break;
1266 }
1267 }
1268#endif
1269 if (!success) {
1270 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "bindBuffer", "invalid target");
1271 return;
1272 }
1273 }
1274
1275 m_context->bindBuffer(target, objectOrZero(buffer));
1276 if (buffer)
1277 buffer->setTarget(target, isWebGL2());
1278}
1279
1280void WebGLRenderingContextBase::bindFramebuffer(GC3Denum target, WebGLFramebuffer* buffer)
1281{
1282 bool deleted;
1283 if (!checkObjectToBeBound("bindFramebuffer", buffer, deleted))
1284 return;
1285 if (deleted)
1286 buffer = 0;
1287
1288 bool isWebGL2DrawFramebufferTarget = false;
1289#if ENABLE(WEBGL2)
1290 isWebGL2DrawFramebufferTarget = isWebGL2() && target == GraphicsContext3D::DRAW_FRAMEBUFFER;
1291#endif
1292 bool success = false;
1293
1294 if (target == GraphicsContext3D::FRAMEBUFFER || isWebGL2DrawFramebufferTarget) {
1295 m_framebufferBinding = buffer;
1296 success = true;
1297 }
1298#if ENABLE(WEBGL2)
1299 if (isWebGL2() && (target == GraphicsContext3D::FRAMEBUFFER || target == GraphicsContext3D::READ_FRAMEBUFFER)) {
1300 m_readFramebufferBinding = buffer;
1301 success = true;
1302 }
1303#endif
1304
1305 if (!success) {
1306 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "bindFramebuffer", "invalid target");
1307 return;
1308 }
1309
1310 m_context->bindFramebuffer(target, objectOrZero(buffer));
1311 if (buffer)
1312 buffer->setHasEverBeenBound();
1313 applyStencilTest();
1314}
1315
1316void WebGLRenderingContextBase::bindRenderbuffer(GC3Denum target, WebGLRenderbuffer* renderBuffer)
1317{
1318 bool deleted;
1319 if (!checkObjectToBeBound("bindRenderbuffer", renderBuffer, deleted))
1320 return;
1321 if (deleted)
1322 renderBuffer = 0;
1323 if (target != GraphicsContext3D::RENDERBUFFER) {
1324 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "bindRenderbuffer", "invalid target");
1325 return;
1326 }
1327 m_renderbufferBinding = renderBuffer;
1328 m_context->bindRenderbuffer(target, objectOrZero(renderBuffer));
1329 if (renderBuffer)
1330 renderBuffer->setHasEverBeenBound();
1331}
1332
1333void WebGLRenderingContextBase::bindTexture(GC3Denum target, WebGLTexture* texture)
1334{
1335 bool deleted;
1336 if (!checkObjectToBeBound("bindTexture", texture, deleted))
1337 return;
1338 if (deleted)
1339 texture = nullptr;
1340 if (texture && texture->getTarget() && texture->getTarget() != target) {
1341 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "bindTexture", "textures can not be used with multiple targets");
1342 return;
1343 }
1344 GC3Dint maxLevel = 0;
1345 auto& textureUnit = m_textureUnits[m_activeTextureUnit];
1346 if (target == GraphicsContext3D::TEXTURE_2D) {
1347 textureUnit.texture2DBinding = texture;
1348 maxLevel = m_maxTextureLevel;
1349 if (texture && texture->needToUseBlackTexture(textureExtensionFlags()))
1350 m_unrenderableTextureUnits.add(m_activeTextureUnit);
1351 else
1352 m_unrenderableTextureUnits.remove(m_activeTextureUnit);
1353 } else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
1354 textureUnit.textureCubeMapBinding = texture;
1355 maxLevel = m_maxCubeMapTextureLevel;
1356 if (texture && texture->needToUseBlackTexture(textureExtensionFlags()))
1357 m_unrenderableTextureUnits.add(m_activeTextureUnit);
1358 else
1359 m_unrenderableTextureUnits.remove(m_activeTextureUnit);
1360 } else {
1361 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "bindTexture", "invalid target");
1362 return;
1363 }
1364 m_context->bindTexture(target, objectOrZero(texture));
1365 if (texture)
1366 texture->setTarget(target, maxLevel);
1367
1368 // Note: previously we used to automatically set the TEXTURE_WRAP_R
1369 // repeat mode to CLAMP_TO_EDGE for cube map textures, because OpenGL
1370 // ES 2.0 doesn't expose this flag (a bug in the specification) and
1371 // otherwise the application has no control over the seams in this
1372 // dimension. However, it appears that supporting this properly on all
1373 // platforms is fairly involved (will require a HashMap from texture ID
1374 // in all ports), and we have not had any complaints, so the logic has
1375 // been removed.
1376}
1377
1378void WebGLRenderingContextBase::blendColor(GC3Dfloat red, GC3Dfloat green, GC3Dfloat blue, GC3Dfloat alpha)
1379{
1380 if (isContextLostOrPending())
1381 return;
1382 m_context->blendColor(red, green, blue, alpha);
1383}
1384
1385void WebGLRenderingContextBase::blendEquation(GC3Denum mode)
1386{
1387 if (isContextLostOrPending() || !validateBlendEquation("blendEquation", mode))
1388 return;
1389 m_context->blendEquation(mode);
1390}
1391
1392void WebGLRenderingContextBase::blendEquationSeparate(GC3Denum modeRGB, GC3Denum modeAlpha)
1393{
1394 if (isContextLostOrPending() || !validateBlendEquation("blendEquation", modeRGB) || !validateBlendEquation("blendEquation", modeAlpha))
1395 return;
1396 m_context->blendEquationSeparate(modeRGB, modeAlpha);
1397}
1398
1399
1400void WebGLRenderingContextBase::blendFunc(GC3Denum sfactor, GC3Denum dfactor)
1401{
1402 if (isContextLostOrPending() || !validateBlendFuncFactors("blendFunc", sfactor, dfactor))
1403 return;
1404 m_context->blendFunc(sfactor, dfactor);
1405}
1406
1407void WebGLRenderingContextBase::blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, GC3Denum srcAlpha, GC3Denum dstAlpha)
1408{
1409 // Note: Alpha does not have the same restrictions as RGB.
1410 if (isContextLostOrPending() || !validateBlendFuncFactors("blendFunc", srcRGB, dstRGB))
1411 return;
1412 m_context->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
1413}
1414
1415void WebGLRenderingContextBase::bufferData(GC3Denum target, long long size, GC3Denum usage)
1416{
1417 if (isContextLostOrPending())
1418 return;
1419 RefPtr<WebGLBuffer> buffer = validateBufferDataParameters("bufferData", target, usage);
1420 if (!buffer)
1421 return;
1422 if (size < 0) {
1423 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferData", "size < 0");
1424 return;
1425 }
1426 if (!size) {
1427 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferData", "size == 0");
1428 return;
1429 }
1430 if (!buffer->associateBufferData(static_cast<GC3Dsizeiptr>(size))) {
1431 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferData", "invalid buffer");
1432 return;
1433 }
1434
1435 m_context->moveErrorsToSyntheticErrorList();
1436 m_context->bufferData(target, static_cast<GC3Dsizeiptr>(size), usage);
1437 if (m_context->moveErrorsToSyntheticErrorList()) {
1438 // The bufferData function failed. Tell the buffer it doesn't have the data it thinks it does.
1439 buffer->disassociateBufferData();
1440 }
1441}
1442
1443void WebGLRenderingContextBase::bufferData(GC3Denum target, Optional<BufferDataSource>&& data, GC3Denum usage)
1444{
1445 if (isContextLostOrPending())
1446 return;
1447 if (!data) {
1448 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferData", "null data");
1449 return;
1450 }
1451 RefPtr<WebGLBuffer> buffer = validateBufferDataParameters("bufferData", target, usage);
1452 if (!buffer)
1453 return;
1454
1455 WTF::visit([&](auto& data) {
1456 if (!buffer->associateBufferData(data.get())) {
1457 this->synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferData", "invalid buffer");
1458 return;
1459 }
1460
1461 m_context->moveErrorsToSyntheticErrorList();
1462 m_context->bufferData(target, data->byteLength(), data->data(), usage);
1463 if (m_context->moveErrorsToSyntheticErrorList()) {
1464 // The bufferData function failed. Tell the buffer it doesn't have the data it thinks it does.
1465 buffer->disassociateBufferData();
1466 }
1467 }, data.value());
1468}
1469
1470void WebGLRenderingContextBase::bufferSubData(GC3Denum target, long long offset, Optional<BufferDataSource>&& data)
1471{
1472 if (isContextLostOrPending())
1473 return;
1474 RefPtr<WebGLBuffer> buffer = validateBufferDataParameters("bufferSubData", target, GraphicsContext3D::STATIC_DRAW);
1475 if (!buffer)
1476 return;
1477 if (offset < 0) {
1478 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferSubData", "offset < 0");
1479 return;
1480 }
1481 if (!data)
1482 return;
1483
1484 WTF::visit([&](auto& data) {
1485 if (!buffer->associateBufferSubData(static_cast<GC3Dintptr>(offset), data.get())) {
1486 this->synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "bufferSubData", "offset out of range");
1487 return;
1488 }
1489
1490 m_context->moveErrorsToSyntheticErrorList();
1491 m_context->bufferSubData(target, static_cast<GC3Dintptr>(offset), data->byteLength(), data->data());
1492 if (m_context->moveErrorsToSyntheticErrorList()) {
1493 // The bufferSubData function failed. Tell the buffer it doesn't have the data it thinks it does.
1494 buffer->disassociateBufferData();
1495 }
1496 }, data.value());
1497}
1498
1499GC3Denum WebGLRenderingContextBase::checkFramebufferStatus(GC3Denum target)
1500{
1501 if (isContextLostOrPending())
1502 return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
1503 if (target != GraphicsContext3D::FRAMEBUFFER) {
1504#if ENABLE(WEBGL2)
1505 if (isWebGL1() || (target != GraphicsContext3D::DRAW_FRAMEBUFFER && target != GraphicsContext3D::READ_FRAMEBUFFER)) {
1506#endif
1507 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "checkFramebufferStatus", "invalid target");
1508 return 0;
1509#if ENABLE(WEBGL2)
1510 }
1511#endif
1512 }
1513
1514 auto targetFramebuffer = (target == GraphicsContext3D::READ_FRAMEBUFFER) ? m_readFramebufferBinding : m_framebufferBinding;
1515
1516 if (!targetFramebuffer || !targetFramebuffer->object())
1517 return GraphicsContext3D::FRAMEBUFFER_COMPLETE;
1518 const char* reason = "framebuffer incomplete";
1519 GC3Denum result = targetFramebuffer->checkStatus(&reason);
1520 if (result != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
1521 String str = "WebGL: checkFramebufferStatus:" + String(reason);
1522 printToConsole(MessageLevel::Warning, str);
1523 return result;
1524 }
1525 result = m_context->checkFramebufferStatus(target);
1526 return result;
1527}
1528
1529void WebGLRenderingContextBase::clearColor(GC3Dfloat r, GC3Dfloat g, GC3Dfloat b, GC3Dfloat a)
1530{
1531 if (isContextLostOrPending())
1532 return;
1533 if (std::isnan(r))
1534 r = 0;
1535 if (std::isnan(g))
1536 g = 0;
1537 if (std::isnan(b))
1538 b = 0;
1539 if (std::isnan(a))
1540 a = 1;
1541 m_clearColor[0] = r;
1542 m_clearColor[1] = g;
1543 m_clearColor[2] = b;
1544 m_clearColor[3] = a;
1545 m_context->clearColor(r, g, b, a);
1546}
1547
1548void WebGLRenderingContextBase::clearDepth(GC3Dfloat depth)
1549{
1550 if (isContextLostOrPending())
1551 return;
1552 m_clearDepth = depth;
1553 m_context->clearDepth(depth);
1554}
1555
1556void WebGLRenderingContextBase::clearStencil(GC3Dint s)
1557{
1558 if (isContextLostOrPending())
1559 return;
1560 m_clearStencil = s;
1561 m_context->clearStencil(s);
1562}
1563
1564void WebGLRenderingContextBase::colorMask(GC3Dboolean red, GC3Dboolean green, GC3Dboolean blue, GC3Dboolean alpha)
1565{
1566 if (isContextLostOrPending())
1567 return;
1568 m_colorMask[0] = red;
1569 m_colorMask[1] = green;
1570 m_colorMask[2] = blue;
1571 m_colorMask[3] = alpha;
1572 m_context->colorMask(red, green, blue, alpha);
1573}
1574
1575void WebGLRenderingContextBase::compileShader(WebGLShader* shader)
1576{
1577 if (isContextLostOrPending() || !validateWebGLObject("compileShader", shader))
1578 return;
1579 m_context->compileShader(objectOrZero(shader));
1580 GC3Dint value;
1581 m_context->getShaderiv(objectOrZero(shader), GraphicsContext3D::COMPILE_STATUS, &value);
1582 shader->setValid(value);
1583
1584 auto* canvas = htmlCanvas();
1585
1586 if (canvas && m_synthesizedErrorsToConsole && !value) {
1587 Ref<Inspector::ScriptCallStack> stackTrace = Inspector::createScriptCallStack(JSExecState::currentState());
1588
1589 for (auto& error : getShaderInfoLog(shader).split('\n'))
1590 canvas->document().addConsoleMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::Rendering, MessageType::Log, MessageLevel::Error, "WebGL: " + error, stackTrace.copyRef()));
1591 }
1592}
1593
1594void WebGLRenderingContextBase::compressedTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, ArrayBufferView& data)
1595{
1596 if (isContextLostOrPending())
1597 return;
1598 if (!validateTexFuncLevel("compressedTexImage2D", target, level))
1599 return;
1600
1601 if (!validateCompressedTexFormat(internalformat)) {
1602 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "compressedTexImage2D", "invalid internalformat");
1603 return;
1604 }
1605 if (border) {
1606 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "compressedTexImage2D", "border not 0");
1607 return;
1608 }
1609 if (!validateCompressedTexDimensions("compressedTexImage2D", target, level, width, height, internalformat))
1610 return;
1611 if (!validateCompressedTexFuncData("compressedTexImage2D", width, height, internalformat, data))
1612 return;
1613
1614 auto tex = validateTextureBinding("compressedTexImage2D", target, true);
1615 if (!tex)
1616 return;
1617 if (!validateNPOTTextureLevel(width, height, level, "compressedTexImage2D"))
1618 return;
1619 m_context->moveErrorsToSyntheticErrorList();
1620 m_context->compressedTexImage2D(target, level, internalformat, width, height,
1621 border, data.byteLength(), data.baseAddress());
1622 if (m_context->moveErrorsToSyntheticErrorList()) {
1623 // The compressedTexImage2D function failed. Tell the WebGLTexture it doesn't have the data for this level.
1624 tex->markInvalid(target, level);
1625 return;
1626 }
1627
1628 tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
1629 tex->setCompressed();
1630}
1631
1632void WebGLRenderingContextBase::compressedTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dsizei width, GC3Dsizei height, GC3Denum format, ArrayBufferView& data)
1633{
1634 if (isContextLostOrPending())
1635 return;
1636 if (!validateTexFuncLevel("compressedTexSubImage2D", target, level))
1637 return;
1638 if (!validateCompressedTexFormat(format)) {
1639 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "compressedTexSubImage2D", "invalid format");
1640 return;
1641 }
1642 if (!validateCompressedTexFuncData("compressedTexSubImage2D", width, height, format, data))
1643 return;
1644
1645 auto tex = validateTextureBinding("compressedTexSubImage2D", target, true);
1646 if (!tex)
1647 return;
1648
1649 if (format != tex->getInternalFormat(target, level)) {
1650 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "compressedTexSubImage2D", "format does not match texture format");
1651 return;
1652 }
1653
1654 if (!validateCompressedTexSubDimensions("compressedTexSubImage2D", target, level, xoffset, yoffset, width, height, format, tex.get()))
1655 return;
1656
1657 graphicsContext3D()->compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, data.byteLength(), data.baseAddress());
1658 tex->setCompressed();
1659}
1660
1661bool WebGLRenderingContextBase::validateSettableTexInternalFormat(const char* functionName, GC3Denum internalFormat)
1662{
1663 switch (internalFormat) {
1664 case GraphicsContext3D::DEPTH_COMPONENT:
1665 case GraphicsContext3D::DEPTH_STENCIL:
1666 case GraphicsContext3D::DEPTH_COMPONENT16:
1667 case GraphicsContext3D::DEPTH_COMPONENT24:
1668 case GraphicsContext3D::DEPTH_COMPONENT32F:
1669 case GraphicsContext3D::DEPTH24_STENCIL8:
1670 case GraphicsContext3D::DEPTH32F_STENCIL8:
1671 case GraphicsContext3D::STENCIL_INDEX8:
1672 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "format can not be set, only rendered to");
1673 return false;
1674 default:
1675 return true;
1676 }
1677}
1678
1679void WebGLRenderingContextBase::copyTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height)
1680{
1681 if (isContextLostOrPending())
1682 return;
1683 if (!validateTexFuncLevel("copyTexSubImage2D", target, level))
1684 return;
1685 auto tex = validateTextureBinding("copyTexSubImage2D", target, true);
1686 if (!tex)
1687 return;
1688 if (!validateSize("copyTexSubImage2D", xoffset, yoffset) || !validateSize("copyTexSubImage2D", width, height))
1689 return;
1690 // Before checking if it is in the range, check if overflow happens first.
1691 if (xoffset + width < 0 || yoffset + height < 0) {
1692 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "copyTexSubImage2D", "bad dimensions");
1693 return;
1694 }
1695 if (xoffset + width > tex->getWidth(target, level) || yoffset + height > tex->getHeight(target, level)) {
1696 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "copyTexSubImage2D", "rectangle out of range");
1697 return;
1698 }
1699 GC3Denum internalFormat = tex->getInternalFormat(target, level);
1700 if (!validateSettableTexInternalFormat("copyTexSubImage2D", internalFormat))
1701 return;
1702 if (!isTexInternalFormatColorBufferCombinationValid(internalFormat, getBoundFramebufferColorFormat())) {
1703 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "copyTexSubImage2D", "framebuffer is incompatible format");
1704 return;
1705 }
1706 const char* reason = "framebuffer incomplete";
1707 if (m_framebufferBinding && !m_framebufferBinding->onAccess(graphicsContext3D(), &reason)) {
1708 synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION, "copyTexSubImage2D", reason);
1709 return;
1710 }
1711 clearIfComposited();
1712
1713 GC3Dint clippedX, clippedY;
1714 GC3Dsizei clippedWidth, clippedHeight;
1715 if (clip2D(x, y, width, height, getBoundFramebufferWidth(), getBoundFramebufferHeight(), &clippedX, &clippedY, &clippedWidth, &clippedHeight)) {
1716 GC3Denum format;
1717 GC3Denum type;
1718 if (!GraphicsContext3D::possibleFormatAndTypeForInternalFormat(tex->getInternalFormat(target, level), format, type)) {
1719 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "copyTexSubImage2D", "Texture has unknown internal format");
1720 return;
1721 }
1722 UniqueArray<unsigned char> zero;
1723 if (width && height) {
1724 unsigned size;
1725 GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &size, nullptr);
1726 if (error != GraphicsContext3D::NO_ERROR) {
1727 synthesizeGLError(error, "copyTexSubImage2D", "bad dimensions");
1728 return;
1729 }
1730 zero = makeUniqueArray<unsigned char>(size);
1731 if (!zero) {
1732 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "copyTexSubImage2D", "out of memory");
1733 return;
1734 }
1735 memset(zero.get(), 0, size);
1736 }
1737 m_context->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, zero.get());
1738 if (clippedWidth > 0 && clippedHeight > 0)
1739 m_context->copyTexSubImage2D(target, level, xoffset + clippedX - x, yoffset + clippedY - y, clippedX, clippedY, clippedWidth, clippedHeight);
1740 } else
1741 m_context->copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
1742}
1743
1744RefPtr<WebGLBuffer> WebGLRenderingContextBase::createBuffer()
1745{
1746 if (isContextLostOrPending())
1747 return nullptr;
1748 auto buffer = WebGLBuffer::create(*this);
1749 addSharedObject(buffer.get());
1750 return buffer;
1751}
1752
1753RefPtr<WebGLFramebuffer> WebGLRenderingContextBase::createFramebuffer()
1754{
1755 if (isContextLostOrPending())
1756 return nullptr;
1757 auto buffer = WebGLFramebuffer::create(*this);
1758 addContextObject(buffer.get());
1759 return buffer;
1760}
1761
1762RefPtr<WebGLTexture> WebGLRenderingContextBase::createTexture()
1763{
1764 if (isContextLostOrPending())
1765 return nullptr;
1766 auto texture = WebGLTexture::create(*this);
1767 addSharedObject(texture.get());
1768 return texture;
1769}
1770
1771RefPtr<WebGLProgram> WebGLRenderingContextBase::createProgram()
1772{
1773 if (isContextLostOrPending())
1774 return nullptr;
1775 auto program = WebGLProgram::create(*this);
1776 addSharedObject(program.get());
1777
1778 InspectorInstrumentation::didCreateProgram(*this, program.get());
1779
1780 return program;
1781}
1782
1783RefPtr<WebGLRenderbuffer> WebGLRenderingContextBase::createRenderbuffer()
1784{
1785 if (isContextLostOrPending())
1786 return nullptr;
1787 auto buffer = WebGLRenderbuffer::create(*this);
1788 addSharedObject(buffer.get());
1789 return buffer;
1790}
1791
1792RefPtr<WebGLShader> WebGLRenderingContextBase::createShader(GC3Denum type)
1793{
1794 if (isContextLostOrPending())
1795 return nullptr;
1796 if (type != GraphicsContext3D::VERTEX_SHADER && type != GraphicsContext3D::FRAGMENT_SHADER) {
1797 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "createShader", "invalid shader type");
1798 return nullptr;
1799 }
1800
1801 auto shader = WebGLShader::create(*this, type);
1802 addSharedObject(shader.get());
1803 return shader;
1804}
1805
1806void WebGLRenderingContextBase::cullFace(GC3Denum mode)
1807{
1808 if (isContextLostOrPending())
1809 return;
1810 m_context->cullFace(mode);
1811}
1812
1813bool WebGLRenderingContextBase::deleteObject(WebGLObject* object)
1814{
1815 if (isContextLostOrPending() || !object)
1816 return false;
1817 if (!object->validate(contextGroup(), *this)) {
1818 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "delete", "object does not belong to this context");
1819 return false;
1820 }
1821 if (object->isDeleted())
1822 return false;
1823 if (object->object())
1824 // We need to pass in context here because we want
1825 // things in this context unbound.
1826 object->deleteObject(graphicsContext3D());
1827 return true;
1828}
1829
1830void WebGLRenderingContextBase::deleteBuffer(WebGLBuffer* buffer)
1831{
1832 if (!deleteObject(buffer))
1833 return;
1834 if (m_boundArrayBuffer == buffer)
1835 m_boundArrayBuffer = nullptr;
1836
1837 m_boundVertexArrayObject->unbindBuffer(*buffer);
1838}
1839
1840void WebGLRenderingContextBase::deleteFramebuffer(WebGLFramebuffer* framebuffer)
1841{
1842 if (!deleteObject(framebuffer))
1843 return;
1844#if ENABLE(WEBGL2)
1845 if (isWebGL2() && framebuffer == m_readFramebufferBinding) {
1846 m_readFramebufferBinding = nullptr;
1847 m_context->bindFramebuffer(GraphicsContext3D::READ_FRAMEBUFFER, 0);
1848 }
1849#endif
1850 if (framebuffer == m_framebufferBinding) {
1851 m_framebufferBinding = nullptr;
1852#if ENABLE(WEBGL2)
1853 if (isWebGL2())
1854 m_context->bindFramebuffer(GraphicsContext3D::DRAW_FRAMEBUFFER, 0);
1855 else
1856#endif
1857 m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0);
1858 }
1859}
1860
1861void WebGLRenderingContextBase::deleteProgram(WebGLProgram* program)
1862{
1863 if (program)
1864 InspectorInstrumentation::willDeleteProgram(*this, *program);
1865
1866 deleteObject(program);
1867 // We don't reset m_currentProgram to 0 here because the deletion of the
1868 // current program is delayed.
1869}
1870
1871void WebGLRenderingContextBase::deleteRenderbuffer(WebGLRenderbuffer* renderbuffer)
1872{
1873 if (!deleteObject(renderbuffer))
1874 return;
1875 if (renderbuffer == m_renderbufferBinding)
1876 m_renderbufferBinding = nullptr;
1877 if (m_framebufferBinding)
1878 m_framebufferBinding->removeAttachmentFromBoundFramebuffer(renderbuffer);
1879}
1880
1881void WebGLRenderingContextBase::deleteShader(WebGLShader* shader)
1882{
1883 deleteObject(shader);
1884}
1885
1886void WebGLRenderingContextBase::deleteTexture(WebGLTexture* texture)
1887{
1888 if (!deleteObject(texture))
1889 return;
1890
1891 unsigned current = 0;
1892 for (auto& textureUnit : m_textureUnits) {
1893 if (texture == textureUnit.texture2DBinding) {
1894 textureUnit.texture2DBinding = nullptr;
1895 m_unrenderableTextureUnits.remove(current);
1896 }
1897 if (texture == textureUnit.textureCubeMapBinding) {
1898 textureUnit.textureCubeMapBinding = nullptr;
1899 m_unrenderableTextureUnits.remove(current);
1900 }
1901 ++current;
1902 }
1903 if (m_framebufferBinding)
1904 m_framebufferBinding->removeAttachmentFromBoundFramebuffer(texture);
1905}
1906
1907void WebGLRenderingContextBase::depthFunc(GC3Denum func)
1908{
1909 if (isContextLostOrPending())
1910 return;
1911 m_context->depthFunc(func);
1912}
1913
1914void WebGLRenderingContextBase::depthMask(GC3Dboolean flag)
1915{
1916 if (isContextLostOrPending())
1917 return;
1918 m_depthMask = flag;
1919 m_context->depthMask(flag);
1920}
1921
1922void WebGLRenderingContextBase::depthRange(GC3Dfloat zNear, GC3Dfloat zFar)
1923{
1924 if (isContextLostOrPending())
1925 return;
1926 if (zNear > zFar) {
1927 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "depthRange", "zNear > zFar");
1928 return;
1929 }
1930 m_context->depthRange(zNear, zFar);
1931}
1932
1933void WebGLRenderingContextBase::detachShader(WebGLProgram* program, WebGLShader* shader)
1934{
1935 if (isContextLostOrPending() || !validateWebGLObject("detachShader", program) || !validateWebGLObject("detachShader", shader))
1936 return;
1937 if (!program->detachShader(shader)) {
1938 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "detachShader", "shader not attached");
1939 return;
1940 }
1941 m_context->detachShader(objectOrZero(program), objectOrZero(shader));
1942 shader->onDetached(graphicsContext3D());
1943}
1944
1945void WebGLRenderingContextBase::disable(GC3Denum cap)
1946{
1947 if (isContextLostOrPending() || !validateCapability("disable", cap))
1948 return;
1949 if (cap == GraphicsContext3D::STENCIL_TEST) {
1950 m_stencilEnabled = false;
1951 applyStencilTest();
1952 return;
1953 }
1954 if (cap == GraphicsContext3D::SCISSOR_TEST)
1955 m_scissorEnabled = false;
1956 m_context->disable(cap);
1957}
1958
1959void WebGLRenderingContextBase::disableVertexAttribArray(GC3Duint index)
1960{
1961 if (isContextLostOrPending())
1962 return;
1963 if (index >= m_maxVertexAttribs) {
1964 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "disableVertexAttribArray", "index out of range");
1965 return;
1966 }
1967
1968 WebGLVertexArrayObjectBase::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index);
1969 state.enabled = false;
1970
1971 if (index > 0 || isGLES2Compliant())
1972 m_context->disableVertexAttribArray(index);
1973}
1974
1975bool WebGLRenderingContextBase::validateNPOTTextureLevel(GC3Dsizei width, GC3Dsizei height, GC3Dint level, const char* functionName)
1976{
1977 if (!isGLES2NPOTStrict() && level && WebGLTexture::isNPOT(width, height)) {
1978 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "level > 0 not power of 2");
1979 return false;
1980 }
1981
1982 return true;
1983}
1984
1985bool WebGLRenderingContextBase::validateElementArraySize(GC3Dsizei count, GC3Denum type, GC3Dintptr offset)
1986{
1987 RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer();
1988
1989 if (!elementArrayBuffer)
1990 return false;
1991
1992 if (offset < 0)
1993 return false;
1994
1995 if (type == GraphicsContext3D::UNSIGNED_INT) {
1996 // For an unsigned int array, offset must be divisible by 4 for alignment reasons.
1997 if (offset % 4)
1998 return false;
1999
2000 // Make uoffset an element offset.
2001 offset /= 4;
2002
2003 GC3Dsizeiptr n = elementArrayBuffer->byteLength() / 4;
2004 if (offset > n || count > n - offset)
2005 return false;
2006 } else if (type == GraphicsContext3D::UNSIGNED_SHORT) {
2007 // For an unsigned short array, offset must be divisible by 2 for alignment reasons.
2008 if (offset % 2)
2009 return false;
2010
2011 // Make uoffset an element offset.
2012 offset /= 2;
2013
2014 GC3Dsizeiptr n = elementArrayBuffer->byteLength() / 2;
2015 if (offset > n || count > n - offset)
2016 return false;
2017 } else if (type == GraphicsContext3D::UNSIGNED_BYTE) {
2018 GC3Dsizeiptr n = elementArrayBuffer->byteLength();
2019 if (offset > n || count > n - offset)
2020 return false;
2021 }
2022 return true;
2023}
2024
2025bool WebGLRenderingContextBase::validateIndexArrayPrecise(GC3Dsizei count, GC3Denum type, GC3Dintptr offset, unsigned& numElementsRequired)
2026{
2027 ASSERT(count >= 0 && offset >= 0);
2028 unsigned maxIndex = 0;
2029
2030 RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer();
2031
2032 if (!elementArrayBuffer)
2033 return false;
2034
2035 if (!count) {
2036 numElementsRequired = 0;
2037 return true;
2038 }
2039
2040 auto buffer = elementArrayBuffer->elementArrayBuffer();
2041 if (!buffer)
2042 return false;
2043
2044 switch (type) {
2045 case GraphicsContext3D::UNSIGNED_INT:
2046 maxIndex = getMaxIndex<GC3Duint>(buffer, offset, count);
2047 break;
2048 case GraphicsContext3D::UNSIGNED_SHORT:
2049 maxIndex = getMaxIndex<GC3Dushort>(buffer, offset, count);
2050 break;
2051 case GraphicsContext3D::UNSIGNED_BYTE:
2052 maxIndex = getMaxIndex<GC3Dubyte>(buffer, offset, count);
2053 break;
2054 }
2055
2056 // Then set the maxiumum index in the index array and make sure it is valid.
2057 auto checkedNumElementsRequired = checkedAddAndMultiply<unsigned>(maxIndex, 1, 1);
2058 if (!checkedNumElementsRequired)
2059 return false;
2060 numElementsRequired = checkedNumElementsRequired.value();
2061 return true;
2062}
2063
2064bool WebGLRenderingContextBase::validateVertexAttributes(unsigned elementCount, unsigned primitiveCount)
2065{
2066 if (!m_currentProgram)
2067 return false;
2068
2069 // Look in each enabled vertex attrib and check if they've been bound to a buffer.
2070 for (unsigned i = 0; i < m_maxVertexAttribs; ++i) {
2071 if (!m_boundVertexArrayObject->getVertexAttribState(i).validateBinding())
2072 return false;
2073 }
2074
2075 if (!elementCount)
2076 return true;
2077
2078 // Look in each consumed vertex attrib (by the current program).
2079 bool sawNonInstancedAttrib = false;
2080 bool sawEnabledAttrib = false;
2081 int numActiveAttribLocations = m_currentProgram->numActiveAttribLocations();
2082 for (int i = 0; i < numActiveAttribLocations; ++i) {
2083 int loc = m_currentProgram->getActiveAttribLocation(i);
2084 if (loc >= 0 && loc < static_cast<int>(m_maxVertexAttribs)) {
2085 const WebGLVertexArrayObjectBase::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(loc);
2086 if (state.enabled) {
2087 sawEnabledAttrib = true;
2088 // Avoid off-by-one errors in numElements computation.
2089 // For the last element, we will only touch the data for the
2090 // element and nothing beyond it.
2091 int bytesRemaining = static_cast<int>(state.bufferBinding->byteLength() - state.offset);
2092 if (bytesRemaining <= 0)
2093 return false;
2094 unsigned numElements = 0;
2095 ASSERT(state.stride > 0);
2096 if (bytesRemaining >= state.bytesPerElement)
2097 numElements = 1 + (bytesRemaining - state.bytesPerElement) / state.stride;
2098 unsigned instancesRequired = 0;
2099 if (state.divisor) {
2100 instancesRequired = ceil(static_cast<float>(primitiveCount) / state.divisor);
2101 if (instancesRequired > numElements)
2102 return false;
2103 } else {
2104 sawNonInstancedAttrib = true;
2105 if (elementCount > numElements)
2106 return false;
2107 }
2108 }
2109 }
2110 }
2111
2112 if (!sawNonInstancedAttrib && sawEnabledAttrib)
2113 return false;
2114
2115 bool usingSimulatedArrayBuffer = m_currentProgram->isUsingVertexAttrib0();
2116
2117 // Guard against access into non-existent buffers.
2118 if (elementCount && !sawEnabledAttrib && !usingSimulatedArrayBuffer)
2119 return false;
2120
2121 if (elementCount && sawEnabledAttrib) {
2122 if (!m_boundArrayBuffer && !m_boundVertexArrayObject->getElementArrayBuffer()) {
2123 if (usingSimulatedArrayBuffer) {
2124 auto& state = m_boundVertexArrayObject->getVertexAttribState(0);
2125 if (state.enabled && state.isBound()) {
2126 if (state.bufferBinding->getTarget() == GraphicsContext3D::ARRAY_BUFFER || state.bufferBinding->getTarget() == GraphicsContext3D::ELEMENT_ARRAY_BUFFER)
2127 return !!state.bufferBinding->byteLength();
2128 }
2129 }
2130 return false;
2131 }
2132 }
2133
2134 return true;
2135}
2136
2137bool WebGLRenderingContextBase::validateWebGLObject(const char* functionName, WebGLObject* object)
2138{
2139 if (!object || !object->object()) {
2140 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no object or object deleted");
2141 return false;
2142 }
2143 if (!object->validate(contextGroup(), *this)) {
2144 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "object does not belong to this context");
2145 return false;
2146 }
2147 return true;
2148}
2149
2150bool WebGLRenderingContextBase::validateDrawArrays(const char* functionName, GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primitiveCount)
2151{
2152 if (isContextLostOrPending() || !validateDrawMode(functionName, mode))
2153 return false;
2154
2155 if (!validateStencilSettings(functionName))
2156 return false;
2157
2158 if (first < 0 || count < 0) {
2159 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "first or count < 0");
2160 return false;
2161 }
2162
2163 if (!count) {
2164 markContextChanged();
2165 return false;
2166 }
2167
2168 if (primitiveCount < 0) {
2169 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "primcount < 0");
2170 return false;
2171 }
2172
2173 // Ensure we have a valid rendering state.
2174 Checked<GC3Dint, RecordOverflow> checkedSum = Checked<GC3Dint, RecordOverflow>(first) + Checked<GC3Dint, RecordOverflow>(count);
2175 Checked<GC3Dint, RecordOverflow> checkedPrimitiveCount(primitiveCount);
2176 if (checkedSum.hasOverflowed() || checkedPrimitiveCount.hasOverflowed() || !validateVertexAttributes(checkedSum.unsafeGet(), checkedPrimitiveCount.unsafeGet())) {
2177 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "attempt to access out of bounds arrays");
2178 return false;
2179 }
2180 if (!validateSimulatedVertexAttrib0(checkedSum.unsafeGet() - 1)) {
2181 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "attempt to access outside the bounds of the simulated vertexAttrib0 array");
2182 return false;
2183 }
2184
2185 const char* reason = "framebuffer incomplete";
2186 if (m_framebufferBinding && !m_framebufferBinding->onAccess(graphicsContext3D(), &reason)) {
2187 synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION, functionName, reason);
2188 return false;
2189 }
2190
2191 return true;
2192}
2193
2194bool WebGLRenderingContextBase::validateDrawElements(const char* functionName, GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, unsigned& numElements, GC3Dsizei primitiveCount)
2195{
2196 if (isContextLostOrPending() || !validateDrawMode(functionName, mode))
2197 return false;
2198
2199 if (!validateStencilSettings(functionName))
2200 return false;
2201
2202 switch (type) {
2203 case GraphicsContext3D::UNSIGNED_BYTE:
2204 case GraphicsContext3D::UNSIGNED_SHORT:
2205 break;
2206 case GraphicsContext3D::UNSIGNED_INT:
2207 if (m_oesElementIndexUint || isWebGL2())
2208 break;
2209 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid type");
2210 return false;
2211 default:
2212 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid type");
2213 return false;
2214 }
2215
2216 if (count < 0 || offset < 0) {
2217 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "count or offset < 0");
2218 return false;
2219 }
2220
2221 if (!count) {
2222 markContextChanged();
2223 return false;
2224 }
2225
2226 if (primitiveCount < 0) {
2227 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "primcount < 0");
2228 return false;
2229 }
2230
2231 if (!m_boundVertexArrayObject->getElementArrayBuffer()) {
2232 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "no ELEMENT_ARRAY_BUFFER bound");
2233 return false;
2234 }
2235
2236 // Ensure we have a valid rendering state.
2237 if (!validateElementArraySize(count, type, static_cast<GC3Dintptr>(offset))) {
2238 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "request out of bounds for current ELEMENT_ARRAY_BUFFER");
2239 return false;
2240 }
2241 if (!count)
2242 return false;
2243
2244 Checked<GC3Dint, RecordOverflow> checkedCount(count);
2245 Checked<GC3Dint, RecordOverflow> checkedPrimitiveCount(primitiveCount);
2246 if (checkedCount.hasOverflowed() || checkedPrimitiveCount.hasOverflowed()) {
2247 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "attempt to access out of bounds arrays");
2248 return false;
2249 }
2250
2251 if (!validateIndexArrayConservative(type, numElements) || !validateVertexAttributes(numElements, checkedPrimitiveCount.unsafeGet())) {
2252 if (!validateIndexArrayPrecise(checkedCount.unsafeGet(), type, static_cast<GC3Dintptr>(offset), numElements) || !validateVertexAttributes(numElements, checkedPrimitiveCount.unsafeGet())) {
2253 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "attempt to access out of bounds arrays");
2254 return false;
2255 }
2256 }
2257
2258 if (!validateSimulatedVertexAttrib0(numElements)) {
2259 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "attempt to access outside the bounds of the simulated vertexAttrib0 array");
2260 return false;
2261 }
2262
2263 const char* reason = "framebuffer incomplete";
2264 if (m_framebufferBinding && !m_framebufferBinding->onAccess(graphicsContext3D(), &reason)) {
2265 synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION, functionName, reason);
2266 return false;
2267 }
2268
2269 return true;
2270}
2271
2272void WebGLRenderingContextBase::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count)
2273{
2274 if (!validateDrawArrays("drawArrays", mode, first, count, 0))
2275 return;
2276
2277 if (m_currentProgram && InspectorInstrumentation::isShaderProgramDisabled(*this, *m_currentProgram))
2278 return;
2279
2280 clearIfComposited();
2281
2282 bool vertexAttrib0Simulated = false;
2283 if (!isGLES2Compliant()) {
2284 auto simulateVertexAttrib0Status = simulateVertexAttrib0(first + count - 1);
2285 if (!simulateVertexAttrib0Status) {
2286 // We were unable to simulate the attribute buffer.
2287 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawArrays", "unable to simulate vertexAttrib0 array");
2288 return;
2289 }
2290 vertexAttrib0Simulated = simulateVertexAttrib0Status.value();
2291 }
2292 bool usesFallbackTexture = false;
2293 if (!isGLES2NPOTStrict())
2294 usesFallbackTexture = checkTextureCompleteness("drawArrays", true);
2295
2296 {
2297 InspectorScopedShaderProgramHighlight scopedHighlight(*this, m_currentProgram.get());
2298
2299 m_context->drawArrays(mode, first, count);
2300 }
2301
2302 if (!isGLES2Compliant() && vertexAttrib0Simulated)
2303 restoreStatesAfterVertexAttrib0Simulation();
2304 if (usesFallbackTexture)
2305 checkTextureCompleteness("drawArrays", false);
2306 markContextChangedAndNotifyCanvasObserver();
2307}
2308
2309#if USE(OPENGL) && ENABLE(WEBGL2)
2310static GC3Duint getRestartIndex(GC3Denum type)
2311{
2312 switch (type) {
2313 case GraphicsContext3D::UNSIGNED_BYTE:
2314 return std::numeric_limits<GC3Dubyte>::max();
2315 case GraphicsContext3D::UNSIGNED_SHORT:
2316 return std::numeric_limits<GC3Dushort>::max();
2317 case GraphicsContext3D::UNSIGNED_INT:
2318 return std::numeric_limits<GC3Duint>::max();
2319 }
2320
2321 return 0;
2322}
2323#endif
2324
2325void WebGLRenderingContextBase::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset)
2326{
2327 unsigned numElements = 0;
2328 if (!validateDrawElements("drawElements", mode, count, type, offset, numElements, 0))
2329 return;
2330
2331 if (m_currentProgram && InspectorInstrumentation::isShaderProgramDisabled(*this, *m_currentProgram))
2332 return;
2333
2334 clearIfComposited();
2335
2336 bool vertexAttrib0Simulated = false;
2337 if (!isGLES2Compliant()) {
2338 if (!numElements)
2339 validateIndexArrayPrecise(count, type, static_cast<GC3Dintptr>(offset), numElements);
2340 auto simulateVertexAttrib0Status = simulateVertexAttrib0(numElements);
2341 if (!simulateVertexAttrib0Status) {
2342 // We were unable to simulate the attribute buffer.
2343 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawElements", "unable to simulate vertexAttrib0 array");
2344 return;
2345 }
2346 vertexAttrib0Simulated = simulateVertexAttrib0Status.value();
2347 }
2348
2349 bool usesFallbackTexture = false;
2350 if (!isGLES2NPOTStrict())
2351 usesFallbackTexture = checkTextureCompleteness("drawElements", true);
2352
2353#if USE(OPENGL) && ENABLE(WEBGL2)
2354 if (isWebGL2())
2355 m_context->primitiveRestartIndex(getRestartIndex(type));
2356#endif
2357
2358 {
2359 InspectorScopedShaderProgramHighlight scopedHighlight(*this, m_currentProgram.get());
2360
2361 m_context->drawElements(mode, count, type, static_cast<GC3Dintptr>(offset));
2362 }
2363
2364 if (!isGLES2Compliant() && vertexAttrib0Simulated)
2365 restoreStatesAfterVertexAttrib0Simulation();
2366 if (usesFallbackTexture)
2367 checkTextureCompleteness("drawElements", false);
2368 markContextChangedAndNotifyCanvasObserver();
2369}
2370
2371void WebGLRenderingContextBase::enable(GC3Denum cap)
2372{
2373 if (isContextLostOrPending() || !validateCapability("enable", cap))
2374 return;
2375 if (cap == GraphicsContext3D::STENCIL_TEST) {
2376 m_stencilEnabled = true;
2377 applyStencilTest();
2378 return;
2379 }
2380 if (cap == GraphicsContext3D::SCISSOR_TEST)
2381 m_scissorEnabled = true;
2382 m_context->enable(cap);
2383}
2384
2385void WebGLRenderingContextBase::enableVertexAttribArray(GC3Duint index)
2386{
2387 if (isContextLostOrPending())
2388 return;
2389 if (index >= m_maxVertexAttribs) {
2390 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "enableVertexAttribArray", "index out of range");
2391 return;
2392 }
2393
2394 WebGLVertexArrayObjectBase::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index);
2395 state.enabled = true;
2396
2397 m_context->enableVertexAttribArray(index);
2398}
2399
2400void WebGLRenderingContextBase::finish()
2401{
2402 if (isContextLostOrPending())
2403 return;
2404 m_context->finish();
2405}
2406
2407void WebGLRenderingContextBase::flush()
2408{
2409 if (isContextLostOrPending())
2410 return;
2411 m_context->flush();
2412}
2413
2414void WebGLRenderingContextBase::framebufferRenderbuffer(GC3Denum target, GC3Denum attachment, GC3Denum renderbuffertarget, WebGLRenderbuffer* buffer)
2415{
2416 if (isContextLostOrPending() || !validateFramebufferFuncParameters("framebufferRenderbuffer", target, attachment))
2417 return;
2418 if (renderbuffertarget != GraphicsContext3D::RENDERBUFFER) {
2419 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "framebufferRenderbuffer", "invalid target");
2420 return;
2421 }
2422 if (buffer && !buffer->validate(contextGroup(), *this)) {
2423 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "framebufferRenderbuffer", "no buffer or buffer not from this context");
2424 return;
2425 }
2426
2427 auto targetFramebuffer = (target == GraphicsContext3D::READ_FRAMEBUFFER) ? m_readFramebufferBinding : m_framebufferBinding;
2428
2429 // Don't allow the default framebuffer to be mutated; all current
2430 // implementations use an FBO internally in place of the default
2431 // FBO.
2432 if (!targetFramebuffer || !targetFramebuffer->object()) {
2433 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "framebufferRenderbuffer", "no framebuffer bound");
2434 return;
2435 }
2436 Platform3DObject bufferObject = objectOrZero(buffer);
2437 switch (attachment) {
2438 case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
2439 m_context->framebufferRenderbuffer(target, GraphicsContext3D::DEPTH_ATTACHMENT, renderbuffertarget, bufferObject);
2440 m_context->framebufferRenderbuffer(target, GraphicsContext3D::STENCIL_ATTACHMENT, renderbuffertarget, bufferObject);
2441 break;
2442 default:
2443 m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, bufferObject);
2444 }
2445 targetFramebuffer->setAttachmentForBoundFramebuffer(attachment, buffer);
2446 applyStencilTest();
2447}
2448
2449void WebGLRenderingContextBase::framebufferTexture2D(GC3Denum target, GC3Denum attachment, GC3Denum textarget, WebGLTexture* texture, GC3Dint level)
2450{
2451 if (isContextLostOrPending() || !validateFramebufferFuncParameters("framebufferTexture2D", target, attachment))
2452 return;
2453 if (level && isWebGL1()) {
2454 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "framebufferTexture2D", "level not 0");
2455 return;
2456 }
2457 if (texture && !texture->validate(contextGroup(), *this)) {
2458 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "framebufferTexture2D", "no texture or texture not from this context");
2459 return;
2460 }
2461
2462 auto targetFramebuffer = (target == GraphicsContext3D::READ_FRAMEBUFFER) ? m_readFramebufferBinding : m_framebufferBinding;
2463
2464 // Don't allow the default framebuffer to be mutated; all current
2465 // implementations use an FBO internally in place of the default
2466 // FBO.
2467 if (!targetFramebuffer || !targetFramebuffer->object()) {
2468 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "framebufferTexture2D", "no framebuffer bound");
2469 return;
2470 }
2471 Platform3DObject textureObject = objectOrZero(texture);
2472 switch (attachment) {
2473 case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
2474 m_context->framebufferTexture2D(target, GraphicsContext3D::DEPTH_ATTACHMENT, textarget, textureObject, level);
2475 m_context->framebufferTexture2D(target, GraphicsContext3D::STENCIL_ATTACHMENT, textarget, textureObject, level);
2476 break;
2477 case GraphicsContext3D::DEPTH_ATTACHMENT:
2478 m_context->framebufferTexture2D(target, attachment, textarget, textureObject, level);
2479 break;
2480 case GraphicsContext3D::STENCIL_ATTACHMENT:
2481 m_context->framebufferTexture2D(target, attachment, textarget, textureObject, level);
2482 break;
2483 default:
2484 m_context->framebufferTexture2D(target, attachment, textarget, textureObject, level);
2485 }
2486 targetFramebuffer->setAttachmentForBoundFramebuffer(attachment, textarget, texture, level);
2487 applyStencilTest();
2488}
2489
2490void WebGLRenderingContextBase::frontFace(GC3Denum mode)
2491{
2492 if (isContextLostOrPending())
2493 return;
2494 m_context->frontFace(mode);
2495}
2496
2497void WebGLRenderingContextBase::generateMipmap(GC3Denum target)
2498{
2499 if (isContextLostOrPending())
2500 return;
2501 auto tex = validateTextureBinding("generateMipmap", target, false);
2502 if (!tex)
2503 return;
2504 if (!tex->canGenerateMipmaps()) {
2505 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "generateMipmap", "level 0 not power of 2 or not all the same size");
2506 return;
2507 }
2508 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=123916. Compressed textures should be allowed in WebGL 2:
2509 if (tex->isCompressed()) {
2510 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "generateMipmap", "trying to generate mipmaps from compressed texture");
2511 return;
2512 }
2513 if (!validateSettableTexInternalFormat("generateMipmap", tex->getInternalFormat(target, 0)))
2514 return;
2515
2516 // generateMipmap won't work properly if minFilter is not NEAREST_MIPMAP_LINEAR
2517 // on Mac. Remove the hack once this driver bug is fixed.
2518#if OS(DARWIN)
2519 bool needToResetMinFilter = false;
2520 if (tex->getMinFilter() != GraphicsContext3D::NEAREST_MIPMAP_LINEAR) {
2521 m_context->texParameteri(target, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST_MIPMAP_LINEAR);
2522 needToResetMinFilter = true;
2523 }
2524#endif
2525 m_context->generateMipmap(target);
2526#if OS(DARWIN)
2527 if (needToResetMinFilter)
2528 m_context->texParameteri(target, GraphicsContext3D::TEXTURE_MIN_FILTER, tex->getMinFilter());
2529#endif
2530 tex->generateMipmapLevelInfo();
2531}
2532
2533RefPtr<WebGLActiveInfo> WebGLRenderingContextBase::getActiveAttrib(WebGLProgram* program, GC3Duint index)
2534{
2535 if (isContextLostOrPending() || !validateWebGLObject("getActiveAttrib", program))
2536 return nullptr;
2537 ActiveInfo info;
2538 if (!m_context->getActiveAttrib(objectOrZero(program), index, info))
2539 return nullptr;
2540
2541 LOG(WebGL, "Returning active attribute %d: %s", index, info.name.utf8().data());
2542
2543 return WebGLActiveInfo::create(info.name, info.type, info.size);
2544}
2545
2546RefPtr<WebGLActiveInfo> WebGLRenderingContextBase::getActiveUniform(WebGLProgram* program, GC3Duint index)
2547{
2548 if (isContextLostOrPending() || !validateWebGLObject("getActiveUniform", program))
2549 return nullptr;
2550 ActiveInfo info;
2551 if (!m_context->getActiveUniform(objectOrZero(program), index, info))
2552 return nullptr;
2553 if (!isGLES2Compliant())
2554 if (info.size > 1 && !info.name.endsWith("[0]"))
2555 info.name.append("[0]");
2556
2557 LOG(WebGL, "Returning active uniform %d: %s", index, info.name.utf8().data());
2558
2559 return WebGLActiveInfo::create(info.name, info.type, info.size);
2560}
2561
2562Optional<Vector<RefPtr<WebGLShader>>> WebGLRenderingContextBase::getAttachedShaders(WebGLProgram* program)
2563{
2564 if (isContextLostOrPending() || !validateWebGLObject("getAttachedShaders", program))
2565 return WTF::nullopt;
2566
2567 const GC3Denum shaderTypes[] = {
2568 GraphicsContext3D::VERTEX_SHADER,
2569 GraphicsContext3D::FRAGMENT_SHADER
2570 };
2571 Vector<RefPtr<WebGLShader>> shaderObjects;
2572 for (auto shaderType : shaderTypes) {
2573 RefPtr<WebGLShader> shader = program->getAttachedShader(shaderType);
2574 if (shader)
2575 shaderObjects.append(shader);
2576 }
2577 return shaderObjects;
2578}
2579
2580GC3Dint WebGLRenderingContextBase::getAttribLocation(WebGLProgram* program, const String& name)
2581{
2582 if (isContextLostOrPending() || !validateWebGLObject("getAttribLocation", program))
2583 return -1;
2584 if (!validateLocationLength("getAttribLocation", name))
2585 return -1;
2586 if (!validateString("getAttribLocation", name))
2587 return -1;
2588 if (isPrefixReserved(name))
2589 return -1;
2590 if (!program->getLinkStatus()) {
2591 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "getAttribLocation", "program not linked");
2592 return -1;
2593 }
2594 return m_context->getAttribLocation(objectOrZero(program), name);
2595}
2596
2597WebGLAny WebGLRenderingContextBase::getBufferParameter(GC3Denum target, GC3Denum pname)
2598{
2599 if (isContextLostOrPending())
2600 return nullptr;
2601
2602 bool valid = false;
2603 if (target == GraphicsContext3D::ARRAY_BUFFER || target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER)
2604 valid = true;
2605#if ENABLE(WEBGL2)
2606 if (isWebGL2()) {
2607 switch (target) {
2608 case GraphicsContext3D::COPY_READ_BUFFER:
2609 case GraphicsContext3D::COPY_WRITE_BUFFER:
2610 case GraphicsContext3D::PIXEL_PACK_BUFFER:
2611 case GraphicsContext3D::PIXEL_UNPACK_BUFFER:
2612 case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER:
2613 case GraphicsContext3D::UNIFORM_BUFFER:
2614 valid = true;
2615 }
2616 }
2617#endif
2618 if (!valid) {
2619 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getBufferParameter", "invalid target");
2620 return nullptr;
2621 }
2622
2623 if (pname != GraphicsContext3D::BUFFER_SIZE && pname != GraphicsContext3D::BUFFER_USAGE) {
2624 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getBufferParameter", "invalid parameter name");
2625 return nullptr;
2626 }
2627
2628 GC3Dint value = 0;
2629 m_context->getBufferParameteriv(target, pname, &value);
2630 if (pname == GraphicsContext3D::BUFFER_SIZE)
2631 return value;
2632 return static_cast<unsigned>(value);
2633}
2634
2635Optional<WebGLContextAttributes> WebGLRenderingContextBase::getContextAttributes()
2636{
2637 if (isContextLostOrPending())
2638 return WTF::nullopt;
2639
2640 // Also, we need to enforce requested values of "false" for depth
2641 // and stencil, regardless of the properties of the underlying
2642 // GraphicsContext3D.
2643
2644 auto attributes = m_context->getContextAttributes();
2645 if (!m_attributes.depth)
2646 attributes.depth = false;
2647 if (!m_attributes.stencil)
2648 attributes.stencil = false;
2649 return attributes;
2650}
2651
2652GC3Denum WebGLRenderingContextBase::getError()
2653{
2654 if (m_isPendingPolicyResolution)
2655 return GraphicsContext3D::NO_ERROR;
2656 return m_context->getError();
2657}
2658
2659WebGLAny WebGLRenderingContextBase::getProgramParameter(WebGLProgram* program, GC3Denum pname)
2660{
2661 if (isContextLostOrPending() || !validateWebGLObject("getProgramParameter", program))
2662 return nullptr;
2663
2664 GC3Dint value = 0;
2665 switch (pname) {
2666 case GraphicsContext3D::DELETE_STATUS:
2667 return program->isDeleted();
2668 case GraphicsContext3D::VALIDATE_STATUS:
2669 m_context->getProgramiv(objectOrZero(program), pname, &value);
2670 return static_cast<bool>(value);
2671 case GraphicsContext3D::LINK_STATUS:
2672 return program->getLinkStatus();
2673 case GraphicsContext3D::ATTACHED_SHADERS:
2674 m_context->getProgramiv(objectOrZero(program), pname, &value);
2675 return value;
2676 case GraphicsContext3D::ACTIVE_ATTRIBUTES:
2677 case GraphicsContext3D::ACTIVE_UNIFORMS:
2678 m_context->getNonBuiltInActiveSymbolCount(objectOrZero(program), pname, &value);
2679 return value;
2680 default:
2681 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getProgramParameter", "invalid parameter name");
2682 return nullptr;
2683 }
2684}
2685
2686String WebGLRenderingContextBase::getProgramInfoLog(WebGLProgram* program)
2687{
2688 if (isContextLostOrPending() || !validateWebGLObject("getProgramInfoLog", program))
2689 return String();
2690 return ensureNotNull(m_context->getProgramInfoLog(objectOrZero(program)));
2691}
2692
2693WebGLAny WebGLRenderingContextBase::getRenderbufferParameter(GC3Denum target, GC3Denum pname)
2694{
2695 if (isContextLostOrPending())
2696 return nullptr;
2697 if (target != GraphicsContext3D::RENDERBUFFER) {
2698 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getRenderbufferParameter", "invalid target");
2699 return nullptr;
2700 }
2701 if (!m_renderbufferBinding || !m_renderbufferBinding->object()) {
2702 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "getRenderbufferParameter", "no renderbuffer bound");
2703 return nullptr;
2704 }
2705
2706 if (m_renderbufferBinding->getInternalFormat() == GraphicsContext3D::DEPTH_STENCIL
2707 && !m_renderbufferBinding->isValid()) {
2708 ASSERT(!isDepthStencilSupported());
2709 int value = 0;
2710 switch (pname) {
2711 case GraphicsContext3D::RENDERBUFFER_WIDTH:
2712 value = m_renderbufferBinding->getWidth();
2713 break;
2714 case GraphicsContext3D::RENDERBUFFER_HEIGHT:
2715 value = m_renderbufferBinding->getHeight();
2716 break;
2717 case GraphicsContext3D::RENDERBUFFER_RED_SIZE:
2718 case GraphicsContext3D::RENDERBUFFER_GREEN_SIZE:
2719 case GraphicsContext3D::RENDERBUFFER_BLUE_SIZE:
2720 case GraphicsContext3D::RENDERBUFFER_ALPHA_SIZE:
2721 value = 0;
2722 break;
2723 case GraphicsContext3D::RENDERBUFFER_DEPTH_SIZE:
2724 value = 24;
2725 break;
2726 case GraphicsContext3D::RENDERBUFFER_STENCIL_SIZE:
2727 value = 8;
2728 break;
2729 case GraphicsContext3D::RENDERBUFFER_INTERNAL_FORMAT:
2730 return m_renderbufferBinding->getInternalFormat();
2731 default:
2732 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getRenderbufferParameter", "invalid parameter name");
2733 return nullptr;
2734 }
2735 return value;
2736 }
2737
2738 GC3Dint value = 0;
2739 switch (pname) {
2740 case GraphicsContext3D::RENDERBUFFER_WIDTH:
2741 case GraphicsContext3D::RENDERBUFFER_HEIGHT:
2742 case GraphicsContext3D::RENDERBUFFER_RED_SIZE:
2743 case GraphicsContext3D::RENDERBUFFER_GREEN_SIZE:
2744 case GraphicsContext3D::RENDERBUFFER_BLUE_SIZE:
2745 case GraphicsContext3D::RENDERBUFFER_ALPHA_SIZE:
2746 case GraphicsContext3D::RENDERBUFFER_DEPTH_SIZE:
2747 case GraphicsContext3D::RENDERBUFFER_STENCIL_SIZE:
2748 m_context->getRenderbufferParameteriv(target, pname, &value);
2749 return value;
2750 case GraphicsContext3D::RENDERBUFFER_INTERNAL_FORMAT:
2751 return m_renderbufferBinding->getInternalFormat();
2752 default:
2753 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getRenderbufferParameter", "invalid parameter name");
2754 return nullptr;
2755 }
2756}
2757
2758WebGLAny WebGLRenderingContextBase::getShaderParameter(WebGLShader* shader, GC3Denum pname)
2759{
2760 if (isContextLostOrPending() || !validateWebGLObject("getShaderParameter", shader))
2761 return nullptr;
2762 GC3Dint value = 0;
2763 switch (pname) {
2764 case GraphicsContext3D::DELETE_STATUS:
2765 return shader->isDeleted();
2766 case GraphicsContext3D::COMPILE_STATUS:
2767 m_context->getShaderiv(objectOrZero(shader), pname, &value);
2768 return static_cast<bool>(value);
2769 case GraphicsContext3D::SHADER_TYPE:
2770 m_context->getShaderiv(objectOrZero(shader), pname, &value);
2771 return static_cast<unsigned>(value);
2772 default:
2773 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getShaderParameter", "invalid parameter name");
2774 return nullptr;
2775 }
2776}
2777
2778String WebGLRenderingContextBase::getShaderInfoLog(WebGLShader* shader)
2779{
2780 if (isContextLostOrPending() || !validateWebGLObject("getShaderInfoLog", shader))
2781 return String();
2782 return ensureNotNull(m_context->getShaderInfoLog(objectOrZero(shader)));
2783}
2784
2785RefPtr<WebGLShaderPrecisionFormat> WebGLRenderingContextBase::getShaderPrecisionFormat(GC3Denum shaderType, GC3Denum precisionType)
2786{
2787 if (isContextLostOrPending())
2788 return nullptr;
2789 switch (shaderType) {
2790 case GraphicsContext3D::VERTEX_SHADER:
2791 case GraphicsContext3D::FRAGMENT_SHADER:
2792 break;
2793 default:
2794 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getShaderPrecisionFormat", "invalid shader type");
2795 return nullptr;
2796 }
2797 switch (precisionType) {
2798 case GraphicsContext3D::LOW_FLOAT:
2799 case GraphicsContext3D::MEDIUM_FLOAT:
2800 case GraphicsContext3D::HIGH_FLOAT:
2801 case GraphicsContext3D::LOW_INT:
2802 case GraphicsContext3D::MEDIUM_INT:
2803 case GraphicsContext3D::HIGH_INT:
2804 break;
2805 default:
2806 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getShaderPrecisionFormat", "invalid precision type");
2807 return nullptr;
2808 }
2809
2810 GC3Dint range[2] = {0, 0};
2811 GC3Dint precision = 0;
2812 m_context->getShaderPrecisionFormat(shaderType, precisionType, range, &precision);
2813 return WebGLShaderPrecisionFormat::create(range[0], range[1], precision);
2814}
2815
2816String WebGLRenderingContextBase::getShaderSource(WebGLShader* shader)
2817{
2818 if (isContextLostOrPending() || !validateWebGLObject("getShaderSource", shader))
2819 return String();
2820 return ensureNotNull(shader->getSource());
2821}
2822
2823WebGLAny WebGLRenderingContextBase::getTexParameter(GC3Denum target, GC3Denum pname)
2824{
2825 if (isContextLostOrPending())
2826 return nullptr;
2827 auto tex = validateTextureBinding("getTexParameter", target, false);
2828 if (!tex)
2829 return nullptr;
2830 GC3Dint value = 0;
2831 switch (pname) {
2832 case GraphicsContext3D::TEXTURE_MAG_FILTER:
2833 case GraphicsContext3D::TEXTURE_MIN_FILTER:
2834 case GraphicsContext3D::TEXTURE_WRAP_S:
2835 case GraphicsContext3D::TEXTURE_WRAP_T:
2836 m_context->getTexParameteriv(target, pname, &value);
2837 return static_cast<unsigned>(value);
2838 case Extensions3D::TEXTURE_MAX_ANISOTROPY_EXT: // EXT_texture_filter_anisotropic
2839 if (m_extTextureFilterAnisotropic) {
2840 m_context->getTexParameteriv(target, pname, &value);
2841 return static_cast<unsigned>(value);
2842 }
2843 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getTexParameter", "invalid parameter name, EXT_texture_filter_anisotropic not enabled");
2844 return nullptr;
2845 default:
2846 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getTexParameter", "invalid parameter name");
2847 return nullptr;
2848 }
2849}
2850
2851WebGLAny WebGLRenderingContextBase::getUniform(WebGLProgram* program, const WebGLUniformLocation* uniformLocation)
2852{
2853 if (isContextLostOrPending() || !validateWebGLObject("getUniform", program))
2854 return nullptr;
2855 if (!uniformLocation || uniformLocation->program() != program) {
2856 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "getUniform", "no uniformlocation or not valid for this program");
2857 return nullptr;
2858 }
2859 GC3Dint location = uniformLocation->location();
2860
2861 GC3Denum baseType;
2862 unsigned length;
2863 switch (uniformLocation->type()) {
2864 case GraphicsContext3D::BOOL:
2865 baseType = GraphicsContext3D::BOOL;
2866 length = 1;
2867 break;
2868 case GraphicsContext3D::BOOL_VEC2:
2869 baseType = GraphicsContext3D::BOOL;
2870 length = 2;
2871 break;
2872 case GraphicsContext3D::BOOL_VEC3:
2873 baseType = GraphicsContext3D::BOOL;
2874 length = 3;
2875 break;
2876 case GraphicsContext3D::BOOL_VEC4:
2877 baseType = GraphicsContext3D::BOOL;
2878 length = 4;
2879 break;
2880 case GraphicsContext3D::INT:
2881 baseType = GraphicsContext3D::INT;
2882 length = 1;
2883 break;
2884 case GraphicsContext3D::INT_VEC2:
2885 baseType = GraphicsContext3D::INT;
2886 length = 2;
2887 break;
2888 case GraphicsContext3D::INT_VEC3:
2889 baseType = GraphicsContext3D::INT;
2890 length = 3;
2891 break;
2892 case GraphicsContext3D::INT_VEC4:
2893 baseType = GraphicsContext3D::INT;
2894 length = 4;
2895 break;
2896 case GraphicsContext3D::FLOAT:
2897 baseType = GraphicsContext3D::FLOAT;
2898 length = 1;
2899 break;
2900 case GraphicsContext3D::FLOAT_VEC2:
2901 baseType = GraphicsContext3D::FLOAT;
2902 length = 2;
2903 break;
2904 case GraphicsContext3D::FLOAT_VEC3:
2905 baseType = GraphicsContext3D::FLOAT;
2906 length = 3;
2907 break;
2908 case GraphicsContext3D::FLOAT_VEC4:
2909 baseType = GraphicsContext3D::FLOAT;
2910 length = 4;
2911 break;
2912 case GraphicsContext3D::FLOAT_MAT2:
2913 baseType = GraphicsContext3D::FLOAT;
2914 length = 4;
2915 break;
2916 case GraphicsContext3D::FLOAT_MAT3:
2917 baseType = GraphicsContext3D::FLOAT;
2918 length = 9;
2919 break;
2920 case GraphicsContext3D::FLOAT_MAT4:
2921 baseType = GraphicsContext3D::FLOAT;
2922 length = 16;
2923 break;
2924 case GraphicsContext3D::SAMPLER_2D:
2925 case GraphicsContext3D::SAMPLER_CUBE:
2926 baseType = GraphicsContext3D::INT;
2927 length = 1;
2928 break;
2929 default:
2930 // Can't handle this type
2931 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "getUniform", "unhandled type");
2932 return nullptr;
2933 }
2934 switch (baseType) {
2935 case GraphicsContext3D::FLOAT: {
2936 GC3Dfloat value[16] = {0};
2937 if (m_isRobustnessEXTSupported)
2938 m_context->getExtensions().getnUniformfvEXT(objectOrZero(program), location, 16 * sizeof(GC3Dfloat), value);
2939 else
2940 m_context->getUniformfv(objectOrZero(program), location, value);
2941 if (length == 1)
2942 return value[0];
2943 return Float32Array::tryCreate(value, length);
2944 }
2945 case GraphicsContext3D::INT: {
2946 GC3Dint value[4] = {0};
2947 if (m_isRobustnessEXTSupported)
2948 m_context->getExtensions().getnUniformivEXT(objectOrZero(program), location, 4 * sizeof(GC3Dint), value);
2949 else
2950 m_context->getUniformiv(objectOrZero(program), location, value);
2951 if (length == 1)
2952 return value[0];
2953 return Int32Array::tryCreate(value, length);
2954 }
2955 case GraphicsContext3D::BOOL: {
2956 GC3Dint value[4] = {0};
2957 if (m_isRobustnessEXTSupported)
2958 m_context->getExtensions().getnUniformivEXT(objectOrZero(program), location, 4 * sizeof(GC3Dint), value);
2959 else
2960 m_context->getUniformiv(objectOrZero(program), location, value);
2961 if (length > 1) {
2962 Vector<bool> vector(length);
2963 for (unsigned j = 0; j < length; j++)
2964 vector[j] = value[j];
2965 return vector;
2966 }
2967 return static_cast<bool>(value[0]);
2968 }
2969 default:
2970 notImplemented();
2971 }
2972
2973 // If we get here, something went wrong in our unfortunately complex logic above
2974 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "getUniform", "unknown error");
2975 return nullptr;
2976}
2977
2978RefPtr<WebGLUniformLocation> WebGLRenderingContextBase::getUniformLocation(WebGLProgram* program, const String& name)
2979{
2980 if (isContextLostOrPending() || !validateWebGLObject("getUniformLocation", program))
2981 return nullptr;
2982 if (!validateLocationLength("getUniformLocation", name))
2983 return nullptr;
2984 if (!validateString("getUniformLocation", name))
2985 return nullptr;
2986 if (isPrefixReserved(name))
2987 return nullptr;
2988 if (!program->getLinkStatus()) {
2989 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "getUniformLocation", "program not linked");
2990 return nullptr;
2991 }
2992 GC3Dint uniformLocation = m_context->getUniformLocation(objectOrZero(program), name);
2993 if (uniformLocation == -1)
2994 return nullptr;
2995
2996 GC3Dint activeUniforms = 0;
2997 m_context->getNonBuiltInActiveSymbolCount(objectOrZero(program), GraphicsContext3D::ACTIVE_UNIFORMS, &activeUniforms);
2998 for (GC3Dint i = 0; i < activeUniforms; i++) {
2999 ActiveInfo info;
3000 if (!m_context->getActiveUniform(objectOrZero(program), i, info))
3001 return nullptr;
3002 // Strip "[0]" from the name if it's an array.
3003 if (info.name.endsWith("[0]"))
3004 info.name = info.name.left(info.name.length() - 3);
3005 // If it's an array, we need to iterate through each element, appending "[index]" to the name.
3006 for (GC3Dint index = 0; index < info.size; ++index) {
3007 String uniformName = makeString(info.name, '[', index, ']');
3008
3009 if (name == uniformName || name == info.name)
3010 return WebGLUniformLocation::create(program, uniformLocation, info.type);
3011 }
3012 }
3013 return nullptr;
3014}
3015
3016WebGLAny WebGLRenderingContextBase::getVertexAttrib(GC3Duint index, GC3Denum pname)
3017{
3018 if (isContextLostOrPending())
3019 return nullptr;
3020
3021 if (index >= m_maxVertexAttribs) {
3022 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "getVertexAttrib", "index out of range");
3023 return nullptr;
3024 }
3025
3026 const WebGLVertexArrayObjectBase::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index);
3027
3028 if ((isWebGL2() || m_angleInstancedArrays) && pname == GraphicsContext3D::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE)
3029 return state.divisor;
3030
3031 switch (pname) {
3032 case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
3033 if ((!isGLES2Compliant() && !index && m_boundVertexArrayObject->getVertexAttribState(0).bufferBinding == m_vertexAttrib0Buffer)
3034 || !state.bufferBinding
3035 || !state.bufferBinding->object())
3036 return nullptr;
3037 return state.bufferBinding;
3038 case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_ENABLED:
3039 return state.enabled;
3040 case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_NORMALIZED:
3041 return state.normalized;
3042 case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_SIZE:
3043 return state.size;
3044 case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_STRIDE:
3045 return state.originalStride;
3046 case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_TYPE:
3047 return state.type;
3048 case GraphicsContext3D::CURRENT_VERTEX_ATTRIB:
3049 return Float32Array::tryCreate(m_vertexAttribValue[index].value, 4);
3050 default:
3051 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "getVertexAttrib", "invalid parameter name");
3052 return nullptr;
3053 }
3054}
3055
3056long long WebGLRenderingContextBase::getVertexAttribOffset(GC3Duint index, GC3Denum pname)
3057{
3058 if (isContextLostOrPending())
3059 return 0;
3060 return m_context->getVertexAttribOffset(index, pname);
3061}
3062
3063bool WebGLRenderingContextBase::extensionIsEnabled(const String& name)
3064{
3065#define CHECK_EXTENSION(variable, nameLiteral) \
3066 if (equalIgnoringASCIICase(name, nameLiteral)) \
3067 return variable != nullptr;
3068
3069 CHECK_EXTENSION(m_extFragDepth, "EXT_frag_depth");
3070 CHECK_EXTENSION(m_extBlendMinMax, "EXT_blend_minmax");
3071 CHECK_EXTENSION(m_extsRGB, "EXT_sRGB");
3072 CHECK_EXTENSION(m_extTextureFilterAnisotropic, "EXT_texture_filter_anisotropic");
3073 CHECK_EXTENSION(m_extTextureFilterAnisotropic, "WEBKIT_EXT_texture_filter_anisotropic");
3074 CHECK_EXTENSION(m_extShaderTextureLOD, "EXT_shader_texture_lod");
3075 CHECK_EXTENSION(m_oesTextureFloat, "OES_texture_float");
3076 CHECK_EXTENSION(m_oesTextureFloatLinear, "OES_texture_float_linear");
3077 CHECK_EXTENSION(m_oesTextureHalfFloat, "OES_texture_half_float");
3078 CHECK_EXTENSION(m_oesTextureHalfFloatLinear, "OES_texture_half_float_linear");
3079 CHECK_EXTENSION(m_oesStandardDerivatives, "OES_standard_derivatives");
3080 CHECK_EXTENSION(m_oesVertexArrayObject, "OES_vertex_array_object");
3081 CHECK_EXTENSION(m_oesElementIndexUint, "OES_element_index_uint");
3082 CHECK_EXTENSION(m_webglLoseContext, "WEBGL_lose_context");
3083 CHECK_EXTENSION(m_webglDebugRendererInfo, "WEBGL_debug_renderer_info");
3084 CHECK_EXTENSION(m_webglDebugShaders, "WEBGL_debug_shaders");
3085 CHECK_EXTENSION(m_webglCompressedTextureATC, "WEBKIT_WEBGL_compressed_texture_atc");
3086 CHECK_EXTENSION(m_webglCompressedTexturePVRTC, "WEBKIT_WEBGL_compressed_texture_pvrtc");
3087 CHECK_EXTENSION(m_webglCompressedTextureS3TC, "WEBGL_compressed_texture_s3tc");
3088 CHECK_EXTENSION(m_webglDepthTexture, "WEBGL_depth_texture");
3089 CHECK_EXTENSION(m_webglDrawBuffers, "WEBGL_draw_buffers");
3090 CHECK_EXTENSION(m_angleInstancedArrays, "ANGLE_instanced_arrays");
3091 return false;
3092}
3093
3094GC3Dboolean WebGLRenderingContextBase::isBuffer(WebGLBuffer* buffer)
3095{
3096 if (!buffer || isContextLostOrPending())
3097 return 0;
3098
3099 if (!buffer->hasEverBeenBound())
3100 return 0;
3101
3102 return m_context->isBuffer(buffer->object());
3103}
3104
3105bool WebGLRenderingContextBase::isContextLost() const
3106{
3107 return m_contextLost;
3108}
3109
3110bool WebGLRenderingContextBase::isContextLostOrPending()
3111{
3112 if (m_isPendingPolicyResolution && !m_hasRequestedPolicyResolution) {
3113 LOG(WebGL, "Context is being used. Attempt to resolve the policy.");
3114 auto* canvas = htmlCanvas();
3115 if (canvas) {
3116 Document& document = canvas->document().topDocument();
3117 Page* page = document.page();
3118 if (page && !document.url().isLocalFile())
3119 page->mainFrame().loader().client().resolveWebGLPolicyForURL(document.url());
3120 // FIXME: We don't currently do anything with the result from resolution. A more
3121 // complete implementation might try to construct a real context, etc and proceed
3122 // with normal operation.
3123 // https://bugs.webkit.org/show_bug.cgi?id=129122
3124 }
3125 m_hasRequestedPolicyResolution = true;
3126 }
3127
3128 return m_contextLost || m_isPendingPolicyResolution;
3129}
3130
3131GC3Dboolean WebGLRenderingContextBase::isEnabled(GC3Denum cap)
3132{
3133 if (isContextLostOrPending() || !validateCapability("isEnabled", cap))
3134 return 0;
3135 if (cap == GraphicsContext3D::STENCIL_TEST)
3136 return m_stencilEnabled;
3137 return m_context->isEnabled(cap);
3138}
3139
3140GC3Dboolean WebGLRenderingContextBase::isFramebuffer(WebGLFramebuffer* framebuffer)
3141{
3142 if (!framebuffer || isContextLostOrPending())
3143 return 0;
3144
3145 if (!framebuffer->hasEverBeenBound())
3146 return 0;
3147
3148 return m_context->isFramebuffer(framebuffer->object());
3149}
3150
3151GC3Dboolean WebGLRenderingContextBase::isProgram(WebGLProgram* program)
3152{
3153 if (!program || isContextLostOrPending())
3154 return 0;
3155
3156 return m_context->isProgram(program->object());
3157}
3158
3159GC3Dboolean WebGLRenderingContextBase::isRenderbuffer(WebGLRenderbuffer* renderbuffer)
3160{
3161 if (!renderbuffer || isContextLostOrPending())
3162 return 0;
3163
3164 if (!renderbuffer->hasEverBeenBound())
3165 return 0;
3166
3167 return m_context->isRenderbuffer(renderbuffer->object());
3168}
3169
3170GC3Dboolean WebGLRenderingContextBase::isShader(WebGLShader* shader)
3171{
3172 if (!shader || isContextLostOrPending())
3173 return 0;
3174
3175 return m_context->isShader(shader->object());
3176}
3177
3178GC3Dboolean WebGLRenderingContextBase::isTexture(WebGLTexture* texture)
3179{
3180 if (!texture || isContextLostOrPending())
3181 return 0;
3182
3183 if (!texture->hasEverBeenBound())
3184 return 0;
3185
3186 return m_context->isTexture(texture->object());
3187}
3188
3189void WebGLRenderingContextBase::lineWidth(GC3Dfloat width)
3190{
3191 if (isContextLostOrPending())
3192 return;
3193 m_context->lineWidth(width);
3194}
3195
3196void WebGLRenderingContextBase::linkProgram(WebGLProgram* program)
3197{
3198 if (!linkProgramWithoutInvalidatingAttribLocations(program))
3199 return;
3200
3201 program->increaseLinkCount();
3202}
3203
3204bool WebGLRenderingContextBase::linkProgramWithoutInvalidatingAttribLocations(WebGLProgram* program)
3205{
3206 if (isContextLostOrPending() || !validateWebGLObject("linkProgram", program))
3207 return false;
3208
3209 RefPtr<WebGLShader> vertexShader = program->getAttachedShader(GraphicsContext3D::VERTEX_SHADER);
3210 RefPtr<WebGLShader> fragmentShader = program->getAttachedShader(GraphicsContext3D::FRAGMENT_SHADER);
3211 if (!vertexShader || !vertexShader->isValid() || !fragmentShader || !fragmentShader->isValid() || !m_context->precisionsMatch(objectOrZero(vertexShader.get()), objectOrZero(fragmentShader.get())) || !m_context->checkVaryingsPacking(objectOrZero(vertexShader.get()), objectOrZero(fragmentShader.get()))) {
3212 program->setLinkStatus(false);
3213 return false;
3214 }
3215
3216 m_context->linkProgram(objectOrZero(program));
3217 return true;
3218}
3219
3220void WebGLRenderingContextBase::pixelStorei(GC3Denum pname, GC3Dint param)
3221{
3222 if (isContextLostOrPending())
3223 return;
3224 switch (pname) {
3225 case GraphicsContext3D::UNPACK_FLIP_Y_WEBGL:
3226 m_unpackFlipY = param;
3227 break;
3228 case GraphicsContext3D::UNPACK_PREMULTIPLY_ALPHA_WEBGL:
3229 m_unpackPremultiplyAlpha = param;
3230 break;
3231 case GraphicsContext3D::UNPACK_COLORSPACE_CONVERSION_WEBGL:
3232 if (param == GraphicsContext3D::BROWSER_DEFAULT_WEBGL || param == GraphicsContext3D::NONE)
3233 m_unpackColorspaceConversion = static_cast<GC3Denum>(param);
3234 else {
3235 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "pixelStorei", "invalid parameter for UNPACK_COLORSPACE_CONVERSION_WEBGL");
3236 return;
3237 }
3238 break;
3239 case GraphicsContext3D::PACK_ALIGNMENT:
3240 case GraphicsContext3D::UNPACK_ALIGNMENT:
3241 if (param == 1 || param == 2 || param == 4 || param == 8) {
3242 if (pname == GraphicsContext3D::PACK_ALIGNMENT)
3243 m_packAlignment = param;
3244 else // GraphicsContext3D::UNPACK_ALIGNMENT:
3245 m_unpackAlignment = param;
3246 m_context->pixelStorei(pname, param);
3247 } else {
3248 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "pixelStorei", "invalid parameter for alignment");
3249 return;
3250 }
3251 break;
3252 default:
3253 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "pixelStorei", "invalid parameter name");
3254 return;
3255 }
3256}
3257
3258void WebGLRenderingContextBase::polygonOffset(GC3Dfloat factor, GC3Dfloat units)
3259{
3260 if (isContextLostOrPending())
3261 return;
3262 m_context->polygonOffset(factor, units);
3263}
3264
3265enum class InternalFormatTheme {
3266 None,
3267 NormalizedFixedPoint,
3268 Packed,
3269 SignedNormalizedFixedPoint,
3270 FloatingPoint,
3271 SignedInteger,
3272 UnsignedInteger
3273};
3274
3275static InternalFormatTheme internalFormatTheme(GC3Denum internalFormat)
3276{
3277 switch (internalFormat) {
3278 case GraphicsContext3D::RGB:
3279 case GraphicsContext3D::RGBA:
3280 case GraphicsContext3D::LUMINANCE_ALPHA:
3281 case GraphicsContext3D::LUMINANCE:
3282 case GraphicsContext3D::ALPHA:
3283 case GraphicsContext3D::R8:
3284 case GraphicsContext3D::RG8:
3285 case GraphicsContext3D::RGB8:
3286 case GraphicsContext3D::SRGB8:
3287 case GraphicsContext3D::RGBA8:
3288 case GraphicsContext3D::SRGB8_ALPHA8:
3289 case GraphicsContext3D::SRGB_ALPHA:
3290 return InternalFormatTheme::NormalizedFixedPoint;
3291 case GraphicsContext3D::RGB565:
3292 case GraphicsContext3D::RGB5_A1:
3293 case GraphicsContext3D::RGBA4:
3294 case GraphicsContext3D::RGB9_E5:
3295 case GraphicsContext3D::RGB10_A2:
3296 case GraphicsContext3D::R11F_G11F_B10F:
3297 case GraphicsContext3D::RGB10_A2UI:
3298 return InternalFormatTheme::Packed;
3299 case GraphicsContext3D::R8_SNORM:
3300 case GraphicsContext3D::RG8_SNORM:
3301 case GraphicsContext3D::RGB8_SNORM:
3302 case GraphicsContext3D::RGBA8_SNORM:
3303 return InternalFormatTheme::SignedNormalizedFixedPoint;
3304 case GraphicsContext3D::R16F:
3305 case GraphicsContext3D::R32F:
3306 case GraphicsContext3D::RG16F:
3307 case GraphicsContext3D::RG32F:
3308 case GraphicsContext3D::RGB16F:
3309 case GraphicsContext3D::RGB32F:
3310 case GraphicsContext3D::RGBA16F:
3311 case GraphicsContext3D::RGBA32F:
3312 return InternalFormatTheme::FloatingPoint;
3313 case GraphicsContext3D::R8I:
3314 case GraphicsContext3D::R16I:
3315 case GraphicsContext3D::R32I:
3316 case GraphicsContext3D::RG8I:
3317 case GraphicsContext3D::RG16I:
3318 case GraphicsContext3D::RG32I:
3319 case GraphicsContext3D::RGB8I:
3320 case GraphicsContext3D::RGB16I:
3321 case GraphicsContext3D::RGB32I:
3322 case GraphicsContext3D::RGBA8I:
3323 case GraphicsContext3D::RGBA16I:
3324 case GraphicsContext3D::RGBA32I:
3325 return InternalFormatTheme::SignedInteger;
3326 case GraphicsContext3D::R8UI:
3327 case GraphicsContext3D::R16UI:
3328 case GraphicsContext3D::R32UI:
3329 case GraphicsContext3D::RG8UI:
3330 case GraphicsContext3D::RG16UI:
3331 case GraphicsContext3D::RG32UI:
3332 case GraphicsContext3D::RGB8UI:
3333 case GraphicsContext3D::RGB16UI:
3334 case GraphicsContext3D::RGB32UI:
3335 case GraphicsContext3D::RGBA8UI:
3336 case GraphicsContext3D::RGBA16UI:
3337 case GraphicsContext3D::RGBA32UI:
3338 return InternalFormatTheme::UnsignedInteger;
3339 default:
3340 return InternalFormatTheme::None;
3341 }
3342}
3343
3344static int numberOfComponentsForFormat(GC3Denum format)
3345{
3346 switch (format) {
3347 case GraphicsContext3D::RED:
3348 case GraphicsContext3D::RED_INTEGER:
3349 return 1;
3350 case GraphicsContext3D::RG:
3351 case GraphicsContext3D::RG_INTEGER:
3352 return 2;
3353 case GraphicsContext3D::RGB:
3354 case GraphicsContext3D::RGB_INTEGER:
3355 return 3;
3356 case GraphicsContext3D::RGBA:
3357 case GraphicsContext3D::RGBA_INTEGER:
3358 return 4;
3359 default:
3360 return 0;
3361 }
3362}
3363
3364static int numberOfComponentsForInternalFormat(GC3Denum internalFormat)
3365{
3366 switch (internalFormat) {
3367 case GraphicsContext3D::LUMINANCE:
3368 case GraphicsContext3D::ALPHA:
3369 case GraphicsContext3D::R8:
3370 case GraphicsContext3D::R8_SNORM:
3371 case GraphicsContext3D::R16F:
3372 case GraphicsContext3D::R32F:
3373 case GraphicsContext3D::R8UI:
3374 case GraphicsContext3D::R8I:
3375 case GraphicsContext3D::R16UI:
3376 case GraphicsContext3D::R16I:
3377 case GraphicsContext3D::R32UI:
3378 case GraphicsContext3D::R32I:
3379 case GraphicsContext3D::DEPTH_COMPONENT16:
3380 case GraphicsContext3D::DEPTH_COMPONENT24:
3381 case GraphicsContext3D::DEPTH_COMPONENT32F:
3382 return 1;
3383 case GraphicsContext3D::RG8:
3384 case GraphicsContext3D::LUMINANCE_ALPHA:
3385 case GraphicsContext3D::RG8_SNORM:
3386 case GraphicsContext3D::RG16F:
3387 case GraphicsContext3D::RG32F:
3388 case GraphicsContext3D::RG8UI:
3389 case GraphicsContext3D::RG8I:
3390 case GraphicsContext3D::RG16UI:
3391 case GraphicsContext3D::RG16I:
3392 case GraphicsContext3D::RG32UI:
3393 case GraphicsContext3D::RG32I:
3394 case GraphicsContext3D::DEPTH24_STENCIL8:
3395 case GraphicsContext3D::DEPTH32F_STENCIL8:
3396 return 2;
3397 case GraphicsContext3D::RGB:
3398 case GraphicsContext3D::RGB8:
3399 case GraphicsContext3D::SRGB8:
3400 case GraphicsContext3D::RGB565:
3401 case GraphicsContext3D::RGB8_SNORM:
3402 case GraphicsContext3D::R11F_G11F_B10F:
3403 case GraphicsContext3D::RGB9_E5:
3404 case GraphicsContext3D::RGB16F:
3405 case GraphicsContext3D::RGB32F:
3406 case GraphicsContext3D::RGB8UI:
3407 case GraphicsContext3D::RGB8I:
3408 case GraphicsContext3D::RGB16UI:
3409 case GraphicsContext3D::RGB16I:
3410 case GraphicsContext3D::RGB32UI:
3411 case GraphicsContext3D::RGB32I:
3412 return 3;
3413 case GraphicsContext3D::RGBA:
3414 case GraphicsContext3D::RGBA8:
3415 case GraphicsContext3D::SRGB_ALPHA:
3416 case GraphicsContext3D::SRGB8_ALPHA8:
3417 case GraphicsContext3D::RGBA8_SNORM:
3418 case GraphicsContext3D::RGB5_A1:
3419 case GraphicsContext3D::RGBA4:
3420 case GraphicsContext3D::RGB10_A2:
3421 case GraphicsContext3D::RGBA16F:
3422 case GraphicsContext3D::RGBA32F:
3423 case GraphicsContext3D::RGBA8UI:
3424 case GraphicsContext3D::RGBA8I:
3425 case GraphicsContext3D::RGB10_A2UI:
3426 case GraphicsContext3D::RGBA16UI:
3427 case GraphicsContext3D::RGBA16I:
3428 case GraphicsContext3D::RGBA32UI:
3429 case GraphicsContext3D::RGBA32I:
3430 return 4;
3431 default:
3432 return 0;
3433 }
3434}
3435
3436void WebGLRenderingContextBase::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView& pixels)
3437{
3438 if (isContextLostOrPending())
3439 return;
3440 // Due to WebGL's same-origin restrictions, it is not possible to
3441 // taint the origin using the WebGL API.
3442 ASSERT(canvasBase().originClean());
3443
3444 GC3Denum internalFormat = 0;
3445 if (m_framebufferBinding) {
3446 const char* reason = "framebuffer incomplete";
3447 if (!m_framebufferBinding->onAccess(graphicsContext3D(), &reason)) {
3448 synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION, "readPixels", reason);
3449 return;
3450 }
3451 // FIXME: readBuffer() should affect this
3452 internalFormat = m_framebufferBinding->getColorBufferFormat();
3453 } else {
3454 if (m_attributes.alpha)
3455 internalFormat = GraphicsContext3D::RGBA8;
3456 else
3457 internalFormat = GraphicsContext3D::RGB8;
3458 }
3459
3460 if (!internalFormat) {
3461 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "Incorrect internal format");
3462 return;
3463 }
3464
3465 if (isWebGL1()) {
3466 switch (format) {
3467 case GraphicsContext3D::ALPHA:
3468 case GraphicsContext3D::RGB:
3469 case GraphicsContext3D::RGBA:
3470 break;
3471 default:
3472 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "invalid format");
3473 return;
3474 }
3475 switch (type) {
3476 case GraphicsContext3D::UNSIGNED_BYTE:
3477 case GraphicsContext3D::UNSIGNED_SHORT_5_6_5:
3478 case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4:
3479 case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1:
3480 break;
3481 case GraphicsContext3D::FLOAT:
3482 if (!m_oesTextureFloat && !m_oesTextureHalfFloat) {
3483 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "invalid type");
3484 return;
3485 }
3486 break;
3487 default:
3488 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "invalid type");
3489 return;
3490 }
3491 if (format != GraphicsContext3D::RGBA || (type != GraphicsContext3D::UNSIGNED_BYTE && type != GraphicsContext3D::FLOAT)) {
3492 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "readPixels", "format not RGBA or type not UNSIGNED_BYTE|FLOAT");
3493 return;
3494 }
3495 }
3496
3497 InternalFormatTheme internalFormatTheme = WebCore::internalFormatTheme(internalFormat);
3498 int internalFormatComponentCount = numberOfComponentsForInternalFormat(internalFormat);
3499 if (internalFormatTheme == InternalFormatTheme::None || !internalFormatComponentCount) {
3500 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "Incorrect internal format");
3501 return;
3502 }
3503
3504#define CHECK_COMPONENT_COUNT \
3505 if (numberOfComponentsForFormat(format) < internalFormatComponentCount) { \
3506 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "Not enough components in format"); \
3507 return; \
3508 }
3509
3510#define INTERNAL_FORMAT_CHECK(typeMacro, pixelTypeMacro) \
3511 if (type != GraphicsContext3D::typeMacro || pixels.getType() != JSC::pixelTypeMacro) { \
3512 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "type does not match internal format"); \
3513 return; \
3514 } \
3515 if (format != GraphicsContext3D::RED && format != GraphicsContext3D::RG && format != GraphicsContext3D::RGB && format != GraphicsContext3D::RGBA) { \
3516 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "Unknown format"); \
3517 return; \
3518 } \
3519 CHECK_COMPONENT_COUNT
3520
3521#define INTERNAL_FORMAT_INTEGER_CHECK(typeMacro, pixelTypeMacro) \
3522 if (type != GraphicsContext3D::typeMacro || pixels.getType() != JSC::pixelTypeMacro) { \
3523 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "type does not match internal format"); \
3524 return; \
3525 } \
3526 if (format != GraphicsContext3D::RED_INTEGER && format != GraphicsContext3D::RG_INTEGER && format != GraphicsContext3D::RGB_INTEGER && format != GraphicsContext3D::RGBA_INTEGER) { \
3527 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "Unknown format"); \
3528 return; \
3529 } \
3530 CHECK_COMPONENT_COUNT
3531
3532#define CASE_PACKED_INTERNAL_FORMAT_CHECK(internalFormatMacro, formatMacro, type0Macro, pixelType0Macro, type1Macro, pixelType1Macro) case GraphicsContext3D::internalFormatMacro: \
3533 if (!(type == GraphicsContext3D::type0Macro && pixels.getType() == JSC::pixelType0Macro) \
3534 && !(type == GraphicsContext3D::type1Macro && pixels.getType() == JSC::pixelType1Macro)) { \
3535 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "type does not match internal format"); \
3536 return; \
3537 } \
3538 if (format != GraphicsContext3D::formatMacro) { \
3539 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "readPixels", "Invalid format"); \
3540 return; \
3541 } \
3542 break;
3543
3544 switch (internalFormatTheme) {
3545 case InternalFormatTheme::NormalizedFixedPoint:
3546 if (type == GraphicsContext3D::FLOAT) {
3547 INTERNAL_FORMAT_CHECK(FLOAT, TypeFloat32);
3548 } else {
3549 INTERNAL_FORMAT_CHECK(UNSIGNED_BYTE, TypeUint8);
3550 }
3551 break;
3552 case InternalFormatTheme::SignedNormalizedFixedPoint:
3553 INTERNAL_FORMAT_CHECK(BYTE, TypeInt8);
3554 break;
3555 case InternalFormatTheme::FloatingPoint:
3556 INTERNAL_FORMAT_CHECK(FLOAT, TypeFloat32);
3557 break;
3558 case InternalFormatTheme::SignedInteger:
3559 INTERNAL_FORMAT_INTEGER_CHECK(INT, TypeInt32);
3560 break;
3561 case InternalFormatTheme::UnsignedInteger:
3562 INTERNAL_FORMAT_INTEGER_CHECK(UNSIGNED_INT, TypeUint32);
3563 break;
3564 case InternalFormatTheme::Packed:
3565 switch (internalFormat) {
3566 CASE_PACKED_INTERNAL_FORMAT_CHECK(RGB565 , RGB , UNSIGNED_SHORT_5_6_5 , TypeUint16, UNSIGNED_BYTE , TypeUint8 );
3567 CASE_PACKED_INTERNAL_FORMAT_CHECK(RGB5_A1 , RGBA , UNSIGNED_SHORT_5_5_5_1 , TypeUint16, UNSIGNED_BYTE , TypeUint8 );
3568 CASE_PACKED_INTERNAL_FORMAT_CHECK(RGBA4 , RGBA , UNSIGNED_SHORT_4_4_4_4 , TypeUint16, UNSIGNED_BYTE , TypeUint8 );
3569 CASE_PACKED_INTERNAL_FORMAT_CHECK(RGB9_E5 , RGB , UNSIGNED_INT_5_9_9_9_REV , TypeUint32, UNSIGNED_INT_5_9_9_9_REV , TypeUint32 );
3570 CASE_PACKED_INTERNAL_FORMAT_CHECK(RGB10_A2 , RGBA , UNSIGNED_INT_2_10_10_10_REV , TypeUint32, UNSIGNED_INT_2_10_10_10_REV, TypeUint32 );
3571 CASE_PACKED_INTERNAL_FORMAT_CHECK(R11F_G11F_B10F, RGB , UNSIGNED_INT_10F_11F_11F_REV, TypeUint32, FLOAT , TypeFloat32);
3572 CASE_PACKED_INTERNAL_FORMAT_CHECK(RGB10_A2UI , RGBA_INTEGER, UNSIGNED_INT_2_10_10_10_REV , TypeUint32, UNSIGNED_INT_2_10_10_10_REV, TypeUint32 );
3573 }
3574 break;
3575 case InternalFormatTheme::None:
3576 ASSERT_NOT_REACHED();
3577 }
3578#undef CHECK_COMPONENT_COUNT
3579#undef INTERNAL_FORMAT_CHECK
3580#undef INTERNAL_FORMAT_INTEGER_CHECK
3581#undef CASE_PACKED_INTERNAL_FORMAT_CHECK
3582
3583 // Calculate array size, taking into consideration of PACK_ALIGNMENT.
3584 unsigned totalBytesRequired = 0;
3585 unsigned padding = 0;
3586 if (!m_isRobustnessEXTSupported) {
3587 GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_packAlignment, &totalBytesRequired, &padding);
3588 if (error != GraphicsContext3D::NO_ERROR) {
3589 synthesizeGLError(error, "readPixels", "invalid dimensions");
3590 return;
3591 }
3592 if (pixels.byteLength() < totalBytesRequired) {
3593 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "readPixels", "ArrayBufferView not large enough for dimensions");
3594 return;
3595 }
3596 }
3597
3598 clearIfComposited();
3599 void* data = pixels.baseAddress();
3600
3601 if (m_isRobustnessEXTSupported)
3602 m_context->getExtensions().readnPixelsEXT(x, y, width, height, format, type, pixels.byteLength(), data);
3603 else
3604 m_context->readPixels(x, y, width, height, format, type, data);
3605}
3606
3607void WebGLRenderingContextBase::releaseShaderCompiler()
3608{
3609 if (isContextLostOrPending())
3610 return;
3611 m_context->releaseShaderCompiler();
3612}
3613
3614void WebGLRenderingContextBase::sampleCoverage(GC3Dfloat value, GC3Dboolean invert)
3615{
3616 if (isContextLostOrPending())
3617 return;
3618 m_context->sampleCoverage(value, invert);
3619}
3620
3621void WebGLRenderingContextBase::scissor(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height)
3622{
3623 if (isContextLostOrPending())
3624 return;
3625 if (!validateSize("scissor", width, height))
3626 return;
3627 m_context->scissor(x, y, width, height);
3628}
3629
3630void WebGLRenderingContextBase::shaderSource(WebGLShader* shader, const String& string)
3631{
3632 if (isContextLostOrPending() || !validateWebGLObject("shaderSource", shader))
3633 return;
3634 String stringWithoutComments = StripComments(string).result();
3635 if (!validateString("shaderSource", stringWithoutComments))
3636 return;
3637 shader->setSource(string);
3638 m_context->shaderSource(objectOrZero(shader), stringWithoutComments);
3639}
3640
3641void WebGLRenderingContextBase::stencilFunc(GC3Denum func, GC3Dint ref, GC3Duint mask)
3642{
3643 if (isContextLostOrPending())
3644 return;
3645 if (!validateStencilFunc("stencilFunc", func))
3646 return;
3647 m_stencilFuncRef = ref;
3648 m_stencilFuncRefBack = ref;
3649 m_stencilFuncMask = mask;
3650 m_stencilFuncMaskBack = mask;
3651 m_context->stencilFunc(func, ref, mask);
3652}
3653
3654void WebGLRenderingContextBase::stencilFuncSeparate(GC3Denum face, GC3Denum func, GC3Dint ref, GC3Duint mask)
3655{
3656 if (isContextLostOrPending())
3657 return;
3658 if (!validateStencilFunc("stencilFuncSeparate", func))
3659 return;
3660 switch (face) {
3661 case GraphicsContext3D::FRONT_AND_BACK:
3662 m_stencilFuncRef = ref;
3663 m_stencilFuncRefBack = ref;
3664 m_stencilFuncMask = mask;
3665 m_stencilFuncMaskBack = mask;
3666 break;
3667 case GraphicsContext3D::FRONT:
3668 m_stencilFuncRef = ref;
3669 m_stencilFuncMask = mask;
3670 break;
3671 case GraphicsContext3D::BACK:
3672 m_stencilFuncRefBack = ref;
3673 m_stencilFuncMaskBack = mask;
3674 break;
3675 default:
3676 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "stencilFuncSeparate", "invalid face");
3677 return;
3678 }
3679 m_context->stencilFuncSeparate(face, func, ref, mask);
3680}
3681
3682void WebGLRenderingContextBase::stencilMask(GC3Duint mask)
3683{
3684 if (isContextLostOrPending())
3685 return;
3686 m_stencilMask = mask;
3687 m_stencilMaskBack = mask;
3688 m_context->stencilMask(mask);
3689}
3690
3691void WebGLRenderingContextBase::stencilMaskSeparate(GC3Denum face, GC3Duint mask)
3692{
3693 if (isContextLostOrPending())
3694 return;
3695 switch (face) {
3696 case GraphicsContext3D::FRONT_AND_BACK:
3697 m_stencilMask = mask;
3698 m_stencilMaskBack = mask;
3699 break;
3700 case GraphicsContext3D::FRONT:
3701 m_stencilMask = mask;
3702 break;
3703 case GraphicsContext3D::BACK:
3704 m_stencilMaskBack = mask;
3705 break;
3706 default:
3707 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "stencilMaskSeparate", "invalid face");
3708 return;
3709 }
3710 m_context->stencilMaskSeparate(face, mask);
3711}
3712
3713void WebGLRenderingContextBase::stencilOp(GC3Denum fail, GC3Denum zfail, GC3Denum zpass)
3714{
3715 if (isContextLostOrPending())
3716 return;
3717 m_context->stencilOp(fail, zfail, zpass);
3718}
3719
3720void WebGLRenderingContextBase::stencilOpSeparate(GC3Denum face, GC3Denum fail, GC3Denum zfail, GC3Denum zpass)
3721{
3722 if (isContextLostOrPending())
3723 return;
3724 m_context->stencilOpSeparate(face, fail, zfail, zpass);
3725}
3726
3727void WebGLRenderingContextBase::texImage2DBase(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, const void* pixels)
3728{
3729 // FIXME: For now we ignore any errors returned.
3730 auto tex = validateTextureBinding("texImage2D", target, true);
3731 ASSERT(validateTexFuncParameters("texImage2D", TexImage, target, level, internalFormat, width, height, border, format, type));
3732 ASSERT(tex);
3733 ASSERT(validateNPOTTextureLevel(width, height, level, "texImage2D"));
3734 if (!pixels) {
3735 if (!m_context->texImage2DResourceSafe(target, level, internalFormat, width, height, border, format, type, m_unpackAlignment))
3736 return;
3737 } else {
3738 ASSERT(validateSettableTexInternalFormat("texImage2D", internalFormat));
3739 m_context->moveErrorsToSyntheticErrorList();
3740 m_context->texImage2D(target, level, internalFormat, width, height,
3741 border, format, type, pixels);
3742 if (m_context->moveErrorsToSyntheticErrorList()) {
3743 // The texImage2D function failed. Tell the WebGLTexture it doesn't have the data for this level.
3744 tex->markInvalid(target, level);
3745 return;
3746 }
3747 }
3748 tex->setLevelInfo(target, level, internalFormat, width, height, type);
3749}
3750
3751void WebGLRenderingContextBase::texImage2DImpl(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Denum format, GC3Denum type, Image* image, GraphicsContext3D::ImageHtmlDomSource domSource, bool flipY, bool premultiplyAlpha)
3752{
3753 Vector<uint8_t> data;
3754 GraphicsContext3D::ImageExtractor imageExtractor(image, domSource, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE);
3755 if (!imageExtractor.extractSucceeded()) {
3756 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "bad image data");
3757 return;
3758 }
3759 GraphicsContext3D::DataFormat sourceDataFormat = imageExtractor.imageSourceFormat();
3760 GraphicsContext3D::AlphaOp alphaOp = imageExtractor.imageAlphaOp();
3761 const void* imagePixelData = imageExtractor.imagePixelData();
3762
3763 bool needConversion = true;
3764 if (type == GraphicsContext3D::UNSIGNED_BYTE && sourceDataFormat == GraphicsContext3D::DataFormatRGBA8 && format == GraphicsContext3D::RGBA && alphaOp == GraphicsContext3D::AlphaDoNothing && !flipY)
3765 needConversion = false;
3766 else {
3767 if (!m_context->packImageData(image, imagePixelData, format, type, flipY, alphaOp, sourceDataFormat, imageExtractor.imageWidth(), imageExtractor.imageHeight(), imageExtractor.imageSourceUnpackAlignment(), data)) {
3768 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "packImage error");
3769 return;
3770 }
3771 }
3772
3773 if (m_unpackAlignment != 1)
3774 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
3775 texImage2DBase(target, level, internalformat, image->width(), image->height(), 0, format, type, needConversion ? data.data() : imagePixelData);
3776 if (m_unpackAlignment != 1)
3777 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment);
3778}
3779
3780bool WebGLRenderingContextBase::validateTexFunc(const char* functionName, TexFuncValidationFunctionType functionType, TexFuncValidationSourceType sourceType, GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, GC3Dint xoffset, GC3Dint yoffset)
3781{
3782 if (!validateTexFuncParameters(functionName, functionType, target, level, internalFormat, width, height, border, format, type))
3783 return false;
3784
3785 auto texture = validateTextureBinding(functionName, target, true);
3786 if (!texture)
3787 return false;
3788
3789 if (functionType != TexSubImage) {
3790 if (functionType == TexImage && texture->immutable()) {
3791 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "texStorage() called on this texture previously");
3792 return false;
3793 }
3794 if (!validateNPOTTextureLevel(width, height, level, functionName))
3795 return false;
3796 // For SourceArrayBufferView, function validateTexFuncData() would handle whether to validate the SettableTexFormat
3797 // by checking if the ArrayBufferView is null or not.
3798 if (sourceType != SourceArrayBufferView) {
3799 if (!validateSettableTexInternalFormat(functionName, internalFormat))
3800 return false;
3801 }
3802 } else {
3803 if (!validateSettableTexInternalFormat(functionName, internalFormat))
3804 return false;
3805 if (!validateSize(functionName, xoffset, yoffset))
3806 return false;
3807 // Before checking if it is in the range, check if overflow happens first.
3808 if (xoffset + width < 0 || yoffset + height < 0) {
3809 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "bad dimensions");
3810 return false;
3811 }
3812 if (xoffset + width > texture->getWidth(target, level) || yoffset + height > texture->getHeight(target, level)) {
3813 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "dimensions out of range");
3814 return false;
3815 }
3816 if (texture->getInternalFormat(target, level) != internalFormat || (isWebGL1() && texture->getType(target, level) != type)) {
3817 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "type and format do not match texture");
3818 return false;
3819 }
3820 }
3821
3822 return true;
3823}
3824
3825void WebGLRenderingContextBase::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, RefPtr<ArrayBufferView>&& pixels)
3826{
3827 if (isContextLostOrPending() || !validateTexFuncData("texImage2D", level, width, height, internalFormat, format, type, pixels.get(), NullAllowed)
3828 || !validateTexFunc("texImage2D", TexImage, SourceArrayBufferView, target, level, internalFormat, width, height, border, format, type, 0, 0))
3829 return;
3830 void* data = pixels ? pixels->baseAddress() : 0;
3831 Vector<uint8_t> tempData;
3832 bool changeUnpackAlignment = false;
3833 if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) {
3834 if (!m_context->extractTextureData(width, height, format, type,
3835 m_unpackAlignment,
3836 m_unpackFlipY, m_unpackPremultiplyAlpha,
3837 data,
3838 tempData))
3839 return;
3840 data = tempData.data();
3841 changeUnpackAlignment = true;
3842 }
3843 if (changeUnpackAlignment)
3844 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
3845 texImage2DBase(target, level, internalFormat, width, height, border, format, type, data);
3846 if (changeUnpackAlignment)
3847 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment);
3848}
3849
3850void WebGLRenderingContextBase::texSubImage2DImpl(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Denum format, GC3Denum type, Image* image, GraphicsContext3D::ImageHtmlDomSource domSource, bool flipY, bool premultiplyAlpha)
3851{
3852 Vector<uint8_t> data;
3853 GraphicsContext3D::ImageExtractor imageExtractor(image, domSource, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE);
3854 if (!imageExtractor.extractSucceeded()) {
3855 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "bad image");
3856 return;
3857 }
3858 GraphicsContext3D::DataFormat sourceDataFormat = imageExtractor.imageSourceFormat();
3859 GraphicsContext3D::AlphaOp alphaOp = imageExtractor.imageAlphaOp();
3860 const void* imagePixelData = imageExtractor.imagePixelData();
3861
3862 bool needConversion = true;
3863 if (type == GraphicsContext3D::UNSIGNED_BYTE && sourceDataFormat == GraphicsContext3D::DataFormatRGBA8 && format == GraphicsContext3D::RGBA && alphaOp == GraphicsContext3D::AlphaDoNothing && !flipY)
3864 needConversion = false;
3865 else {
3866 if (!m_context->packImageData(image, imagePixelData, format, type, flipY, alphaOp, sourceDataFormat, imageExtractor.imageWidth(), imageExtractor.imageHeight(), imageExtractor.imageSourceUnpackAlignment(), data)) {
3867 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "bad image data");
3868 return;
3869 }
3870 }
3871
3872 if (m_unpackAlignment != 1)
3873 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
3874
3875 texSubImage2DBase(target, level, xoffset, yoffset, image->width(), image->height(), format, format, type, needConversion ? data.data() : imagePixelData);
3876
3877 if (m_unpackAlignment != 1)
3878 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment);
3879}
3880
3881void WebGLRenderingContextBase::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, RefPtr<ArrayBufferView>&& pixels)
3882{
3883 if (isContextLostOrPending())
3884 return;
3885
3886 auto texture = validateTextureBinding("texSubImage2D", target, true);
3887 if (!texture)
3888 return;
3889
3890 GC3Denum internalFormat = texture->getInternalFormat(target, level);
3891 if (!internalFormat) {
3892 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "invalid texture target or level");
3893 return;
3894 }
3895
3896 if (!validateTexFuncData("texSubImage2D", level, width, height, internalFormat, format, type, pixels.get(), NullNotAllowed))
3897 return;
3898
3899 if (!validateTexFunc("texSubImage2D", TexSubImage, SourceArrayBufferView, target, level, internalFormat, width, height, 0, format, type, xoffset, yoffset))
3900 return;
3901
3902 void* data = pixels->baseAddress();
3903 Vector<uint8_t> tempData;
3904 bool changeUnpackAlignment = false;
3905 if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) {
3906 if (!m_context->extractTextureData(width, height, format, type, m_unpackAlignment, m_unpackFlipY, m_unpackPremultiplyAlpha, data, tempData))
3907 return;
3908 data = tempData.data();
3909 changeUnpackAlignment = true;
3910 }
3911 if (changeUnpackAlignment)
3912 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
3913
3914 texSubImage2DBase(target, level, xoffset, yoffset, width, height, internalFormat, format, type, data);
3915
3916 if (changeUnpackAlignment)
3917 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment);
3918}
3919
3920ExceptionOr<void> WebGLRenderingContextBase::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Denum format, GC3Denum type, Optional<TexImageSource>&& source)
3921{
3922 if (!source) {
3923 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "source is null");
3924 return { };
3925 }
3926
3927 if (isContextLostOrPending())
3928 return { };
3929
3930 auto visitor = WTF::makeVisitor([&](const RefPtr<ImageBitmap>& bitmap) -> ExceptionOr<void> {
3931 auto texture = validateTextureBinding("texSubImage2D", target, true);
3932 if (!texture)
3933 return { };
3934
3935 GC3Denum internalFormat = texture->getInternalFormat(target, level);
3936 if (!internalFormat) {
3937 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "invalid texture target or level");
3938 return { };
3939 }
3940
3941 if (!validateTexFunc("texSubImage2D", TexSubImage, SourceImageBitmap, target, level, internalFormat, bitmap->width(), bitmap->height(), 0, format, type, xoffset, yoffset))
3942 return { };
3943
3944 ImageBuffer* buffer = bitmap->buffer();
3945 if (!buffer)
3946 return { };
3947
3948 RefPtr<Image> image = buffer->copyImage(ImageBuffer::fastCopyImageMode());
3949 if (image)
3950 texSubImage2DImpl(target, level, xoffset, yoffset, format, type, image.get(), GraphicsContext3D::HtmlDomImage, m_unpackFlipY, m_unpackPremultiplyAlpha);
3951 return { };
3952 }, [&](const RefPtr<ImageData>& pixels) -> ExceptionOr<void> {
3953 auto texture = validateTextureBinding("texSubImage2D", target, true);
3954 if (!texture)
3955 return { };
3956
3957 GC3Denum internalFormat = texture->getInternalFormat(target, level);
3958 if (!internalFormat) {
3959 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "invalid texture target or level");
3960 return { };
3961 }
3962
3963 if (!validateTexFunc("texSubImage2D", TexSubImage, SourceImageData, target, level, internalFormat, pixels->width(), pixels->height(), 0, format, type, xoffset, yoffset))
3964 return { };
3965
3966 Vector<uint8_t> data;
3967 bool needConversion = true;
3968 // The data from ImageData is always of format RGBA8.
3969 // No conversion is needed if destination format is RGBA and type is USIGNED_BYTE and no Flip or Premultiply operation is required.
3970 if (format == GraphicsContext3D::RGBA && type == GraphicsContext3D::UNSIGNED_BYTE && !m_unpackFlipY && !m_unpackPremultiplyAlpha)
3971 needConversion = false;
3972 else {
3973 if (!m_context->extractImageData(pixels.get(), format, type, m_unpackFlipY, m_unpackPremultiplyAlpha, data)) {
3974 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "bad image data");
3975 return { };
3976 }
3977 }
3978 if (m_unpackAlignment != 1)
3979 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
3980
3981 texSubImage2DBase(target, level, xoffset, yoffset, pixels->width(), pixels->height(), format, format, type, needConversion ? data.data() : pixels->data()->data());
3982
3983 if (m_unpackAlignment != 1)
3984 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment);
3985
3986 return { };
3987 } , [&](const RefPtr<HTMLImageElement>& image) -> ExceptionOr<void> {
3988 if (isContextLostOrPending())
3989 return { };
3990 auto validationResult = validateHTMLImageElement("texSubImage2D", image.get());
3991 if (validationResult.hasException())
3992 return validationResult.releaseException();
3993 if (!validationResult.returnValue())
3994 return { };
3995
3996 RefPtr<Image> imageForRender = image->cachedImage()->imageForRenderer(image->renderer());
3997 if (!imageForRender)
3998 return { };
3999
4000 if (imageForRender->isSVGImage())
4001 imageForRender = drawImageIntoBuffer(*imageForRender, image->width(), image->height(), 1);
4002
4003 auto texture = validateTextureBinding("texSubImage2D", target, true);
4004 if (!texture)
4005 return { };
4006
4007 GC3Denum internalFormat = texture->getInternalFormat(target, level);
4008 if (!internalFormat) {
4009 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "invalid texture target or level");
4010 return { };
4011 }
4012
4013 if (!imageForRender || !validateTexFunc("texSubImage2D", TexSubImage, SourceHTMLImageElement, target, level, internalFormat, imageForRender->width(), imageForRender->height(), 0, format, type, xoffset, yoffset))
4014 return { };
4015
4016 texSubImage2DImpl(target, level, xoffset, yoffset, format, type, imageForRender.get(), GraphicsContext3D::HtmlDomImage, m_unpackFlipY, m_unpackPremultiplyAlpha);
4017 return { };
4018 }, [&](const RefPtr<HTMLCanvasElement>& canvas) -> ExceptionOr<void> {
4019 if (isContextLostOrPending())
4020 return { };
4021 auto validationResult = validateHTMLCanvasElement("texSubImage2D", canvas.get());
4022 if (validationResult.hasException())
4023 return validationResult.releaseException();
4024 if (!validationResult.returnValue())
4025 return { };
4026
4027 auto texture = validateTextureBinding("texSubImage2D", target, true);
4028 if (!texture)
4029 return { };
4030
4031 GC3Denum internalFormat = texture->getInternalFormat(target, level);
4032 if (!internalFormat) {
4033 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "invalid texture target or level");
4034 return { };
4035 }
4036
4037 if (!validateTexFunc("texSubImage2D", TexSubImage, SourceHTMLCanvasElement, target, level, internalFormat, canvas->width(), canvas->height(), 0, format, type, xoffset, yoffset))
4038 return { };
4039
4040 RefPtr<ImageData> imageData = canvas->getImageData();
4041 if (imageData)
4042 texSubImage2D(target, level, xoffset, yoffset, format, type, TexImageSource(imageData.get()));
4043 else
4044 texSubImage2DImpl(target, level, xoffset, yoffset, format, type, canvas->copiedImage(), GraphicsContext3D::HtmlDomCanvas, m_unpackFlipY, m_unpackPremultiplyAlpha);
4045 return { };
4046 }
4047#if ENABLE(VIDEO)
4048 , [&](const RefPtr<HTMLVideoElement>& video) -> ExceptionOr<void> {
4049 if (isContextLostOrPending())
4050 return { };
4051 auto validationResult = validateHTMLVideoElement("texSubImage2D", video.get());
4052 if (validationResult.hasException())
4053 return validationResult.releaseException();
4054 if (!validationResult.returnValue())
4055 return { };
4056
4057 auto texture = validateTextureBinding("texSubImage2D", target, true);
4058 if (!texture)
4059 return { };
4060
4061 GC3Denum internalFormat = texture->getInternalFormat(target, level);
4062 if (!internalFormat) {
4063 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "invalid texture target or level");
4064 return { };
4065 }
4066
4067 if (!validateTexFunc("texSubImage2D", TexSubImage, SourceHTMLVideoElement, target, level, internalFormat, video->videoWidth(), video->videoHeight(), 0, format, type, xoffset, yoffset))
4068 return { };
4069
4070 RefPtr<Image> image = videoFrameToImage(video.get(), ImageBuffer::fastCopyImageMode());
4071 if (!image)
4072 return { };
4073 texSubImage2DImpl(target, level, xoffset, yoffset, format, type, image.get(), GraphicsContext3D::HtmlDomVideo, m_unpackFlipY, m_unpackPremultiplyAlpha);
4074 return { };
4075 }
4076#endif
4077 );
4078
4079 return WTF::visit(visitor, source.value());
4080}
4081
4082bool WebGLRenderingContextBase::validateArrayBufferType(const char* functionName, GC3Denum type, Optional<JSC::TypedArrayType> arrayType)
4083{
4084#define TYPE_VALIDATION_CASE(arrayTypeMacro) if (arrayType && arrayType.value() != JSC::arrayTypeMacro) { \
4085 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "ArrayBufferView not " #arrayTypeMacro); \
4086 return false; \
4087 } \
4088 break;
4089
4090 switch (type) {
4091 case GraphicsContext3D::UNSIGNED_BYTE:
4092 TYPE_VALIDATION_CASE(TypeUint8);
4093 case GraphicsContext3D::BYTE:
4094 TYPE_VALIDATION_CASE(TypeInt8);
4095 case GraphicsContext3D::UNSIGNED_SHORT:
4096 case GraphicsContext3D::UNSIGNED_SHORT_5_6_5:
4097 case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4:
4098 case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1:
4099 TYPE_VALIDATION_CASE(TypeUint16);
4100 case GraphicsContext3D::SHORT:
4101 TYPE_VALIDATION_CASE(TypeInt16);
4102 case GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV:
4103 case GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV:
4104 case GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV:
4105 case GraphicsContext3D::UNSIGNED_INT_24_8:
4106 case GraphicsContext3D::UNSIGNED_INT:
4107 TYPE_VALIDATION_CASE(TypeUint32);
4108 case GraphicsContext3D::INT:
4109 TYPE_VALIDATION_CASE(TypeInt32);
4110 case GraphicsContext3D::FLOAT: // OES_texture_float
4111 TYPE_VALIDATION_CASE(TypeFloat32);
4112 case GraphicsContext3D::HALF_FLOAT_OES: // OES_texture_half_float
4113 case GraphicsContext3D::HALF_FLOAT:
4114 case GraphicsContext3D::FLOAT_32_UNSIGNED_INT_24_8_REV:
4115 // As per the specification, ArrayBufferView should be null when
4116 // OES_texture_half_float is enabled.
4117 if (arrayType) {
4118 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "type HALF_FLOAT_OES but ArrayBufferView is not NULL");
4119 return false;
4120 }
4121 break;
4122 default:
4123 ASSERT_NOT_REACHED();
4124 return false;
4125 }
4126#undef TYPE_VALIDATION_CASE
4127 return true;
4128}
4129
4130bool WebGLRenderingContextBase::validateTexFuncData(const char* functionName, GC3Dint level, GC3Dsizei width, GC3Dsizei height, GC3Denum internalFormat, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, NullDisposition disposition)
4131{
4132 if (!pixels) {
4133 if (disposition == NullAllowed)
4134 return true;
4135 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no pixels");
4136 return false;
4137 }
4138
4139 if (!validateTexFuncFormatAndType(functionName, internalFormat, format, type, level))
4140 return false;
4141 if (!validateSettableTexInternalFormat(functionName, internalFormat))
4142 return false;
4143 if (!validateArrayBufferType(functionName, type, pixels ? Optional<JSC::TypedArrayType>(pixels->getType()) : WTF::nullopt))
4144 return false;
4145
4146 unsigned totalBytesRequired;
4147 GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &totalBytesRequired, nullptr);
4148 if (error != GraphicsContext3D::NO_ERROR) {
4149 synthesizeGLError(error, functionName, "invalid texture dimensions");
4150 return false;
4151 }
4152 if (pixels->byteLength() < totalBytesRequired) {
4153 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "ArrayBufferView not big enough for request");
4154 return false;
4155 }
4156 return true;
4157}
4158
4159bool WebGLRenderingContextBase::validateTexFuncParameters(const char* functionName,
4160 TexFuncValidationFunctionType functionType,
4161 GC3Denum target, GC3Dint level,
4162 GC3Denum internalformat,
4163 GC3Dsizei width, GC3Dsizei height, GC3Dint border,
4164 GC3Denum format, GC3Denum type)
4165{
4166 // We absolutely have to validate the format and type combination.
4167 // The texImage2D entry points taking HTMLImage, etc. will produce
4168 // temporary data based on this combination, so it must be legal.
4169 if (!validateTexFuncFormatAndType(functionName, internalformat, format, type, level) || !validateTexFuncLevel(functionName, target, level))
4170 return false;
4171
4172 if (width < 0 || height < 0) {
4173 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width or height < 0");
4174 return false;
4175 }
4176
4177 GC3Dint maxTextureSizeForLevel = pow(2.0, m_maxTextureLevel - 1 - level);
4178 switch (target) {
4179 case GraphicsContext3D::TEXTURE_2D:
4180 if (width > maxTextureSizeForLevel || height > maxTextureSizeForLevel) {
4181 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width or height out of range");
4182 return false;
4183 }
4184 break;
4185 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
4186 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
4187 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
4188 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
4189 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
4190 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
4191 if (functionType != TexSubImage && width != height) {
4192 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width != height for cube map");
4193 return false;
4194 }
4195 // No need to check height here. For texImage width == height.
4196 // For texSubImage that will be checked when checking yoffset + height is in range.
4197 if (width > maxTextureSizeForLevel) {
4198 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width or height out of range for cube map");
4199 return false;
4200 }
4201 break;
4202 default:
4203 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid target");
4204 return false;
4205 }
4206
4207 if (border) {
4208 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "border != 0");
4209 return false;
4210 }
4211
4212 return true;
4213}
4214
4215bool WebGLRenderingContextBase::validateTexFuncFormatAndType(const char* functionName, GC3Denum internalFormat, GC3Denum format, GC3Denum type, GC3Dint level)
4216{
4217 switch (format) {
4218 case GraphicsContext3D::ALPHA:
4219 case GraphicsContext3D::LUMINANCE:
4220 case GraphicsContext3D::LUMINANCE_ALPHA:
4221 case GraphicsContext3D::RGB:
4222 case GraphicsContext3D::RGBA:
4223 break;
4224 case GraphicsContext3D::DEPTH_STENCIL:
4225 case GraphicsContext3D::DEPTH_COMPONENT:
4226 if (!m_webglDepthTexture && isWebGL1()) {
4227 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "depth texture formats not enabled");
4228 return false;
4229 }
4230 if (level > 0) {
4231 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "level must be 0 for depth formats");
4232 return false;
4233 }
4234 break;
4235 case Extensions3D::SRGB_EXT:
4236 case Extensions3D::SRGB_ALPHA_EXT:
4237 if (!m_extsRGB) {
4238 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "sRGB texture formats not enabled");
4239 return false;
4240 }
4241 break;
4242 default:
4243#if ENABLE(WEBGL2)
4244 if (!isWebGL1()) {
4245 switch (format) {
4246 case GraphicsContext3D::RED:
4247 case GraphicsContext3D::RED_INTEGER:
4248 case GraphicsContext3D::RG:
4249 case GraphicsContext3D::RG_INTEGER:
4250 case GraphicsContext3D::RGB_INTEGER:
4251 case GraphicsContext3D::RGBA_INTEGER:
4252 break;
4253 default:
4254 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture format");
4255 return false;
4256 }
4257 } else
4258#endif
4259 {
4260 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture format");
4261 return false;
4262 }
4263 }
4264
4265 switch (type) {
4266 case GraphicsContext3D::UNSIGNED_BYTE:
4267 case GraphicsContext3D::UNSIGNED_SHORT_5_6_5:
4268 case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4:
4269 case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1:
4270 break;
4271 case GraphicsContext3D::FLOAT:
4272 if (!m_oesTextureFloat && isWebGL1()) {
4273 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type");
4274 return false;
4275 }
4276 break;
4277 case GraphicsContext3D::HALF_FLOAT:
4278 case GraphicsContext3D::HALF_FLOAT_OES:
4279 if (!m_oesTextureHalfFloat && isWebGL1()) {
4280 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type");
4281 return false;
4282 }
4283 break;
4284 case GraphicsContext3D::UNSIGNED_INT:
4285 case GraphicsContext3D::UNSIGNED_INT_24_8:
4286 case GraphicsContext3D::UNSIGNED_SHORT:
4287 if (!m_webglDepthTexture && isWebGL1()) {
4288 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type");
4289 return false;
4290 }
4291 break;
4292 default:
4293#if ENABLE(WEBGL2)
4294 if (!isWebGL1()) {
4295 switch (type) {
4296 case GraphicsContext3D::BYTE:
4297 case GraphicsContext3D::SHORT:
4298 case GraphicsContext3D::INT:
4299 case GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV:
4300 case GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV:
4301 case GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV:
4302 case GraphicsContext3D::FLOAT_32_UNSIGNED_INT_24_8_REV:
4303 break;
4304 default:
4305 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type");
4306 return false;
4307 }
4308 } else
4309#endif
4310 {
4311 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type");
4312 return false;
4313 }
4314 }
4315
4316 // Verify that the combination of internalformat, format, and type is supported.
4317#define INTERNAL_FORMAT_CASE(internalFormatMacro, formatMacro, type0, type1, type2, type3, type4) case GraphicsContext3D::internalFormatMacro: \
4318 if (format != GraphicsContext3D::formatMacro) { \
4319 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "invalid format for internalformat"); \
4320 return false; \
4321 } \
4322 if (type != type0 && type != type1 && type != type2 && type != type3 && type != type4) { \
4323 if (type != GraphicsContext3D::HALF_FLOAT_OES || (type0 != GraphicsContext3D::HALF_FLOAT && type1 != GraphicsContext3D::HALF_FLOAT && type2 != GraphicsContext3D::HALF_FLOAT && type3 != GraphicsContext3D::HALF_FLOAT)) { \
4324 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "invalid type for internalformat"); \
4325 return false; \
4326 } \
4327 } \
4328 break;
4329 switch (internalFormat) {
4330 INTERNAL_FORMAT_CASE(RGB , RGB , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::UNSIGNED_SHORT_5_6_5 , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 );
4331 INTERNAL_FORMAT_CASE(RGBA , RGBA , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4, GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1 , GraphicsContext3D::HALF_FLOAT, GraphicsContext3D::FLOAT);
4332 INTERNAL_FORMAT_CASE(LUMINANCE_ALPHA , LUMINANCE_ALPHA, GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 );
4333 INTERNAL_FORMAT_CASE(LUMINANCE , LUMINANCE , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 );
4334 INTERNAL_FORMAT_CASE(ALPHA , ALPHA , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 );
4335 INTERNAL_FORMAT_CASE(R8 , RED , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4336 INTERNAL_FORMAT_CASE(R8_SNORM , RED , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4337 INTERNAL_FORMAT_CASE(R16F , RED , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 , 0 );
4338 INTERNAL_FORMAT_CASE(R32F , RED , GraphicsContext3D::FLOAT , 0 , 0 , 0 , 0 );
4339 INTERNAL_FORMAT_CASE(R8UI , RED_INTEGER , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4340 INTERNAL_FORMAT_CASE(R8I , RED_INTEGER , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4341 INTERNAL_FORMAT_CASE(R16UI , RED_INTEGER , GraphicsContext3D::UNSIGNED_SHORT , 0 , 0 , 0 , 0 );
4342 INTERNAL_FORMAT_CASE(R16I , RED_INTEGER , GraphicsContext3D::SHORT , 0 , 0 , 0 , 0 );
4343 INTERNAL_FORMAT_CASE(R32UI , RED_INTEGER , GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 , 0 );
4344 INTERNAL_FORMAT_CASE(R32I , RED_INTEGER , GraphicsContext3D::INT , 0 , 0 , 0 , 0 );
4345 INTERNAL_FORMAT_CASE(RG8 , RG , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4346 INTERNAL_FORMAT_CASE(RG8_SNORM , RG , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4347 INTERNAL_FORMAT_CASE(RG16F , RG , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 , 0 );
4348 INTERNAL_FORMAT_CASE(RG32F , RG , GraphicsContext3D::FLOAT , 0 , 0 , 0 , 0 );
4349 INTERNAL_FORMAT_CASE(RG8UI , RG_INTEGER , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4350 INTERNAL_FORMAT_CASE(RG8I , RG_INTEGER , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4351 INTERNAL_FORMAT_CASE(RG16UI , RG_INTEGER , GraphicsContext3D::UNSIGNED_SHORT , 0 , 0 , 0 , 0 );
4352 INTERNAL_FORMAT_CASE(RG16I , RG_INTEGER , GraphicsContext3D::SHORT , 0 , 0 , 0 , 0 );
4353 INTERNAL_FORMAT_CASE(RG32UI , RG_INTEGER , GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 , 0 );
4354 INTERNAL_FORMAT_CASE(RG32I , RG_INTEGER , GraphicsContext3D::INT , 0 , 0 , 0 , 0 );
4355 INTERNAL_FORMAT_CASE(RGB8 , RGB , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4356 INTERNAL_FORMAT_CASE(SRGB8 , RGB , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4357 INTERNAL_FORMAT_CASE(RGB565 , RGB , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::UNSIGNED_SHORT_5_6_5 , 0 , 0 , 0 );
4358 INTERNAL_FORMAT_CASE(RGB8_SNORM , RGB , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4359 INTERNAL_FORMAT_CASE(R11F_G11F_B10F , RGB , GraphicsContext3D::UNSIGNED_INT_10F_11F_11F_REV , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 );
4360 INTERNAL_FORMAT_CASE(RGB9_E5 , RGB , GraphicsContext3D::UNSIGNED_INT_5_9_9_9_REV , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 );
4361 INTERNAL_FORMAT_CASE(RGB16F , RGB , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 , 0 );
4362 INTERNAL_FORMAT_CASE(RGB32F , RGB , GraphicsContext3D::FLOAT , 0 , 0 , 0 , 0 );
4363 INTERNAL_FORMAT_CASE(RGB8UI , RGB_INTEGER , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4364 INTERNAL_FORMAT_CASE(RGB8I , RGB_INTEGER , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4365 INTERNAL_FORMAT_CASE(RGB16UI , RGB_INTEGER , GraphicsContext3D::UNSIGNED_SHORT , 0 , 0 , 0 , 0 );
4366 INTERNAL_FORMAT_CASE(RGB16I , RGB_INTEGER , GraphicsContext3D::SHORT , 0 , 0 , 0 , 0 );
4367 INTERNAL_FORMAT_CASE(RGB32UI , RGB_INTEGER , GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 , 0 );
4368 INTERNAL_FORMAT_CASE(RGB32I , RGB_INTEGER , GraphicsContext3D::INT , 0 , 0 , 0 , 0 );
4369 INTERNAL_FORMAT_CASE(RGBA8 , RGBA , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4370 INTERNAL_FORMAT_CASE(SRGB8_ALPHA8 , RGBA , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4371 INTERNAL_FORMAT_CASE(RGBA8_SNORM , RGBA , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4372 INTERNAL_FORMAT_CASE(RGB5_A1 , RGBA , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1, GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV , 0 , 0 );
4373 INTERNAL_FORMAT_CASE(RGBA4 , RGBA , GraphicsContext3D::UNSIGNED_BYTE , GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4, 0 , 0 , 0 );
4374 INTERNAL_FORMAT_CASE(RGB10_A2 , RGBA , GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV , 0 , 0 , 0 , 0 );
4375 INTERNAL_FORMAT_CASE(RGBA16F , RGBA , GraphicsContext3D::HALF_FLOAT , GraphicsContext3D::FLOAT , 0 , 0 , 0 );
4376 INTERNAL_FORMAT_CASE(RGBA32F , RGBA , GraphicsContext3D::FLOAT , 0 , 0 , 0 , 0 );
4377 INTERNAL_FORMAT_CASE(RGBA8UI , RGBA_INTEGER , GraphicsContext3D::UNSIGNED_BYTE , 0 , 0 , 0 , 0 );
4378 INTERNAL_FORMAT_CASE(RGBA8I , RGBA_INTEGER , GraphicsContext3D::BYTE , 0 , 0 , 0 , 0 );
4379 INTERNAL_FORMAT_CASE(RGB10_A2UI , RGBA_INTEGER , GraphicsContext3D::UNSIGNED_INT_2_10_10_10_REV , 0 , 0 , 0 , 0 );
4380 INTERNAL_FORMAT_CASE(RGBA16UI , RGBA_INTEGER , GraphicsContext3D::UNSIGNED_SHORT , 0 , 0 , 0 , 0 );
4381 INTERNAL_FORMAT_CASE(RGBA16I , RGBA_INTEGER , GraphicsContext3D::SHORT , 0 , 0 , 0 , 0 );
4382 INTERNAL_FORMAT_CASE(RGBA32I , RGBA_INTEGER , GraphicsContext3D::INT , 0 , 0 , 0 , 0 );
4383 INTERNAL_FORMAT_CASE(RGBA32UI , RGBA_INTEGER , GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 , 0 );
4384 INTERNAL_FORMAT_CASE(DEPTH_COMPONENT , DEPTH_COMPONENT, GraphicsContext3D::UNSIGNED_SHORT , GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 );
4385 INTERNAL_FORMAT_CASE(DEPTH_COMPONENT16 , DEPTH_COMPONENT, GraphicsContext3D::UNSIGNED_SHORT , GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 );
4386 INTERNAL_FORMAT_CASE(DEPTH_COMPONENT24 , DEPTH_COMPONENT, GraphicsContext3D::UNSIGNED_INT , 0 , 0 , 0 , 0 );
4387 INTERNAL_FORMAT_CASE(DEPTH_COMPONENT32F, DEPTH_COMPONENT, GraphicsContext3D::FLOAT , 0 , 0 , 0 , 0 );
4388 INTERNAL_FORMAT_CASE(DEPTH_STENCIL , DEPTH_STENCIL , GraphicsContext3D::UNSIGNED_INT_24_8 , 0 , 0 , 0 , 0 );
4389 INTERNAL_FORMAT_CASE(DEPTH24_STENCIL8 , DEPTH_STENCIL , GraphicsContext3D::UNSIGNED_INT_24_8 , 0 , 0 , 0 , 0 );
4390 INTERNAL_FORMAT_CASE(DEPTH32F_STENCIL8 , DEPTH_STENCIL , GraphicsContext3D::FLOAT_32_UNSIGNED_INT_24_8_REV, 0 , 0 , 0 , 0 );
4391 case Extensions3D::SRGB_EXT:
4392 if (format != internalFormat) {
4393 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "format and internalformat must match");
4394 return false;
4395 }
4396 if (type != GraphicsContext3D::UNSIGNED_BYTE && type != GraphicsContext3D::UNSIGNED_SHORT_5_6_5 && type != GraphicsContext3D::FLOAT && type != GraphicsContext3D::HALF_FLOAT_OES && type != GraphicsContext3D::HALF_FLOAT) {
4397 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "invalid type for internal format");
4398 return false;
4399 }
4400 break;
4401 case Extensions3D::SRGB_ALPHA_EXT:
4402 if (format != internalFormat) {
4403 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "format and internalformat must match");
4404 return false;
4405 }
4406 if (type != GraphicsContext3D::UNSIGNED_BYTE && type != GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4 && type != GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1 && type != GraphicsContext3D::FLOAT && type != GraphicsContext3D::HALF_FLOAT_OES && type != GraphicsContext3D::HALF_FLOAT) {
4407 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "invalid type for internal format");
4408 return false;
4409 }
4410 break;
4411 default:
4412 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "Unknown internal format");
4413 return false;
4414 }
4415#undef INTERNAL_FORMAT_CASE
4416
4417 return true;
4418}
4419
4420void WebGLRenderingContextBase::texSubImage2DBase(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dsizei width, GC3Dsizei height, GC3Denum internalFormat, GC3Denum format, GC3Denum type, const void* pixels)
4421{
4422 ASSERT(!isContextLost());
4423 ASSERT(validateTexFuncParameters("texSubImage2D", TexSubImage, target, level, internalFormat, width, height, 0, format, type));
4424 ASSERT(validateSize("texSubImage2D", xoffset, yoffset));
4425 ASSERT(validateSettableTexInternalFormat("texSubImage2D", internalFormat));
4426 auto tex = validateTextureBinding("texSubImage2D", target, true);
4427 if (!tex) {
4428 ASSERT_NOT_REACHED();
4429 return;
4430 }
4431 ASSERT((xoffset + width) >= 0);
4432 ASSERT((yoffset + height) >= 0);
4433 ASSERT(tex->getWidth(target, level) >= (xoffset + width));
4434 ASSERT(tex->getHeight(target, level) >= (yoffset + height));
4435 ASSERT_UNUSED(internalFormat, tex->getInternalFormat(target, level) == internalFormat);
4436 m_context->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
4437}
4438
4439void WebGLRenderingContextBase::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border)
4440{
4441 if (isContextLostOrPending())
4442 return;
4443 if (!validateTexFuncParameters("copyTexImage2D", CopyTexImage, target, level, internalFormat, width, height, border, internalFormat, GraphicsContext3D::UNSIGNED_BYTE))
4444 return;
4445 if (!validateSettableTexInternalFormat("copyTexImage2D", internalFormat))
4446 return;
4447 auto tex = validateTextureBinding("copyTexImage2D", target, true);
4448 if (!tex)
4449 return;
4450 if (!isTexInternalFormatColorBufferCombinationValid(internalFormat, getBoundFramebufferColorFormat())) {
4451 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "copyTexImage2D", "framebuffer is incompatible format");
4452 return;
4453 }
4454 if (!isGLES2NPOTStrict() && level && WebGLTexture::isNPOT(width, height)) {
4455 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "copyTexImage2D", "level > 0 not power of 2");
4456 return;
4457 }
4458 const char* reason = "framebuffer incomplete";
4459 if (m_framebufferBinding && !m_framebufferBinding->onAccess(graphicsContext3D(), &reason)) {
4460 synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION, "copyTexImage2D", reason);
4461 return;
4462 }
4463 clearIfComposited();
4464
4465 GC3Dint clippedX, clippedY;
4466 GC3Dsizei clippedWidth, clippedHeight;
4467 if (clip2D(x, y, width, height, getBoundFramebufferWidth(), getBoundFramebufferHeight(), &clippedX, &clippedY, &clippedWidth, &clippedHeight)) {
4468 m_context->texImage2DResourceSafe(target, level, internalFormat, width, height, border,
4469 internalFormat, GraphicsContext3D::UNSIGNED_BYTE, m_unpackAlignment);
4470 if (clippedWidth > 0 && clippedHeight > 0) {
4471 m_context->copyTexSubImage2D(target, level, clippedX - x, clippedY - y,
4472 clippedX, clippedY, clippedWidth, clippedHeight);
4473 }
4474 } else
4475 m_context->copyTexImage2D(target, level, internalFormat, x, y, width, height, border);
4476
4477 // FIXME: if the framebuffer is not complete, none of the below should be executed.
4478 tex->setLevelInfo(target, level, internalFormat, width, height, GraphicsContext3D::UNSIGNED_BYTE);
4479}
4480
4481static bool isRGBFormat(GC3Denum internalFormat)
4482{
4483 return internalFormat == GraphicsContext3D::RGB
4484 || internalFormat == GraphicsContext3D::RGBA
4485 || internalFormat == GraphicsContext3D::RGB8
4486 || internalFormat == GraphicsContext3D::RGBA8;
4487}
4488
4489ExceptionOr<void> WebGLRenderingContextBase::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Denum format, GC3Denum type, Optional<TexImageSource> source)
4490{
4491 if (!source) {
4492 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "source is null");
4493 return { };
4494 }
4495
4496 auto visitor = WTF::makeVisitor([&](const RefPtr<ImageBitmap>& bitmap) -> ExceptionOr<void> {
4497 if (isContextLostOrPending() || !validateTexFunc("texImage2D", TexImage, SourceImageBitmap, target, level, internalformat, bitmap->width(), bitmap->height(), 0, format, type, 0, 0))
4498 return { };
4499
4500 ImageBuffer* buffer = bitmap->buffer();
4501 if (!buffer)
4502 return { };
4503
4504 auto texture = validateTextureBinding("texImage2D", target, true);
4505 // If possible, copy from the bitmap directly to the texture
4506 // via the GPU, without a read-back to system memory.
4507 //
4508 // FIXME: restriction of (RGB || RGBA)/UNSIGNED_BYTE should be lifted when
4509 // ImageBuffer::copyToPlatformTexture implementations are fully functional.
4510 if (texture
4511 && (format == GraphicsContext3D::RGB || format == GraphicsContext3D::RGBA)
4512 && type == GraphicsContext3D::UNSIGNED_BYTE) {
4513 auto textureInternalFormat = texture->getInternalFormat(target, level);
4514 if (isRGBFormat(textureInternalFormat) || !texture->isValid(target, level)) {
4515 if (buffer->copyToPlatformTexture(*m_context.get(), target, texture->object(), internalformat, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
4516 texture->setLevelInfo(target, level, internalformat, bitmap->width(), bitmap->height(), type);
4517 return { };
4518 }
4519 }
4520 }
4521
4522 // Normal pure SW path.
4523 RefPtr<Image> image = buffer->copyImage(ImageBuffer::fastCopyImageMode());
4524 if (image)
4525 texImage2DImpl(target, level, internalformat, format, type, image.get(), GraphicsContext3D::HtmlDomImage, m_unpackFlipY, m_unpackPremultiplyAlpha);
4526 return { };
4527 }, [&](const RefPtr<ImageData>& pixels) -> ExceptionOr<void> {
4528 if (isContextLostOrPending() || !validateTexFunc("texImage2D", TexImage, SourceImageData, target, level, internalformat, pixels->width(), pixels->height(), 0, format, type, 0, 0))
4529 return { };
4530 Vector<uint8_t> data;
4531 bool needConversion = true;
4532 // The data from ImageData is always of format RGBA8.
4533 // No conversion is needed if destination format is RGBA and type is USIGNED_BYTE and no Flip or Premultiply operation is required.
4534 if (!m_unpackFlipY && !m_unpackPremultiplyAlpha && format == GraphicsContext3D::RGBA && type == GraphicsContext3D::UNSIGNED_BYTE)
4535 needConversion = false;
4536 else {
4537 if (!m_context->extractImageData(pixels.get(), format, type, m_unpackFlipY, m_unpackPremultiplyAlpha, data)) {
4538 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "bad image data");
4539 return { };
4540 }
4541 }
4542 if (m_unpackAlignment != 1)
4543 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
4544 texImage2DBase(target, level, internalformat, pixels->width(), pixels->height(), 0, format, type, needConversion ? data.data() : pixels->data()->data());
4545 if (m_unpackAlignment != 1)
4546 m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment);
4547 return { };
4548 }, [&](const RefPtr<HTMLImageElement>& image) -> ExceptionOr<void> {
4549 if (isContextLostOrPending())
4550 return { };
4551 auto validationResult = validateHTMLImageElement("texImage2D", image.get());
4552 if (validationResult.hasException())
4553 return validationResult.releaseException();
4554 if (!validationResult.returnValue())
4555 return { };
4556
4557 RefPtr<Image> imageForRender = image->cachedImage()->imageForRenderer(image->renderer());
4558 if (!imageForRender)
4559 return { };
4560
4561 if (imageForRender->isSVGImage())
4562 imageForRender = drawImageIntoBuffer(*imageForRender, image->width(), image->height(), 1);
4563
4564 if (!imageForRender || !validateTexFunc("texImage2D", TexImage, SourceHTMLImageElement, target, level, internalformat, imageForRender->width(), imageForRender->height(), 0, format, type, 0, 0))
4565 return { };
4566
4567 texImage2DImpl(target, level, internalformat, format, type, imageForRender.get(), GraphicsContext3D::HtmlDomImage, m_unpackFlipY, m_unpackPremultiplyAlpha);
4568 return { };
4569 }, [&](const RefPtr<HTMLCanvasElement>& canvas) -> ExceptionOr<void> {
4570 if (isContextLostOrPending())
4571 return { };
4572 auto validationResult = validateHTMLCanvasElement("texImage2D", canvas.get());
4573 if (validationResult.hasException())
4574 return validationResult.releaseException();
4575 if (!validationResult.returnValue())
4576 return { };
4577 if (!validateTexFunc("texImage2D", TexImage, SourceHTMLCanvasElement, target, level, internalformat, canvas->width(), canvas->height(), 0, format, type, 0, 0))
4578 return { };
4579
4580 auto texture = validateTextureBinding("texImage2D", target, true);
4581 // If possible, copy from the canvas element directly to the texture
4582 // via the GPU, without a read-back to system memory.
4583 //
4584 // FIXME: restriction of (RGB || RGBA)/UNSIGNED_BYTE should be lifted when
4585 // ImageBuffer::copyToPlatformTexture implementations are fully functional.
4586 if (texture
4587 && (format == GraphicsContext3D::RGB || format == GraphicsContext3D::RGBA)
4588 && type == GraphicsContext3D::UNSIGNED_BYTE) {
4589 auto textureInternalFormat = texture->getInternalFormat(target, level);
4590 if (isRGBFormat(textureInternalFormat) || !texture->isValid(target, level)) {
4591 ImageBuffer* buffer = canvas->buffer();
4592 if (buffer && buffer->copyToPlatformTexture(*m_context.get(), target, texture->object(), internalformat, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
4593 texture->setLevelInfo(target, level, internalformat, canvas->width(), canvas->height(), type);
4594 return { };
4595 }
4596 }
4597 }
4598
4599 RefPtr<ImageData> imageData = canvas->getImageData();
4600 if (imageData)
4601 texImage2D(target, level, internalformat, format, type, TexImageSource(imageData.get()));
4602 else
4603 texImage2DImpl(target, level, internalformat, format, type, canvas->copiedImage(), GraphicsContext3D::HtmlDomCanvas, m_unpackFlipY, m_unpackPremultiplyAlpha);
4604 return { };
4605 }
4606#if ENABLE(VIDEO)
4607 , [&](const RefPtr<HTMLVideoElement>& video) -> ExceptionOr<void> {
4608 if (isContextLostOrPending())
4609 return { };
4610 auto validationResult = validateHTMLVideoElement("texImage2D", video.get());
4611 if (validationResult.hasException())
4612 return validationResult.releaseException();
4613 if (!validationResult.returnValue())
4614 return { };
4615 if (!validateTexFunc("texImage2D", TexImage, SourceHTMLVideoElement, target, level, internalformat, video->videoWidth(), video->videoHeight(), 0, format, type, 0, 0))
4616 return { };
4617
4618 // Go through the fast path doing a GPU-GPU textures copy without a readback to system memory if possible.
4619 // Otherwise, it will fall back to the normal SW path.
4620 // FIXME: The current restrictions require that format shoud be RGB or RGBA,
4621 // type should be UNSIGNED_BYTE and level should be 0. It may be lifted in the future.
4622 auto texture = validateTextureBinding("texImage2D", target, true);
4623 if (GraphicsContext3D::TEXTURE_2D == target && texture
4624 && (format == GraphicsContext3D::RGB || format == GraphicsContext3D::RGBA)
4625 && type == GraphicsContext3D::UNSIGNED_BYTE
4626 && !level) {
4627 auto textureInternalFormat = texture->getInternalFormat(target, level);
4628 if (isRGBFormat(textureInternalFormat) || !texture->isValid(target, level)) {
4629 if (video->copyVideoTextureToPlatformTexture(m_context.get(), texture->object(), target, level, internalformat, format, type, m_unpackPremultiplyAlpha, m_unpackFlipY)) {
4630 texture->setLevelInfo(target, level, internalformat, video->videoWidth(), video->videoHeight(), type);
4631 return { };
4632 }
4633 }
4634 }
4635
4636 // Normal pure SW path.
4637 RefPtr<Image> image = videoFrameToImage(video.get(), ImageBuffer::fastCopyImageMode());
4638 if (!image)
4639 return { };
4640 texImage2DImpl(target, level, internalformat, format, type, image.get(), GraphicsContext3D::HtmlDomVideo, m_unpackFlipY, m_unpackPremultiplyAlpha);
4641 return { };
4642 }
4643#endif
4644 );
4645
4646 return WTF::visit(visitor, source.value());
4647}
4648
4649RefPtr<Image> WebGLRenderingContextBase::drawImageIntoBuffer(Image& image, int width, int height, int deviceScaleFactor)
4650{
4651 IntSize size(width, height);
4652 size.scale(deviceScaleFactor);
4653 ImageBuffer* buf = m_generatedImageCache.imageBuffer(size);
4654 if (!buf) {
4655 synthesizeGLError(GraphicsContext3D::OUT_OF_MEMORY, "texImage2D", "out of memory");
4656 return nullptr;
4657 }
4658
4659 FloatRect srcRect(FloatPoint(), image.size());
4660 FloatRect destRect(FloatPoint(), size);
4661 buf->context().drawImage(image, destRect, srcRect);
4662 return buf->copyImage(ImageBuffer::fastCopyImageMode());
4663}
4664
4665#if ENABLE(VIDEO)
4666
4667RefPtr<Image> WebGLRenderingContextBase::videoFrameToImage(HTMLVideoElement* video, BackingStoreCopy backingStoreCopy)
4668{
4669 IntSize size(video->videoWidth(), video->videoHeight());
4670 ImageBuffer* buf = m_generatedImageCache.imageBuffer(size);
4671 if (!buf) {
4672 synthesizeGLError(GraphicsContext3D::OUT_OF_MEMORY, "texImage2D", "out of memory");
4673 return nullptr;
4674 }
4675 FloatRect destRect(0, 0, size.width(), size.height());
4676 // FIXME: Turn this into a GPU-GPU texture copy instead of CPU readback.
4677 video->paintCurrentFrameInContext(buf->context(), destRect);
4678 return buf->copyImage(backingStoreCopy);
4679}
4680
4681#endif
4682
4683void WebGLRenderingContextBase::texParameter(GC3Denum target, GC3Denum pname, GC3Dfloat paramf, GC3Dint parami, bool isFloat)
4684{
4685 if (isContextLostOrPending())
4686 return;
4687 auto tex = validateTextureBinding("texParameter", target, false);
4688 if (!tex)
4689 return;
4690 switch (pname) {
4691 case GraphicsContext3D::TEXTURE_MIN_FILTER:
4692 case GraphicsContext3D::TEXTURE_MAG_FILTER:
4693 break;
4694 case GraphicsContext3D::TEXTURE_WRAP_S:
4695 case GraphicsContext3D::TEXTURE_WRAP_T:
4696 if ((isFloat && paramf != GraphicsContext3D::CLAMP_TO_EDGE && paramf != GraphicsContext3D::MIRRORED_REPEAT && paramf != GraphicsContext3D::REPEAT)
4697 || (!isFloat && parami != GraphicsContext3D::CLAMP_TO_EDGE && parami != GraphicsContext3D::MIRRORED_REPEAT && parami != GraphicsContext3D::REPEAT)) {
4698 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "texParameter", "invalid parameter");
4699 return;
4700 }
4701 break;
4702 case Extensions3D::TEXTURE_MAX_ANISOTROPY_EXT: // EXT_texture_filter_anisotropic
4703 if (!m_extTextureFilterAnisotropic) {
4704 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "texParameter", "invalid parameter, EXT_texture_filter_anisotropic not enabled");
4705 return;
4706 }
4707 break;
4708 default:
4709 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "texParameter", "invalid parameter name");
4710 return;
4711 }
4712 if (isFloat) {
4713 tex->setParameterf(pname, paramf);
4714 m_context->texParameterf(target, pname, paramf);
4715 } else {
4716 tex->setParameteri(pname, parami);
4717 m_context->texParameteri(target, pname, parami);
4718 }
4719}
4720
4721void WebGLRenderingContextBase::texParameterf(GC3Denum target, GC3Denum pname, GC3Dfloat param)
4722{
4723 texParameter(target, pname, param, 0, true);
4724}
4725
4726void WebGLRenderingContextBase::texParameteri(GC3Denum target, GC3Denum pname, GC3Dint param)
4727{
4728 texParameter(target, pname, 0, param, false);
4729}
4730
4731void WebGLRenderingContextBase::uniform1f(const WebGLUniformLocation* location, GC3Dfloat x)
4732{
4733 if (isContextLostOrPending() || !location)
4734 return;
4735
4736 if (location->program() != m_currentProgram) {
4737 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform1f", "location not for current program");
4738 return;
4739 }
4740
4741 m_context->uniform1f(location->location(), x);
4742}
4743
4744void WebGLRenderingContextBase::uniform2f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y)
4745{
4746 if (isContextLostOrPending() || !location)
4747 return;
4748
4749 if (location->program() != m_currentProgram) {
4750 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform2f", "location not for current program");
4751 return;
4752 }
4753
4754 m_context->uniform2f(location->location(), x, y);
4755}
4756
4757void WebGLRenderingContextBase::uniform3f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z)
4758{
4759 if (isContextLostOrPending() || !location)
4760 return;
4761
4762 if (location->program() != m_currentProgram) {
4763 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform3f", "location not for current program");
4764 return;
4765 }
4766
4767 m_context->uniform3f(location->location(), x, y, z);
4768}
4769
4770void WebGLRenderingContextBase::uniform4f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z, GC3Dfloat w)
4771{
4772 if (isContextLostOrPending() || !location)
4773 return;
4774
4775 if (location->program() != m_currentProgram) {
4776 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform4f", "location not for current program");
4777 return;
4778 }
4779
4780 m_context->uniform4f(location->location(), x, y, z, w);
4781}
4782
4783void WebGLRenderingContextBase::uniform1i(const WebGLUniformLocation* location, GC3Dint x)
4784{
4785 if (isContextLostOrPending() || !location)
4786 return;
4787
4788 if (location->program() != m_currentProgram) {
4789 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform1i", "location not for current program");
4790 return;
4791 }
4792
4793 if ((location->type() == GraphicsContext3D::SAMPLER_2D || location->type() == GraphicsContext3D::SAMPLER_CUBE) && x >= (int)m_textureUnits.size()) {
4794 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "uniform1i", "invalid texture unit");
4795 return;
4796 }
4797
4798 m_context->uniform1i(location->location(), x);
4799}
4800
4801void WebGLRenderingContextBase::uniform2i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y)
4802{
4803 if (isContextLostOrPending() || !location)
4804 return;
4805
4806 if (location->program() != m_currentProgram) {
4807 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform2i", "location not for current program");
4808 return;
4809 }
4810
4811 m_context->uniform2i(location->location(), x, y);
4812}
4813
4814void WebGLRenderingContextBase::uniform3i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, GC3Dint z)
4815{
4816 if (isContextLostOrPending() || !location)
4817 return;
4818
4819 if (location->program() != m_currentProgram) {
4820 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform3i", "location not for current program");
4821 return;
4822 }
4823
4824 m_context->uniform3i(location->location(), x, y, z);
4825}
4826
4827void WebGLRenderingContextBase::uniform4i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, GC3Dint z, GC3Dint w)
4828{
4829 if (isContextLostOrPending() || !location)
4830 return;
4831
4832 if (location->program() != m_currentProgram) {
4833 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "uniform4i", "location not for current program");
4834 return;
4835 }
4836
4837 m_context->uniform4i(location->location(), x, y, z, w);
4838}
4839
4840void WebGLRenderingContextBase::uniform1fv(const WebGLUniformLocation* location, Float32List&& v)
4841{
4842 if (isContextLostOrPending() || !validateUniformParameters("uniform1fv", location, v, 1))
4843 return;
4844
4845 m_context->uniform1fv(location->location(), v.length(), v.data());
4846}
4847
4848void WebGLRenderingContextBase::uniform2fv(const WebGLUniformLocation* location, Float32List&& v)
4849{
4850 if (isContextLostOrPending() || !validateUniformParameters("uniform2fv", location, v, 2))
4851 return;
4852
4853 m_context->uniform2fv(location->location(), v.length() / 2, v.data());
4854}
4855
4856void WebGLRenderingContextBase::uniform3fv(const WebGLUniformLocation* location, Float32List&& v)
4857{
4858 if (isContextLostOrPending() || !validateUniformParameters("uniform3fv", location, v, 3))
4859 return;
4860
4861 m_context->uniform3fv(location->location(), v.length() / 3, v.data());
4862}
4863
4864void WebGLRenderingContextBase::uniform4fv(const WebGLUniformLocation* location, Float32List&& v)
4865{
4866 if (isContextLostOrPending() || !validateUniformParameters("uniform4fv", location, v, 4))
4867 return;
4868
4869 m_context->uniform4fv(location->location(), v.length() / 4, v.data());
4870}
4871
4872void WebGLRenderingContextBase::uniform1iv(const WebGLUniformLocation* location, Int32List&& v)
4873{
4874 if (isContextLostOrPending() || !validateUniformParameters("uniform1iv", location, v, 1))
4875 return;
4876
4877 auto data = v.data();
4878 auto length = v.length();
4879
4880 if (location->type() == GraphicsContext3D::SAMPLER_2D || location->type() == GraphicsContext3D::SAMPLER_CUBE) {
4881 for (auto i = 0; i < length; ++i) {
4882 if (data[i] >= static_cast<int>(m_textureUnits.size())) {
4883 LOG(WebGL, "Texture unit size=%zu, v[%d]=%d. Location type = %04X.", m_textureUnits.size(), i, data[i], location->type());
4884 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "uniform1iv", "invalid texture unit");
4885 return;
4886 }
4887 }
4888 }
4889
4890 m_context->uniform1iv(location->location(), length, data);
4891}
4892
4893void WebGLRenderingContextBase::uniform2iv(const WebGLUniformLocation* location, Int32List&& v)
4894{
4895 if (isContextLostOrPending() || !validateUniformParameters("uniform2iv", location, v, 2))
4896 return;
4897
4898 m_context->uniform2iv(location->location(), v.length() / 2, v.data());
4899}
4900
4901void WebGLRenderingContextBase::uniform3iv(const WebGLUniformLocation* location, Int32List&& v)
4902{
4903 if (isContextLostOrPending() || !validateUniformParameters("uniform3iv", location, v, 3))
4904 return;
4905
4906 m_context->uniform3iv(location->location(), v.length() / 3, v.data());
4907}
4908
4909void WebGLRenderingContextBase::uniform4iv(const WebGLUniformLocation* location, Int32List&& v)
4910{
4911 if (isContextLostOrPending() || !validateUniformParameters("uniform4iv", location, v, 4))
4912 return;
4913
4914 m_context->uniform4iv(location->location(), v.length() / 4, v.data());
4915}
4916
4917void WebGLRenderingContextBase::uniformMatrix2fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32List&& v)
4918{
4919 if (isContextLostOrPending() || !validateUniformMatrixParameters("uniformMatrix2fv", location, transpose, v, 4))
4920 return;
4921 m_context->uniformMatrix2fv(location->location(), v.length() / 4, transpose, v.data());
4922}
4923
4924void WebGLRenderingContextBase::uniformMatrix3fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32List&& v)
4925{
4926 if (isContextLostOrPending() || !validateUniformMatrixParameters("uniformMatrix3fv", location, transpose, v, 9))
4927 return;
4928 m_context->uniformMatrix3fv(location->location(), v.length() / 9, transpose, v.data());
4929}
4930
4931void WebGLRenderingContextBase::uniformMatrix4fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32List&& v)
4932{
4933 if (isContextLostOrPending() || !validateUniformMatrixParameters("uniformMatrix4fv", location, transpose, v, 16))
4934 return;
4935 m_context->uniformMatrix4fv(location->location(), v.length() / 16, transpose, v.data());
4936}
4937
4938void WebGLRenderingContextBase::useProgram(WebGLProgram* program)
4939{
4940 bool deleted;
4941 if (!checkObjectToBeBound("useProgram", program, deleted))
4942 return;
4943 if (deleted)
4944 program = 0;
4945 if (program && !program->getLinkStatus()) {
4946 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "useProgram", "program not valid");
4947 return;
4948 }
4949 if (m_currentProgram != program) {
4950 if (m_currentProgram)
4951 m_currentProgram->onDetached(graphicsContext3D());
4952 m_currentProgram = program;
4953 m_context->useProgram(objectOrZero(program));
4954 if (program)
4955 program->onAttached();
4956 }
4957}
4958
4959void WebGLRenderingContextBase::validateProgram(WebGLProgram* program)
4960{
4961 if (isContextLostOrPending() || !validateWebGLObject("validateProgram", program))
4962 return;
4963 m_context->validateProgram(objectOrZero(program));
4964}
4965
4966void WebGLRenderingContextBase::vertexAttrib1f(GC3Duint index, GC3Dfloat v0)
4967{
4968 vertexAttribfImpl("vertexAttrib1f", index, 1, v0, 0.0f, 0.0f, 1.0f);
4969}
4970
4971void WebGLRenderingContextBase::vertexAttrib2f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1)
4972{
4973 vertexAttribfImpl("vertexAttrib2f", index, 2, v0, v1, 0.0f, 1.0f);
4974}
4975
4976void WebGLRenderingContextBase::vertexAttrib3f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2)
4977{
4978 vertexAttribfImpl("vertexAttrib3f", index, 3, v0, v1, v2, 1.0f);
4979}
4980
4981void WebGLRenderingContextBase::vertexAttrib4f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3)
4982{
4983 vertexAttribfImpl("vertexAttrib4f", index, 4, v0, v1, v2, v3);
4984}
4985
4986void WebGLRenderingContextBase::vertexAttrib1fv(GC3Duint index, Float32List&& v)
4987{
4988 vertexAttribfvImpl("vertexAttrib1fv", index, WTFMove(v), 1);
4989}
4990
4991void WebGLRenderingContextBase::vertexAttrib2fv(GC3Duint index, Float32List&& v)
4992{
4993 vertexAttribfvImpl("vertexAttrib2fv", index, WTFMove(v), 2);
4994}
4995
4996void WebGLRenderingContextBase::vertexAttrib3fv(GC3Duint index, Float32List&& v)
4997{
4998 vertexAttribfvImpl("vertexAttrib3fv", index, WTFMove(v), 3);
4999}
5000
5001void WebGLRenderingContextBase::vertexAttrib4fv(GC3Duint index, Float32List&& v)
5002{
5003 vertexAttribfvImpl("vertexAttrib4fv", index, WTFMove(v), 4);
5004}
5005
5006void WebGLRenderingContextBase::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, long long offset)
5007{
5008 if (isContextLostOrPending())
5009 return;
5010 switch (type) {
5011 case GraphicsContext3D::BYTE:
5012 case GraphicsContext3D::UNSIGNED_BYTE:
5013 case GraphicsContext3D::SHORT:
5014 case GraphicsContext3D::UNSIGNED_SHORT:
5015 case GraphicsContext3D::FLOAT:
5016 break;
5017 default:
5018 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "vertexAttribPointer", "invalid type");
5019 return;
5020 }
5021 if (index >= m_maxVertexAttribs) {
5022 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "vertexAttribPointer", "index out of range");
5023 return;
5024 }
5025 if (size < 1 || size > 4) {
5026 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "vertexAttribPointer", "bad size");
5027 return;
5028 }
5029 if (stride < 0 || stride > 255) {
5030 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "vertexAttribPointer", "bad stride");
5031 return;
5032 }
5033 if (offset < 0 || offset > std::numeric_limits<int32_t>::max()) {
5034 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "vertexAttribPointer", "bad offset");
5035 return;
5036 }
5037 if (!m_boundArrayBuffer) {
5038 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "vertexAttribPointer", "no bound ARRAY_BUFFER");
5039 return;
5040 }
5041 // Determine the number of elements the bound buffer can hold, given the offset, size, type and stride
5042 auto typeSize = sizeInBytes(type);
5043 if (!typeSize) {
5044 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, "vertexAttribPointer", "invalid type");
5045 return;
5046 }
5047 if ((stride % typeSize) || (static_cast<GC3Dintptr>(offset) % typeSize)) {
5048 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "vertexAttribPointer", "stride or offset not valid for type");
5049 return;
5050 }
5051 GC3Dsizei bytesPerElement = size * typeSize;
5052
5053 m_boundVertexArrayObject->setVertexAttribState(index, bytesPerElement, size, type, normalized, stride, static_cast<GC3Dintptr>(offset), *m_boundArrayBuffer);
5054 m_context->vertexAttribPointer(index, size, type, normalized, stride, static_cast<GC3Dintptr>(offset));
5055}
5056
5057void WebGLRenderingContextBase::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height)
5058{
5059 if (isContextLostOrPending())
5060 return;
5061 if (!validateSize("viewport", width, height))
5062 return;
5063 m_context->viewport(x, y, width, height);
5064}
5065
5066void WebGLRenderingContextBase::forceLostContext(WebGLRenderingContextBase::LostContextMode mode)
5067{
5068 if (isContextLostOrPending()) {
5069 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "loseContext", "context already lost");
5070 return;
5071 }
5072
5073 m_contextGroup->loseContextGroup(mode);
5074}
5075
5076void WebGLRenderingContextBase::loseContextImpl(WebGLRenderingContextBase::LostContextMode mode)
5077{
5078 if (isContextLost())
5079 return;
5080
5081 m_contextLost = true;
5082 m_contextLostMode = mode;
5083
5084 if (mode == RealLostContext) {
5085 // Inform the embedder that a lost context was received. In response, the embedder might
5086 // decide to take action such as asking the user for permission to use WebGL again.
5087 auto* canvas = htmlCanvas();
5088 if (canvas) {
5089 if (RefPtr<Frame> frame = canvas->document().frame())
5090 frame->loader().client().didLoseWebGLContext(m_context->getExtensions().getGraphicsResetStatusARB());
5091 }
5092 }
5093
5094 detachAndRemoveAllObjects();
5095
5096 // There is no direct way to clear errors from a GL implementation and
5097 // looping until getError() becomes NO_ERROR might cause an infinite loop if
5098 // the driver or context implementation had a bug. So, loop a reasonably
5099 // large number of times to clear any existing errors.
5100 for (int i = 0; i < 100; ++i) {
5101 if (m_context->getError() == GraphicsContext3D::NO_ERROR)
5102 break;
5103 }
5104 ConsoleDisplayPreference display = (mode == RealLostContext) ? DisplayInConsole: DontDisplayInConsole;
5105 synthesizeGLError(GraphicsContext3D::CONTEXT_LOST_WEBGL, "loseContext", "context lost", display);
5106
5107 // Don't allow restoration unless the context lost event has both been
5108 // dispatched and its default behavior prevented.
5109 m_restoreAllowed = false;
5110
5111 // Always defer the dispatch of the context lost event, to implement
5112 // the spec behavior of queueing a task.
5113 m_dispatchContextLostEventTimer.startOneShot(0_s);
5114}
5115
5116void WebGLRenderingContextBase::forceRestoreContext()
5117{
5118 if (!isContextLostOrPending()) {
5119 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "restoreContext", "context not lost");
5120 return;
5121 }
5122
5123 if (!m_restoreAllowed) {
5124 if (m_contextLostMode == SyntheticLostContext)
5125 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "restoreContext", "context restoration not allowed");
5126 return;
5127 }
5128
5129 if (!m_restoreTimer.isActive())
5130 m_restoreTimer.startOneShot(0_s);
5131}
5132
5133PlatformLayer* WebGLRenderingContextBase::platformLayer() const
5134{
5135 return (!isContextLost() && !m_isPendingPolicyResolution) ? m_context->platformLayer() : 0;
5136}
5137
5138void WebGLRenderingContextBase::removeSharedObject(WebGLSharedObject& object)
5139{
5140 if (m_isPendingPolicyResolution)
5141 return;
5142
5143 m_contextGroup->removeObject(object);
5144}
5145
5146void WebGLRenderingContextBase::addSharedObject(WebGLSharedObject& object)
5147{
5148 if (m_isPendingPolicyResolution)
5149 return;
5150
5151 ASSERT(!isContextLost());
5152 m_contextGroup->addObject(object);
5153}
5154
5155void WebGLRenderingContextBase::removeContextObject(WebGLContextObject& object)
5156{
5157 if (m_isPendingPolicyResolution)
5158 return;
5159
5160 m_contextObjects.remove(&object);
5161}
5162
5163void WebGLRenderingContextBase::addContextObject(WebGLContextObject& object)
5164{
5165 if (m_isPendingPolicyResolution)
5166 return;
5167
5168 ASSERT(!isContextLost());
5169 m_contextObjects.add(&object);
5170}
5171
5172void WebGLRenderingContextBase::detachAndRemoveAllObjects()
5173{
5174 if (m_isPendingPolicyResolution)
5175 return;
5176
5177 while (m_contextObjects.size() > 0) {
5178 HashSet<WebGLContextObject*>::iterator it = m_contextObjects.begin();
5179 (*it)->detachContext();
5180 }
5181}
5182
5183bool WebGLRenderingContextBase::hasPendingActivity() const
5184{
5185 return false;
5186}
5187
5188void WebGLRenderingContextBase::stop()
5189{
5190 if (!isContextLost() && !m_isPendingPolicyResolution) {
5191 forceLostContext(SyntheticLostContext);
5192 destroyGraphicsContext3D();
5193 }
5194}
5195
5196const char* WebGLRenderingContextBase::activeDOMObjectName() const
5197{
5198 return "WebGLRenderingContext";
5199}
5200
5201bool WebGLRenderingContextBase::canSuspendForDocumentSuspension() const
5202{
5203 // FIXME: We should try and do better here.
5204 return false;
5205}
5206
5207bool WebGLRenderingContextBase::getBooleanParameter(GC3Denum pname)
5208{
5209 GC3Dboolean value = 0;
5210 m_context->getBooleanv(pname, &value);
5211 return value;
5212}
5213
5214Vector<bool> WebGLRenderingContextBase::getBooleanArrayParameter(GC3Denum pname)
5215{
5216 if (pname != GraphicsContext3D::COLOR_WRITEMASK) {
5217 notImplemented();
5218 return { };
5219 }
5220 GC3Dboolean value[4] = { 0 };
5221 m_context->getBooleanv(pname, value);
5222 Vector<bool> vector(4);
5223 for (unsigned i = 0; i < 4; ++i)
5224 vector[i] = value[i];
5225 return vector;
5226}
5227
5228float WebGLRenderingContextBase::getFloatParameter(GC3Denum pname)
5229{
5230 GC3Dfloat value = 0;
5231 m_context->getFloatv(pname, &value);
5232 return value;
5233}
5234
5235int WebGLRenderingContextBase::getIntParameter(GC3Denum pname)
5236{
5237 GC3Dint value = 0;
5238 m_context->getIntegerv(pname, &value);
5239 return value;
5240}
5241
5242unsigned WebGLRenderingContextBase::getUnsignedIntParameter(GC3Denum pname)
5243{
5244 GC3Dint value = 0;
5245 m_context->getIntegerv(pname, &value);
5246 return value;
5247}
5248
5249long long WebGLRenderingContextBase::getInt64Parameter(GC3Denum pname)
5250{
5251 GC3Dint64 value = 0;
5252 m_context->getInteger64v(pname, &value);
5253 return value;
5254}
5255
5256RefPtr<Float32Array> WebGLRenderingContextBase::getWebGLFloatArrayParameter(GC3Denum pname)
5257{
5258 GC3Dfloat value[4] = {0};
5259 m_context->getFloatv(pname, value);
5260 unsigned length = 0;
5261 switch (pname) {
5262 case GraphicsContext3D::ALIASED_POINT_SIZE_RANGE:
5263 case GraphicsContext3D::ALIASED_LINE_WIDTH_RANGE:
5264 case GraphicsContext3D::DEPTH_RANGE:
5265 length = 2;
5266 break;
5267 case GraphicsContext3D::BLEND_COLOR:
5268 case GraphicsContext3D::COLOR_CLEAR_VALUE:
5269 length = 4;
5270 break;
5271 default:
5272 notImplemented();
5273 }
5274 return Float32Array::tryCreate(value, length);
5275}
5276
5277RefPtr<Int32Array> WebGLRenderingContextBase::getWebGLIntArrayParameter(GC3Denum pname)
5278{
5279 GC3Dint value[4] = {0};
5280 m_context->getIntegerv(pname, value);
5281 unsigned length = 0;
5282 switch (pname) {
5283 case GraphicsContext3D::MAX_VIEWPORT_DIMS:
5284 length = 2;
5285 break;
5286 case GraphicsContext3D::SCISSOR_BOX:
5287 case GraphicsContext3D::VIEWPORT:
5288 length = 4;
5289 break;
5290 default:
5291 notImplemented();
5292 }
5293 return Int32Array::tryCreate(value, length);
5294}
5295
5296bool WebGLRenderingContextBase::checkTextureCompleteness(const char* functionName, bool prepareToDraw)
5297{
5298 bool resetActiveUnit = false;
5299 bool usesAtLeastOneBlackTexture = false;
5300 WebGLTexture::TextureExtensionFlag extensions = textureExtensionFlags();
5301
5302 Vector<unsigned> noLongerUnrenderable;
5303 for (unsigned badTexture : m_unrenderableTextureUnits) {
5304 ASSERT(badTexture < m_textureUnits.size());
5305 auto& textureUnit = m_textureUnits[badTexture];
5306 bool needsToUseBlack2DTexture = textureUnit.texture2DBinding && textureUnit.texture2DBinding->needToUseBlackTexture(extensions);
5307 bool needsToUseBlack3DTexture = textureUnit.textureCubeMapBinding && textureUnit.textureCubeMapBinding->needToUseBlackTexture(extensions);
5308
5309 if (!needsToUseBlack2DTexture && !needsToUseBlack3DTexture) {
5310 noLongerUnrenderable.append(badTexture);
5311 continue;
5312 }
5313
5314 usesAtLeastOneBlackTexture = true;
5315
5316 if (badTexture != m_activeTextureUnit) {
5317 m_context->activeTexture(badTexture + GraphicsContext3D::TEXTURE0);
5318 resetActiveUnit = true;
5319 } else if (resetActiveUnit) {
5320 m_context->activeTexture(badTexture + GraphicsContext3D::TEXTURE0);
5321 resetActiveUnit = false;
5322 }
5323 RefPtr<WebGLTexture> tex2D;
5324 RefPtr<WebGLTexture> texCubeMap;
5325 if (prepareToDraw) {
5326 printToConsole(MessageLevel::Error, makeString("WebGL: ", functionName, ": texture bound to texture unit ", badTexture,
5327 " is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete',"
5328 " or it is a float/half-float type with linear filtering and without the relevant float/half-float linear extension enabled."));
5329 tex2D = m_blackTexture2D.get();
5330 texCubeMap = m_blackTextureCubeMap.get();
5331 } else {
5332 tex2D = textureUnit.texture2DBinding.get();
5333 texCubeMap = textureUnit.textureCubeMapBinding.get();
5334 }
5335 if (needsToUseBlack2DTexture)
5336 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, objectOrZero(tex2D.get()));
5337 if (needsToUseBlack3DTexture)
5338 m_context->bindTexture(GraphicsContext3D::TEXTURE_CUBE_MAP, objectOrZero(texCubeMap.get()));
5339 }
5340 if (resetActiveUnit)
5341 m_context->activeTexture(m_activeTextureUnit + GraphicsContext3D::TEXTURE0);
5342
5343 for (unsigned renderable : noLongerUnrenderable)
5344 m_unrenderableTextureUnits.remove(renderable);
5345
5346 return usesAtLeastOneBlackTexture;
5347}
5348
5349void WebGLRenderingContextBase::createFallbackBlackTextures1x1()
5350{
5351 unsigned char black[] = {0, 0, 0, 255};
5352 m_blackTexture2D = createTexture();
5353 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_blackTexture2D->object());
5354 m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, 1, 1,
5355 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5356 m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
5357 m_blackTextureCubeMap = createTexture();
5358 m_context->bindTexture(GraphicsContext3D::TEXTURE_CUBE_MAP, m_blackTextureCubeMap->object());
5359 m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X, 0, GraphicsContext3D::RGBA, 1, 1,
5360 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5361 m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GraphicsContext3D::RGBA, 1, 1,
5362 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5363 m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GraphicsContext3D::RGBA, 1, 1,
5364 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5365 m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GraphicsContext3D::RGBA, 1, 1,
5366 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5367 m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GraphicsContext3D::RGBA, 1, 1,
5368 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5369 m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GraphicsContext3D::RGBA, 1, 1,
5370 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black);
5371 m_context->bindTexture(GraphicsContext3D::TEXTURE_CUBE_MAP, 0);
5372}
5373
5374bool WebGLRenderingContextBase::isTexInternalFormatColorBufferCombinationValid(GC3Denum texInternalFormat,
5375 GC3Denum colorBufferFormat)
5376{
5377 unsigned need = GraphicsContext3D::getChannelBitsByFormat(texInternalFormat);
5378 unsigned have = GraphicsContext3D::getChannelBitsByFormat(colorBufferFormat);
5379 return (need & have) == need;
5380}
5381
5382GC3Denum WebGLRenderingContextBase::getBoundFramebufferColorFormat()
5383{
5384 if (m_framebufferBinding && m_framebufferBinding->object())
5385 return m_framebufferBinding->getColorBufferFormat();
5386 if (m_attributes.alpha)
5387 return GraphicsContext3D::RGBA;
5388 return GraphicsContext3D::RGB;
5389}
5390
5391int WebGLRenderingContextBase::getBoundFramebufferWidth()
5392{
5393 if (m_framebufferBinding && m_framebufferBinding->object())
5394 return m_framebufferBinding->getColorBufferWidth();
5395 return m_context->getInternalFramebufferSize().width();
5396}
5397
5398int WebGLRenderingContextBase::getBoundFramebufferHeight()
5399{
5400 if (m_framebufferBinding && m_framebufferBinding->object())
5401 return m_framebufferBinding->getColorBufferHeight();
5402 return m_context->getInternalFramebufferSize().height();
5403}
5404
5405RefPtr<WebGLTexture> WebGLRenderingContextBase::validateTextureBinding(const char* functionName, GC3Denum target, bool useSixEnumsForCubeMap)
5406{
5407 RefPtr<WebGLTexture> texture;
5408 switch (target) {
5409 case GraphicsContext3D::TEXTURE_2D:
5410 texture = m_textureUnits[m_activeTextureUnit].texture2DBinding;
5411 break;
5412 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
5413 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
5414 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
5415 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
5416 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
5417 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
5418 if (!useSixEnumsForCubeMap) {
5419 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture target");
5420 return nullptr;
5421 }
5422 texture = m_textureUnits[m_activeTextureUnit].textureCubeMapBinding;
5423 break;
5424 case GraphicsContext3D::TEXTURE_CUBE_MAP:
5425 if (useSixEnumsForCubeMap) {
5426 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture target");
5427 return nullptr;
5428 }
5429 texture = m_textureUnits[m_activeTextureUnit].textureCubeMapBinding;
5430 break;
5431 default:
5432 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture target");
5433 return nullptr;
5434 }
5435
5436 if (texture && texture->needToUseBlackTexture(textureExtensionFlags()))
5437 m_unrenderableTextureUnits.add(m_activeTextureUnit);
5438
5439 if (!texture)
5440 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "no texture");
5441 return texture;
5442}
5443
5444bool WebGLRenderingContextBase::validateLocationLength(const char* functionName, const String& string)
5445{
5446 const unsigned maxWebGLLocationLength = 256;
5447 if (string.length() > maxWebGLLocationLength) {
5448 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "location length > 256");
5449 return false;
5450 }
5451 return true;
5452}
5453
5454bool WebGLRenderingContextBase::validateSize(const char* functionName, GC3Dint x, GC3Dint y)
5455{
5456 if (x < 0 || y < 0) {
5457 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "size < 0");
5458 return false;
5459 }
5460 return true;
5461}
5462
5463bool WebGLRenderingContextBase::validateString(const char* functionName, const String& string)
5464{
5465 for (size_t i = 0; i < string.length(); ++i) {
5466 if (!validateCharacter(string[i])) {
5467 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "string not ASCII");
5468 return false;
5469 }
5470 }
5471 return true;
5472}
5473
5474bool WebGLRenderingContextBase::validateTexFuncLevel(const char* functionName, GC3Denum target, GC3Dint level)
5475{
5476 if (level < 0) {
5477 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "level < 0");
5478 return false;
5479 }
5480 switch (target) {
5481 case GraphicsContext3D::TEXTURE_2D:
5482 if (level >= m_maxTextureLevel) {
5483 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "level out of range");
5484 return false;
5485 }
5486 break;
5487 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
5488 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
5489 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
5490 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
5491 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
5492 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
5493 if (level >= m_maxCubeMapTextureLevel) {
5494 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "level out of range");
5495 return false;
5496 }
5497 break;
5498 }
5499 // This function only checks if level is legal, so we return true and don't
5500 // generate INVALID_ENUM if target is illegal.
5501 return true;
5502}
5503
5504bool WebGLRenderingContextBase::validateCompressedTexFormat(GC3Denum format)
5505{
5506 return m_compressedTextureFormats.contains(format);
5507}
5508
5509struct BlockParameters {
5510 const int width;
5511 const int height;
5512 const int size;
5513};
5514
5515static inline unsigned calculateBytesForASTC(GC3Dsizei width, GC3Dsizei height, const BlockParameters& parameters)
5516{
5517 return ((width + parameters.width - 1) / parameters.width) * ((height + parameters.height - 1) / parameters.height) * parameters.size;
5518}
5519
5520bool WebGLRenderingContextBase::validateCompressedTexFuncData(const char* functionName, GC3Dsizei width, GC3Dsizei height, GC3Denum format, ArrayBufferView& pixels)
5521{
5522 if (width < 0 || height < 0) {
5523 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "width or height < 0");
5524 return false;
5525 }
5526
5527 unsigned bytesRequired = 0;
5528
5529 // Block parameters for ASTC formats
5530 const int kASTCBlockSize = 16;
5531 static const BlockParameters ASTCParameters[] {
5532 BlockParameters { 4, 4, kASTCBlockSize },
5533 BlockParameters { 5, 4, kASTCBlockSize },
5534 BlockParameters { 5, 5, kASTCBlockSize },
5535 BlockParameters { 6, 5, kASTCBlockSize },
5536 BlockParameters { 6, 6, kASTCBlockSize },
5537 BlockParameters { 8, 5, kASTCBlockSize },
5538 BlockParameters { 8, 6, kASTCBlockSize },
5539 BlockParameters { 8, 8, kASTCBlockSize },
5540 BlockParameters { 10, 5, kASTCBlockSize },
5541 BlockParameters { 10, 6, kASTCBlockSize },
5542 BlockParameters { 10, 8, kASTCBlockSize },
5543 BlockParameters { 10, 10, kASTCBlockSize },
5544 BlockParameters { 12, 10, kASTCBlockSize },
5545 BlockParameters { 12, 12, kASTCBlockSize }
5546 };
5547 const GC3Denum ASTCEnumStartRGBA = Extensions3D::COMPRESSED_RGBA_ASTC_4x4_KHR;
5548 const GC3Denum ASTCEnumStartSRGB8 = Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;
5549
5550 switch (format) {
5551 case Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT:
5552 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT:
5553 case Extensions3D::COMPRESSED_ATC_RGB_AMD:
5554 {
5555 const int kBlockSize = 8;
5556 const int kBlockWidth = 4;
5557 const int kBlockHeight = 4;
5558 int numBlocksAcross = (width + kBlockWidth - 1) / kBlockWidth;
5559 int numBlocksDown = (height + kBlockHeight - 1) / kBlockHeight;
5560 bytesRequired = numBlocksAcross * numBlocksDown * kBlockSize;
5561 }
5562 break;
5563 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT3_EXT:
5564 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT:
5565 case Extensions3D::COMPRESSED_ATC_RGBA_EXPLICIT_ALPHA_AMD:
5566 case Extensions3D::COMPRESSED_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
5567 {
5568 const int kBlockSize = 16;
5569 const int kBlockWidth = 4;
5570 const int kBlockHeight = 4;
5571 int numBlocksAcross = (width + kBlockWidth - 1) / kBlockWidth;
5572 int numBlocksDown = (height + kBlockHeight - 1) / kBlockHeight;
5573 bytesRequired = numBlocksAcross * numBlocksDown * kBlockSize;
5574 }
5575 break;
5576 case Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
5577 case Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
5578 {
5579 const int kBlockSize = 8;
5580 const int kBlockWidth = 8;
5581 const int kBlockHeight = 8;
5582 bytesRequired = (std::max(width, kBlockWidth) * std::max(height, kBlockHeight) * 4 + 7) / kBlockSize;
5583 }
5584 break;
5585 case Extensions3D::COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
5586 case Extensions3D::COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
5587 {
5588 const int kBlockSize = 8;
5589 const int kBlockWidth = 16;
5590 const int kBlockHeight = 8;
5591 bytesRequired = (std::max(width, kBlockWidth) * std::max(height, kBlockHeight) * 2 + 7) / kBlockSize;
5592 }
5593 break;
5594 case Extensions3D::COMPRESSED_RGBA_ASTC_4x4_KHR:
5595 case Extensions3D::COMPRESSED_RGBA_ASTC_5x4_KHR:
5596 case Extensions3D::COMPRESSED_RGBA_ASTC_5x5_KHR:
5597 case Extensions3D::COMPRESSED_RGBA_ASTC_6x5_KHR:
5598 case Extensions3D::COMPRESSED_RGBA_ASTC_6x6_KHR:
5599 case Extensions3D::COMPRESSED_RGBA_ASTC_8x5_KHR:
5600 case Extensions3D::COMPRESSED_RGBA_ASTC_8x6_KHR:
5601 case Extensions3D::COMPRESSED_RGBA_ASTC_8x8_KHR:
5602 case Extensions3D::COMPRESSED_RGBA_ASTC_10x5_KHR:
5603 case Extensions3D::COMPRESSED_RGBA_ASTC_10x6_KHR:
5604 case Extensions3D::COMPRESSED_RGBA_ASTC_10x8_KHR:
5605 case Extensions3D::COMPRESSED_RGBA_ASTC_10x10_KHR:
5606 case Extensions3D::COMPRESSED_RGBA_ASTC_12x10_KHR:
5607 case Extensions3D::COMPRESSED_RGBA_ASTC_12x12_KHR:
5608 bytesRequired = calculateBytesForASTC(width, height, ASTCParameters[format - ASTCEnumStartRGBA]);
5609 break;
5610 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
5611 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
5612 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
5613 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
5614 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
5615 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
5616 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
5617 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
5618 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
5619 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
5620 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
5621 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
5622 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
5623 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
5624 bytesRequired = calculateBytesForASTC(width, height, ASTCParameters[format - ASTCEnumStartSRGB8]);
5625 break;
5626 default:
5627 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid format");
5628 return false;
5629 }
5630
5631 if (pixels.byteLength() != bytesRequired) {
5632 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "length of ArrayBufferView is not correct for dimensions");
5633 return false;
5634 }
5635
5636 return true;
5637}
5638
5639bool WebGLRenderingContextBase::validateCompressedTexDimensions(const char* functionName, GC3Denum target, GC3Dint level, GC3Dsizei width, GC3Dsizei height, GC3Denum format)
5640{
5641 switch (format) {
5642 case Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT:
5643 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT:
5644 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT3_EXT:
5645 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT: {
5646 const GC3Dsizei kBlockWidth = 4;
5647 const GC3Dsizei kBlockHeight = 4;
5648 const GC3Dint maxTextureSize = target ? m_maxTextureSize : m_maxCubeMapTextureSize;
5649 const GC3Dsizei maxCompressedDimension = maxTextureSize >> level;
5650 bool widthValid = (level && width == 1) || (level && width == 2) || (!(width % kBlockWidth) && width <= maxCompressedDimension);
5651 bool heightValid = (level && height == 1) || (level && height == 2) || (!(height % kBlockHeight) && height <= maxCompressedDimension);
5652 if (!widthValid || !heightValid) {
5653 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "width or height invalid for level");
5654 return false;
5655 }
5656 return true;
5657 }
5658 case Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
5659 case Extensions3D::COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
5660 case Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
5661 case Extensions3D::COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
5662 // Height and width must be powers of 2.
5663 if ((width & (width - 1)) || (height & (height - 1))) {
5664 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "width or height invalid for level");
5665 return false;
5666 }
5667 return true;
5668 case Extensions3D::COMPRESSED_RGBA_ASTC_4x4_KHR:
5669 case Extensions3D::COMPRESSED_RGBA_ASTC_5x4_KHR:
5670 case Extensions3D::COMPRESSED_RGBA_ASTC_5x5_KHR:
5671 case Extensions3D::COMPRESSED_RGBA_ASTC_6x5_KHR:
5672 case Extensions3D::COMPRESSED_RGBA_ASTC_6x6_KHR:
5673 case Extensions3D::COMPRESSED_RGBA_ASTC_8x5_KHR:
5674 case Extensions3D::COMPRESSED_RGBA_ASTC_8x6_KHR:
5675 case Extensions3D::COMPRESSED_RGBA_ASTC_8x8_KHR:
5676 case Extensions3D::COMPRESSED_RGBA_ASTC_10x5_KHR:
5677 case Extensions3D::COMPRESSED_RGBA_ASTC_10x6_KHR:
5678 case Extensions3D::COMPRESSED_RGBA_ASTC_10x8_KHR:
5679 case Extensions3D::COMPRESSED_RGBA_ASTC_10x10_KHR:
5680 case Extensions3D::COMPRESSED_RGBA_ASTC_12x10_KHR:
5681 case Extensions3D::COMPRESSED_RGBA_ASTC_12x12_KHR:
5682 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
5683 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
5684 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
5685 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
5686 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
5687 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
5688 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
5689 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
5690 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
5691 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
5692 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
5693 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
5694 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
5695 case Extensions3D::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
5696 // No height and width restrictions on ASTC.
5697 return true;
5698 default:
5699 return false;
5700 }
5701}
5702
5703bool WebGLRenderingContextBase::validateCompressedTexSubDimensions(const char* functionName, GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset,
5704 GC3Dsizei width, GC3Dsizei height, GC3Denum format, WebGLTexture* tex)
5705{
5706 if (xoffset < 0 || yoffset < 0) {
5707 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "xoffset or yoffset < 0");
5708 return false;
5709 }
5710
5711 switch (format) {
5712 case Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT:
5713 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT:
5714 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT3_EXT:
5715 case Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT: {
5716 const int kBlockWidth = 4;
5717 const int kBlockHeight = 4;
5718 if ((xoffset % kBlockWidth) || (yoffset % kBlockHeight)) {
5719 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "xoffset or yoffset not multiple of 4");
5720 return false;
5721 }
5722 if (width - xoffset > tex->getWidth(target, level)
5723 || height - yoffset > tex->getHeight(target, level)) {
5724 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "dimensions out of range");
5725 return false;
5726 }
5727 return validateCompressedTexDimensions(functionName, target, level, width, height, format);
5728 }
5729 case Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
5730 case Extensions3D::COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
5731 case Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
5732 case Extensions3D::COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: {
5733 if (xoffset || yoffset) {
5734 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "xoffset and yoffset must be zero");
5735 return false;
5736 }
5737 if (width != tex->getWidth(target, level)
5738 || height != tex->getHeight(target, level)) {
5739 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "dimensions must match existing level");
5740 return false;
5741 }
5742 return validateCompressedTexDimensions(functionName, target, level, width, height, format);
5743 }
5744 default:
5745 return false;
5746 }
5747}
5748
5749bool WebGLRenderingContextBase::validateDrawMode(const char* functionName, GC3Denum mode)
5750{
5751 switch (mode) {
5752 case GraphicsContext3D::POINTS:
5753 case GraphicsContext3D::LINE_STRIP:
5754 case GraphicsContext3D::LINE_LOOP:
5755 case GraphicsContext3D::LINES:
5756 case GraphicsContext3D::TRIANGLE_STRIP:
5757 case GraphicsContext3D::TRIANGLE_FAN:
5758 case GraphicsContext3D::TRIANGLES:
5759 return true;
5760 default:
5761 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid draw mode");
5762 return false;
5763 }
5764}
5765
5766bool WebGLRenderingContextBase::validateStencilSettings(const char* functionName)
5767{
5768 if (m_stencilMask != m_stencilMaskBack || m_stencilFuncRef != m_stencilFuncRefBack || m_stencilFuncMask != m_stencilFuncMaskBack) {
5769 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "front and back stencils settings do not match");
5770 return false;
5771 }
5772 return true;
5773}
5774
5775bool WebGLRenderingContextBase::validateStencilFunc(const char* functionName, GC3Denum func)
5776{
5777 switch (func) {
5778 case GraphicsContext3D::NEVER:
5779 case GraphicsContext3D::LESS:
5780 case GraphicsContext3D::LEQUAL:
5781 case GraphicsContext3D::GREATER:
5782 case GraphicsContext3D::GEQUAL:
5783 case GraphicsContext3D::EQUAL:
5784 case GraphicsContext3D::NOTEQUAL:
5785 case GraphicsContext3D::ALWAYS:
5786 return true;
5787 default:
5788 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid function");
5789 return false;
5790 }
5791}
5792
5793void WebGLRenderingContextBase::printToConsole(MessageLevel level, const String& message)
5794{
5795 if (!m_synthesizedErrorsToConsole || !m_numGLErrorsToConsoleAllowed)
5796 return;
5797
5798 std::unique_ptr<Inspector::ConsoleMessage> consoleMessage;
5799
5800 // Error messages can occur during function calls, so show stack traces for them.
5801 if (level == MessageLevel::Error) {
5802 Ref<Inspector::ScriptCallStack> stackTrace = Inspector::createScriptCallStack(JSExecState::currentState());
5803 consoleMessage = std::make_unique<Inspector::ConsoleMessage>(MessageSource::Rendering, MessageType::Log, level, message, WTFMove(stackTrace));
5804 } else
5805 consoleMessage = std::make_unique<Inspector::ConsoleMessage>(MessageSource::Rendering, MessageType::Log, level, message);
5806
5807 auto* canvas = htmlCanvas();
5808 if (canvas)
5809 canvas->document().addConsoleMessage(WTFMove(consoleMessage));
5810
5811 --m_numGLErrorsToConsoleAllowed;
5812 if (!m_numGLErrorsToConsoleAllowed)
5813 printToConsole(MessageLevel::Warning, "WebGL: too many errors, no more errors will be reported to the console for this context.");
5814}
5815
5816bool WebGLRenderingContextBase::validateBlendFuncFactors(const char* functionName, GC3Denum src, GC3Denum dst)
5817{
5818 if (((src == GraphicsContext3D::CONSTANT_COLOR || src == GraphicsContext3D::ONE_MINUS_CONSTANT_COLOR)
5819 && (dst == GraphicsContext3D::CONSTANT_ALPHA || dst == GraphicsContext3D::ONE_MINUS_CONSTANT_ALPHA))
5820 || ((dst == GraphicsContext3D::CONSTANT_COLOR || dst == GraphicsContext3D::ONE_MINUS_CONSTANT_COLOR)
5821 && (src == GraphicsContext3D::CONSTANT_ALPHA || src == GraphicsContext3D::ONE_MINUS_CONSTANT_ALPHA))) {
5822 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "incompatible src and dst");
5823 return false;
5824 }
5825 return true;
5826}
5827
5828bool WebGLRenderingContextBase::validateUniformParameters(const char* functionName, const WebGLUniformLocation* location, const Float32List& v, GC3Dsizei requiredMinSize)
5829{
5830 return validateUniformMatrixParameters(functionName, location, false, v.data(), v.length(), requiredMinSize);
5831}
5832
5833bool WebGLRenderingContextBase::validateUniformParameters(const char* functionName, const WebGLUniformLocation* location, const Int32List& v, GC3Dsizei requiredMinSize)
5834{
5835 return validateUniformMatrixParameters(functionName, location, false, v.data(), v.length(), requiredMinSize);
5836}
5837
5838bool WebGLRenderingContextBase::validateUniformParameters(const char* functionName, const WebGLUniformLocation* location, void* v, GC3Dsizei size, GC3Dsizei requiredMinSize)
5839{
5840 return validateUniformMatrixParameters(functionName, location, false, v, size, requiredMinSize);
5841}
5842
5843bool WebGLRenderingContextBase::validateUniformMatrixParameters(const char* functionName, const WebGLUniformLocation* location, GC3Dboolean transpose, const Float32List& v, GC3Dsizei requiredMinSize)
5844{
5845 return validateUniformMatrixParameters(functionName, location, transpose, v.data(), v.length(), requiredMinSize);
5846}
5847
5848bool WebGLRenderingContextBase::validateUniformMatrixParameters(const char* functionName, const WebGLUniformLocation* location, GC3Dboolean transpose, const void* v, GC3Dsizei size, GC3Dsizei requiredMinSize)
5849{
5850 if (!location)
5851 return false;
5852 if (location->program() != m_currentProgram) {
5853 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "location is not from current program");
5854 return false;
5855 }
5856 if (!v) {
5857 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no array");
5858 return false;
5859 }
5860 if (transpose) {
5861 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "transpose not FALSE");
5862 return false;
5863 }
5864 if (size < requiredMinSize || (size % requiredMinSize)) {
5865 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "invalid size");
5866 return false;
5867 }
5868 return true;
5869}
5870
5871WebGLBuffer* WebGLRenderingContextBase::validateBufferDataParameters(const char* functionName, GC3Denum target, GC3Denum usage)
5872{
5873 Optional<WebGLBuffer*> buffer;
5874 switch (target) {
5875 case GraphicsContext3D::ELEMENT_ARRAY_BUFFER:
5876 buffer = m_boundVertexArrayObject->getElementArrayBuffer();
5877 break;
5878 case GraphicsContext3D::ARRAY_BUFFER:
5879 buffer = m_boundArrayBuffer.get();
5880 break;
5881 default:
5882#if ENABLE(WEBGL2)
5883 if (isWebGL2()) {
5884 switch (target) {
5885 case GraphicsContext3D::COPY_READ_BUFFER:
5886 buffer = m_boundCopyReadBuffer.get();
5887 break;
5888 case GraphicsContext3D::COPY_WRITE_BUFFER:
5889 buffer = m_boundCopyWriteBuffer.get();
5890 break;
5891 case GraphicsContext3D::PIXEL_PACK_BUFFER:
5892 buffer = m_boundPixelPackBuffer.get();
5893 break;
5894 case GraphicsContext3D::PIXEL_UNPACK_BUFFER:
5895 buffer = m_boundPixelUnpackBuffer.get();
5896 break;
5897 case GraphicsContext3D::TRANSFORM_FEEDBACK_BUFFER:
5898 buffer = m_boundTransformFeedbackBuffer.get();
5899 break;
5900 case GraphicsContext3D::UNIFORM_BUFFER:
5901 buffer = m_boundUniformBuffer.get();
5902 break;
5903 }
5904 if (buffer)
5905 break;
5906 }
5907#endif
5908 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid target");
5909 return nullptr;
5910 }
5911 if (!buffer || !buffer.value()) {
5912 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "no buffer");
5913 return nullptr;
5914 }
5915 switch (usage) {
5916 case GraphicsContext3D::STREAM_DRAW:
5917 case GraphicsContext3D::STATIC_DRAW:
5918 case GraphicsContext3D::DYNAMIC_DRAW:
5919 return buffer.value();
5920 }
5921 synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid usage");
5922 return nullptr;
5923}
5924
5925ExceptionOr<bool> WebGLRenderingContextBase::validateHTMLImageElement(const char* functionName, HTMLImageElement* image)
5926{
5927 if (!image || !image->cachedImage()) {
5928 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no image");
5929 return false;
5930 }
5931 const URL& url = image->cachedImage()->response().url();
5932 if (url.isNull() || url.isEmpty() || !url.isValid()) {
5933 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "invalid image");
5934 return false;
5935 }
5936 if (wouldTaintOrigin(image))
5937 return Exception { SecurityError };
5938 return true;
5939}
5940
5941ExceptionOr<bool> WebGLRenderingContextBase::validateHTMLCanvasElement(const char* functionName, HTMLCanvasElement* canvas)
5942{
5943 if (!canvas || !canvas->buffer()) {
5944 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no canvas");
5945 return false;
5946 }
5947 if (wouldTaintOrigin(canvas))
5948 return Exception { SecurityError };
5949 return true;
5950}
5951
5952#if ENABLE(VIDEO)
5953
5954ExceptionOr<bool> WebGLRenderingContextBase::validateHTMLVideoElement(const char* functionName, HTMLVideoElement* video)
5955{
5956 if (!video || !video->videoWidth() || !video->videoHeight()) {
5957 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no video");
5958 return false;
5959 }
5960 if (wouldTaintOrigin(video))
5961 return Exception { SecurityError };
5962 return true;
5963}
5964
5965#endif
5966
5967void WebGLRenderingContextBase::vertexAttribfImpl(const char* functionName, GC3Duint index, GC3Dsizei expectedSize, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3)
5968{
5969 if (isContextLostOrPending())
5970 return;
5971 if (index >= m_maxVertexAttribs) {
5972 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "index out of range");
5973 return;
5974 }
5975 // In GL, we skip setting vertexAttrib0 values.
5976 if (index || isGLES2Compliant()) {
5977 switch (expectedSize) {
5978 case 1:
5979 m_context->vertexAttrib1f(index, v0);
5980 break;
5981 case 2:
5982 m_context->vertexAttrib2f(index, v0, v1);
5983 break;
5984 case 3:
5985 m_context->vertexAttrib3f(index, v0, v1, v2);
5986 break;
5987 case 4:
5988 m_context->vertexAttrib4f(index, v0, v1, v2, v3);
5989 break;
5990 }
5991 }
5992 VertexAttribValue& attribValue = m_vertexAttribValue[index];
5993 attribValue.value[0] = v0;
5994 attribValue.value[1] = v1;
5995 attribValue.value[2] = v2;
5996 attribValue.value[3] = v3;
5997}
5998
5999void WebGLRenderingContextBase::vertexAttribfvImpl(const char* functionName, GC3Duint index, Float32List&& list, GC3Dsizei expectedSize)
6000{
6001 if (isContextLostOrPending())
6002 return;
6003
6004 auto data = list.data();
6005 if (!data) {
6006 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "no array");
6007 return;
6008 }
6009
6010 int size = list.length();
6011 if (size < expectedSize) {
6012 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "invalid size");
6013 return;
6014 }
6015 if (index >= m_maxVertexAttribs) {
6016 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, functionName, "index out of range");
6017 return;
6018 }
6019 // In GL, we skip setting vertexAttrib0 values.
6020 if (index || isGLES2Compliant()) {
6021 switch (expectedSize) {
6022 case 1:
6023 m_context->vertexAttrib1fv(index, data);
6024 break;
6025 case 2:
6026 m_context->vertexAttrib2fv(index, data);
6027 break;
6028 case 3:
6029 m_context->vertexAttrib3fv(index, data);
6030 break;
6031 case 4:
6032 m_context->vertexAttrib4fv(index, data);
6033 break;
6034 }
6035 }
6036 VertexAttribValue& attribValue = m_vertexAttribValue[index];
6037 attribValue.initValue();
6038 for (int ii = 0; ii < expectedSize; ++ii)
6039 attribValue.value[ii] = data[ii];
6040}
6041
6042void WebGLRenderingContextBase::initVertexAttrib0()
6043{
6044 WebGLVertexArrayObjectBase::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0);
6045
6046 m_vertexAttrib0Buffer = createBuffer();
6047 m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vertexAttrib0Buffer->object());
6048 m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, 0, GraphicsContext3D::DYNAMIC_DRAW);
6049 m_context->vertexAttribPointer(0, 4, GraphicsContext3D::FLOAT, false, 0, 0);
6050 state.bufferBinding = m_vertexAttrib0Buffer;
6051 m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
6052 m_context->enableVertexAttribArray(0);
6053 m_vertexAttrib0BufferSize = 0;
6054 m_vertexAttrib0BufferValue[0] = 0.0f;
6055 m_vertexAttrib0BufferValue[1] = 0.0f;
6056 m_vertexAttrib0BufferValue[2] = 0.0f;
6057 m_vertexAttrib0BufferValue[3] = 1.0f;
6058 m_forceAttrib0BufferRefill = false;
6059 m_vertexAttrib0UsedBefore = false;
6060}
6061
6062bool WebGLRenderingContextBase::validateSimulatedVertexAttrib0(GC3Duint numVertex)
6063{
6064 if (!m_currentProgram)
6065 return true;
6066
6067 bool usingVertexAttrib0 = m_currentProgram->isUsingVertexAttrib0();
6068 if (!usingVertexAttrib0)
6069 return true;
6070
6071 auto& state = m_boundVertexArrayObject->getVertexAttribState(0);
6072 if (state.enabled)
6073 return true;
6074
6075 auto bufferSize = checkedAddAndMultiply<GC3Duint>(numVertex, 1, 4);
6076 if (!bufferSize)
6077 return false;
6078
6079 Checked<GC3Dsizeiptr, RecordOverflow> bufferDataSize(bufferSize.value());
6080 bufferDataSize *= Checked<GC3Dsizeiptr>(sizeof(GC3Dfloat));
6081 return !bufferDataSize.hasOverflowed() && bufferDataSize.unsafeGet() > 0;
6082}
6083
6084Optional<bool> WebGLRenderingContextBase::simulateVertexAttrib0(GC3Duint numVertex)
6085{
6086 if (!m_currentProgram)
6087 return false;
6088 bool usingVertexAttrib0 = m_currentProgram->isUsingVertexAttrib0();
6089 if (usingVertexAttrib0)
6090 m_vertexAttrib0UsedBefore = true;
6091
6092 auto& state = m_boundVertexArrayObject->getVertexAttribState(0);
6093 if (state.enabled && usingVertexAttrib0)
6094 return false;
6095 if (!usingVertexAttrib0 && !m_vertexAttrib0UsedBefore)
6096 return false;
6097 m_vertexAttrib0UsedBefore = true;
6098 m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vertexAttrib0Buffer->object());
6099
6100 // We know bufferSize and bufferDataSize won't overflow or go negative, thanks to validateSimulatedVertexAttrib0
6101 GC3Duint bufferSize = (numVertex + 1) * 4;
6102 GC3Dsizeiptr bufferDataSize = bufferSize * sizeof(GC3Dfloat);
6103
6104 if (bufferDataSize > m_vertexAttrib0BufferSize) {
6105 m_context->moveErrorsToSyntheticErrorList();
6106 m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, bufferDataSize, 0, GraphicsContext3D::DYNAMIC_DRAW);
6107 if (m_context->getError() != GraphicsContext3D::NO_ERROR) {
6108 // We were unable to create a buffer.
6109 m_vertexAttrib0UsedBefore = false;
6110 m_vertexAttrib0BufferSize = 0;
6111 m_forceAttrib0BufferRefill = true;
6112 return WTF::nullopt;
6113 }
6114 m_vertexAttrib0BufferSize = bufferDataSize;
6115 m_forceAttrib0BufferRefill = true;
6116 }
6117
6118 auto& attribValue = m_vertexAttribValue[0];
6119
6120 if (usingVertexAttrib0
6121 && (m_forceAttrib0BufferRefill
6122 || attribValue.value[0] != m_vertexAttrib0BufferValue[0]
6123 || attribValue.value[1] != m_vertexAttrib0BufferValue[1]
6124 || attribValue.value[2] != m_vertexAttrib0BufferValue[2]
6125 || attribValue.value[3] != m_vertexAttrib0BufferValue[3])) {
6126
6127 auto bufferData = makeUniqueArray<GC3Dfloat>(bufferSize);
6128 for (GC3Duint ii = 0; ii < numVertex + 1; ++ii) {
6129 bufferData[ii * 4] = attribValue.value[0];
6130 bufferData[ii * 4 + 1] = attribValue.value[1];
6131 bufferData[ii * 4 + 2] = attribValue.value[2];
6132 bufferData[ii * 4 + 3] = attribValue.value[3];
6133 }
6134 m_vertexAttrib0BufferValue[0] = attribValue.value[0];
6135 m_vertexAttrib0BufferValue[1] = attribValue.value[1];
6136 m_vertexAttrib0BufferValue[2] = attribValue.value[2];
6137 m_vertexAttrib0BufferValue[3] = attribValue.value[3];
6138 m_forceAttrib0BufferRefill = false;
6139 m_context->bufferSubData(GraphicsContext3D::ARRAY_BUFFER, 0, bufferDataSize, bufferData.get());
6140 }
6141 m_context->vertexAttribPointer(0, 4, GraphicsContext3D::FLOAT, 0, 0, 0);
6142 return true;
6143}
6144
6145void WebGLRenderingContextBase::restoreStatesAfterVertexAttrib0Simulation()
6146{
6147 const WebGLVertexArrayObjectBase::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0);
6148 if (state.bufferBinding != m_vertexAttrib0Buffer) {
6149 m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, objectOrZero(state.bufferBinding.get()));
6150 m_context->vertexAttribPointer(0, state.size, state.type, state.normalized, state.originalStride, state.offset);
6151 }
6152 m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, objectOrZero(m_boundArrayBuffer.get()));
6153}
6154
6155void WebGLRenderingContextBase::dispatchContextLostEvent()
6156{
6157 auto* canvas = htmlCanvas();
6158 if (!canvas)
6159 return;
6160
6161 Ref<WebGLContextEvent> event = WebGLContextEvent::create(eventNames().webglcontextlostEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString());
6162 canvas->dispatchEvent(event);
6163 m_restoreAllowed = event->defaultPrevented();
6164 if (m_contextLostMode == RealLostContext && m_restoreAllowed)
6165 m_restoreTimer.startOneShot(0_s);
6166}
6167
6168void WebGLRenderingContextBase::maybeRestoreContext()
6169{
6170 ASSERT(m_contextLost);
6171 if (!m_contextLost)
6172 return;
6173
6174 // The rendering context is not restored unless the default behavior of the
6175 // webglcontextlost event was prevented earlier.
6176 //
6177 // Because of the way m_restoreTimer is set up for real vs. synthetic lost
6178 // context events, we don't have to worry about this test short-circuiting
6179 // the retry loop for real context lost events.
6180 if (!m_restoreAllowed)
6181 return;
6182
6183 int contextLostReason = m_context->getExtensions().getGraphicsResetStatusARB();
6184
6185 switch (contextLostReason) {
6186 case GraphicsContext3D::NO_ERROR:
6187 // The GraphicsContext3D implementation might not fully
6188 // support GL_ARB_robustness semantics yet. Alternatively, the
6189 // WEBGL_lose_context extension might have been used to force
6190 // a lost context.
6191 break;
6192 case Extensions3D::GUILTY_CONTEXT_RESET_ARB:
6193 // The rendering context is not restored if this context was
6194 // guilty of causing the graphics reset.
6195 printToConsole(MessageLevel::Warning, "WARNING: WebGL content on the page caused the graphics card to reset; not restoring the context");
6196 return;
6197 case Extensions3D::INNOCENT_CONTEXT_RESET_ARB:
6198 // Always allow the context to be restored.
6199 break;
6200 case Extensions3D::UNKNOWN_CONTEXT_RESET_ARB:
6201 // Warn. Ideally, prompt the user telling them that WebGL
6202 // content on the page might have caused the graphics card to
6203 // reset and ask them whether they want to continue running
6204 // the content. Only if they say "yes" should we start
6205 // attempting to restore the context.
6206 printToConsole(MessageLevel::Warning, "WARNING: WebGL content on the page might have caused the graphics card to reset");
6207 break;
6208 }
6209
6210 auto* canvas = htmlCanvas();
6211 if (!canvas)
6212 return;
6213
6214 RefPtr<Frame> frame = canvas->document().frame();
6215 if (!frame)
6216 return;
6217
6218 if (!frame->loader().client().allowWebGL(frame->settings().webGLEnabled()))
6219 return;
6220
6221 RefPtr<FrameView> view = frame->view();
6222 if (!view)
6223 return;
6224 RefPtr<ScrollView> root = view->root();
6225 if (!root)
6226 return;
6227 HostWindow* hostWindow = root->hostWindow();
6228 if (!hostWindow)
6229 return;
6230
6231 RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(m_attributes, hostWindow));
6232 if (!context) {
6233 if (m_contextLostMode == RealLostContext)
6234 m_restoreTimer.startOneShot(secondsBetweenRestoreAttempts);
6235 else
6236 // This likely shouldn't happen but is the best way to report it to the WebGL app.
6237 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "", "error restoring context");
6238 return;
6239 }
6240
6241 m_context = context;
6242 addActivityStateChangeObserverIfNecessary();
6243 m_contextLost = false;
6244 setupFlags();
6245 initializeNewContext();
6246 initializeVertexArrayObjects();
6247 canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextrestoredEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
6248}
6249
6250void WebGLRenderingContextBase::simulateContextChanged()
6251{
6252 if (m_context)
6253 m_context->simulateContextChanged();
6254}
6255
6256String WebGLRenderingContextBase::ensureNotNull(const String& text) const
6257{
6258 if (text.isNull())
6259 return WTF::emptyString();
6260 return text;
6261}
6262
6263WebGLRenderingContextBase::LRUImageBufferCache::LRUImageBufferCache(int capacity)
6264 : m_buffers(capacity)
6265{
6266}
6267
6268ImageBuffer* WebGLRenderingContextBase::LRUImageBufferCache::imageBuffer(const IntSize& size)
6269{
6270 size_t i;
6271 for (i = 0; i < m_buffers.size(); ++i) {
6272 ImageBuffer* buf = m_buffers[i].get();
6273 if (!buf)
6274 break;
6275 if (buf->logicalSize() != size)
6276 continue;
6277 bubbleToFront(i);
6278 buf->context().clearRect(FloatRect({ }, FloatSize(size)));
6279 return buf;
6280 }
6281
6282 // FIXME (149423): Should this ImageBuffer be unconditionally unaccelerated?
6283 std::unique_ptr<ImageBuffer> temp = ImageBuffer::create(size, Unaccelerated);
6284 if (!temp)
6285 return nullptr;
6286 ASSERT(m_buffers.size() > 0);
6287 i = std::min(m_buffers.size() - 1, i);
6288 m_buffers[i] = WTFMove(temp);
6289
6290 ImageBuffer* buf = m_buffers[i].get();
6291 bubbleToFront(i);
6292 return buf;
6293}
6294
6295void WebGLRenderingContextBase::LRUImageBufferCache::bubbleToFront(size_t idx)
6296{
6297 for (size_t i = idx; i > 0; --i)
6298 m_buffers[i].swap(m_buffers[i-1]);
6299}
6300
6301namespace {
6302
6303 String GetErrorString(GC3Denum error)
6304 {
6305 switch (error) {
6306 case GraphicsContext3D::INVALID_ENUM:
6307 return "INVALID_ENUM"_s;
6308 case GraphicsContext3D::INVALID_VALUE:
6309 return "INVALID_VALUE"_s;
6310 case GraphicsContext3D::INVALID_OPERATION:
6311 return "INVALID_OPERATION"_s;
6312 case GraphicsContext3D::OUT_OF_MEMORY:
6313 return "OUT_OF_MEMORY"_s;
6314 case GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION:
6315 return "INVALID_FRAMEBUFFER_OPERATION"_s;
6316 case GraphicsContext3D::CONTEXT_LOST_WEBGL:
6317 return "CONTEXT_LOST_WEBGL"_s;
6318 default:
6319 return makeString("WebGL ERROR(", hex(error, 4, Lowercase), ')');
6320 }
6321 }
6322
6323} // namespace anonymous
6324
6325void WebGLRenderingContextBase::synthesizeGLError(GC3Denum error, const char* functionName, const char* description, ConsoleDisplayPreference display)
6326{
6327 if (m_synthesizedErrorsToConsole && display == DisplayInConsole) {
6328 String str = "WebGL: " + GetErrorString(error) + ": " + String(functionName) + ": " + String(description);
6329 printToConsole(MessageLevel::Error, str);
6330 }
6331 m_context->synthesizeGLError(error);
6332}
6333
6334void WebGLRenderingContextBase::applyStencilTest()
6335{
6336 bool haveStencilBuffer = false;
6337
6338 if (m_framebufferBinding)
6339 haveStencilBuffer = m_framebufferBinding->hasStencilBuffer();
6340 else {
6341 auto attributes = getContextAttributes();
6342 ASSERT(attributes);
6343 haveStencilBuffer = attributes->stencil;
6344 }
6345 enableOrDisable(GraphicsContext3D::STENCIL_TEST, m_stencilEnabled && haveStencilBuffer);
6346}
6347
6348void WebGLRenderingContextBase::enableOrDisable(GC3Denum capability, bool enable)
6349{
6350 if (enable)
6351 m_context->enable(capability);
6352 else
6353 m_context->disable(capability);
6354}
6355
6356IntSize WebGLRenderingContextBase::clampedCanvasSize()
6357{
6358 return IntSize(clamp(canvasBase().width(), 1, m_maxViewportDims[0]),
6359 clamp(canvasBase().height(), 1, m_maxViewportDims[1]));
6360}
6361
6362GC3Dint WebGLRenderingContextBase::getMaxDrawBuffers()
6363{
6364 if (!supportsDrawBuffers())
6365 return 0;
6366 if (!m_maxDrawBuffers)
6367 m_context->getIntegerv(Extensions3D::MAX_DRAW_BUFFERS_EXT, &m_maxDrawBuffers);
6368 if (!m_maxColorAttachments)
6369 m_context->getIntegerv(Extensions3D::MAX_COLOR_ATTACHMENTS_EXT, &m_maxColorAttachments);
6370 // WEBGL_draw_buffers requires MAX_COLOR_ATTACHMENTS >= MAX_DRAW_BUFFERS.
6371 return std::min(m_maxDrawBuffers, m_maxColorAttachments);
6372}
6373
6374GC3Dint WebGLRenderingContextBase::getMaxColorAttachments()
6375{
6376 if (!supportsDrawBuffers())
6377 return 0;
6378 if (!m_maxColorAttachments)
6379 m_context->getIntegerv(Extensions3D::MAX_COLOR_ATTACHMENTS_EXT, &m_maxColorAttachments);
6380 return m_maxColorAttachments;
6381}
6382
6383void WebGLRenderingContextBase::setBackDrawBuffer(GC3Denum buf)
6384{
6385 m_backDrawBuffer = buf;
6386}
6387
6388void WebGLRenderingContextBase::restoreCurrentFramebuffer()
6389{
6390 bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_framebufferBinding.get());
6391}
6392
6393void WebGLRenderingContextBase::restoreCurrentTexture2D()
6394{
6395 auto texture = m_textureUnits[m_activeTextureUnit].texture2DBinding.get();
6396 bindTexture(GraphicsContext3D::TEXTURE_2D, texture);
6397 if (texture && texture->needToUseBlackTexture(textureExtensionFlags()))
6398 m_unrenderableTextureUnits.add(m_activeTextureUnit);
6399}
6400
6401bool WebGLRenderingContextBase::supportsDrawBuffers()
6402{
6403 if (!m_drawBuffersWebGLRequirementsChecked) {
6404 m_drawBuffersWebGLRequirementsChecked = true;
6405 m_drawBuffersSupported = WebGLDrawBuffers::supported(*this);
6406 }
6407 return m_drawBuffersSupported;
6408}
6409
6410void WebGLRenderingContextBase::drawArraysInstanced(GC3Denum mode, GC3Dint first, GC3Dsizei count, GC3Dsizei primcount)
6411{
6412 if (!primcount) {
6413 markContextChanged();
6414 return;
6415 }
6416
6417 if (!validateDrawArrays("drawArraysInstanced", mode, first, count, primcount))
6418 return;
6419
6420 clearIfComposited();
6421
6422 bool vertexAttrib0Simulated = false;
6423 if (!isGLES2Compliant()) {
6424 auto simulateVertexAttrib0Status = simulateVertexAttrib0(first + count - 1);
6425 if (!simulateVertexAttrib0Status) {
6426 // We were unable to simulate the attribute buffer.
6427 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawArraysInstanced", "unable to simulate vertexAttrib0 array");
6428 return;
6429 }
6430 vertexAttrib0Simulated = simulateVertexAttrib0Status.value();
6431 }
6432 if (!isGLES2NPOTStrict())
6433 checkTextureCompleteness("drawArraysInstanced", true);
6434
6435 m_context->drawArraysInstanced(mode, first, count, primcount);
6436
6437 if (!isGLES2Compliant() && vertexAttrib0Simulated)
6438 restoreStatesAfterVertexAttrib0Simulation();
6439 if (!isGLES2NPOTStrict())
6440 checkTextureCompleteness("drawArraysInstanced", false);
6441 markContextChangedAndNotifyCanvasObserver();
6442}
6443
6444void WebGLRenderingContextBase::drawElementsInstanced(GC3Denum mode, GC3Dsizei count, GC3Denum type, long long offset, GC3Dsizei primcount)
6445{
6446 if (!primcount) {
6447 markContextChanged();
6448 return;
6449 }
6450
6451 unsigned numElements = 0;
6452 if (!validateDrawElements("drawElementsInstanced", mode, count, type, offset, numElements, primcount))
6453 return;
6454
6455 clearIfComposited();
6456
6457 bool vertexAttrib0Simulated = false;
6458 if (!isGLES2Compliant()) {
6459 if (!numElements)
6460 validateIndexArrayPrecise(count, type, static_cast<GC3Dintptr>(offset), numElements);
6461 auto simulateVertexAttrib0Status = simulateVertexAttrib0(numElements);
6462 if (!simulateVertexAttrib0Status) {
6463 // We were unable to simulate the attribute buffer.
6464 synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawArraysInstanced", "unable to simulate vertexAttrib0 array");
6465 return;
6466 }
6467 vertexAttrib0Simulated = simulateVertexAttrib0Status.value();
6468 }
6469 if (!isGLES2NPOTStrict())
6470 checkTextureCompleteness("drawElementsInstanced", true);
6471
6472#if USE(OPENGL) && ENABLE(WEBGL2)
6473 if (isWebGL2())
6474 m_context->primitiveRestartIndex(getRestartIndex(type));
6475#endif
6476
6477 m_context->drawElementsInstanced(mode, count, type, static_cast<GC3Dintptr>(offset), primcount);
6478
6479 if (!isGLES2Compliant() && vertexAttrib0Simulated)
6480 restoreStatesAfterVertexAttrib0Simulation();
6481 if (!isGLES2NPOTStrict())
6482 checkTextureCompleteness("drawElementsInstanced", false);
6483 markContextChangedAndNotifyCanvasObserver();
6484}
6485
6486void WebGLRenderingContextBase::vertexAttribDivisor(GC3Duint index, GC3Duint divisor)
6487{
6488 if (isContextLostOrPending())
6489 return;
6490
6491 if (index >= m_maxVertexAttribs) {
6492 synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "vertexAttribDivisor", "index out of range");
6493 return;
6494 }
6495
6496 m_boundVertexArrayObject->setVertexAttribDivisor(index, divisor);
6497 m_context->vertexAttribDivisor(index, divisor);
6498}
6499
6500bool WebGLRenderingContextBase::enableSupportedExtension(ASCIILiteral extensionNameLiteral)
6501{
6502 ASSERT(m_context);
6503 auto& extensions = m_context->getExtensions();
6504 String extensionName { extensionNameLiteral };
6505 if (!extensions.supports(extensionName))
6506 return false;
6507 extensions.ensureEnabled(extensionName);
6508 return true;
6509}
6510
6511void WebGLRenderingContextBase::activityStateDidChange(OptionSet<ActivityState::Flag> oldActivityState, OptionSet<ActivityState::Flag> newActivityState)
6512{
6513 if (!m_context)
6514 return;
6515
6516 auto changed = oldActivityState ^ newActivityState;
6517 if (changed & ActivityState::IsVisible)
6518 m_context->setContextVisibility(newActivityState.contains(ActivityState::IsVisible));
6519}
6520
6521void WebGLRenderingContextBase::setFailNextGPUStatusCheck()
6522{
6523 if (!m_context)
6524 return;
6525
6526 m_context->setFailNextGPUStatusCheck();
6527}
6528
6529void WebGLRenderingContextBase::didComposite()
6530{
6531 if (UNLIKELY(callTracingActive()))
6532 InspectorInstrumentation::didFinishRecordingCanvasFrame(*this);
6533}
6534
6535void WebGLRenderingContextBase::forceContextLost()
6536{
6537 forceLostContext(WebGLRenderingContextBase::RealLostContext);
6538}
6539
6540void WebGLRenderingContextBase::recycleContext()
6541{
6542 printToConsole(MessageLevel::Error, "There are too many active WebGL contexts on this page, the oldest context will be lost.");
6543 // Using SyntheticLostContext means the developer won't be able to force the restoration
6544 // of the context by calling preventDefault() in a "webglcontextlost" event handler.
6545 forceLostContext(SyntheticLostContext);
6546 destroyGraphicsContext3D();
6547}
6548
6549void WebGLRenderingContextBase::dispatchContextChangedNotification()
6550{
6551 auto* canvas = htmlCanvas();
6552 if (!canvas)
6553 return;
6554
6555 canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextchangedEvent, Event::CanBubble::No, Event::IsCancelable::Yes, emptyString()));
6556}
6557
6558
6559} // namespace WebCore
6560
6561#endif // ENABLE(WEBGL)
6562