1/*
2 * Copyright (C) 2013 Google 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(WEBGL)
29#include "WebGLDrawBuffers.h"
30
31#include "Extensions3D.h"
32
33namespace WebCore {
34
35WebGLDrawBuffers::WebGLDrawBuffers(WebGLRenderingContextBase& context)
36 : WebGLExtension(context)
37{
38}
39
40WebGLDrawBuffers::~WebGLDrawBuffers() = default;
41
42WebGLExtension::ExtensionName WebGLDrawBuffers::getName() const
43{
44 return WebGLExtension::WebGLDrawBuffersName;
45}
46
47bool WebGLDrawBuffers::supported(WebGLRenderingContextBase& context)
48{
49 return context.graphicsContext3D()->getExtensions().supports("GL_EXT_draw_buffers")
50 && satisfiesWebGLRequirements(context);
51}
52
53void WebGLDrawBuffers::drawBuffersWEBGL(const Vector<GC3Denum>& buffers)
54{
55 if (m_context.isContextLost())
56 return;
57 GC3Dsizei n = buffers.size();
58 const GC3Denum* bufs = buffers.data();
59 if (!m_context.m_framebufferBinding) {
60 if (n != 1) {
61 m_context.synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "drawBuffersWEBGL", "more than one buffer");
62 return;
63 }
64 if (bufs[0] != GraphicsContext3D::BACK && bufs[0] != GraphicsContext3D::NONE) {
65 m_context.synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawBuffersWEBGL", "BACK or NONE");
66 return;
67 }
68 // Because the backbuffer is simulated on all current WebKit ports, we need to change BACK to COLOR_ATTACHMENT0.
69 GC3Denum value = (bufs[0] == GraphicsContext3D::BACK) ? GraphicsContext3D::COLOR_ATTACHMENT0 : GraphicsContext3D::NONE;
70 m_context.graphicsContext3D()->getExtensions().drawBuffersEXT(1, &value);
71 m_context.setBackDrawBuffer(bufs[0]);
72 } else {
73 if (n > m_context.getMaxDrawBuffers()) {
74 m_context.synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "drawBuffersWEBGL", "more than max draw buffers");
75 return;
76 }
77 for (GC3Dsizei i = 0; i < n; ++i) {
78 if (bufs[i] != GraphicsContext3D::NONE && bufs[i] != static_cast<GC3Denum>(Extensions3D::COLOR_ATTACHMENT0_EXT + i)) {
79 m_context.synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawBuffersWEBGL", "COLOR_ATTACHMENTi_EXT or NONE");
80 return;
81 }
82 }
83 m_context.m_framebufferBinding->drawBuffers(buffers);
84 }
85}
86
87// static
88bool WebGLDrawBuffers::satisfiesWebGLRequirements(WebGLRenderingContextBase& webglContext)
89{
90 GraphicsContext3D* context = webglContext.graphicsContext3D();
91
92 // This is called after we make sure GL_EXT_draw_buffers is supported.
93 GC3Dint maxDrawBuffers = 0;
94 GC3Dint maxColorAttachments = 0;
95 context->getIntegerv(Extensions3D::MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers);
96 context->getIntegerv(Extensions3D::MAX_COLOR_ATTACHMENTS_EXT, &maxColorAttachments);
97 if (maxDrawBuffers < 4 || maxColorAttachments < 4)
98 return false;
99
100 Platform3DObject fbo = context->createFramebuffer();
101 context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, fbo);
102
103 const unsigned char buffer[4] = { 0, 0, 0, 0 }; // textures are required to be initialized for other ports.
104 bool supportsDepth = context->getExtensions().supports("GL_OES_depth_texture")
105 || context->getExtensions().supports("GL_ARB_depth_texture");
106 bool supportsDepthStencil = (context->getExtensions().supports("GL_EXT_packed_depth_stencil")
107 || context->getExtensions().supports("GL_OES_packed_depth_stencil"));
108 Platform3DObject depthStencil = 0;
109 if (supportsDepthStencil) {
110 depthStencil = context->createTexture();
111 context->bindTexture(GraphicsContext3D::TEXTURE_2D, depthStencil);
112 context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::DEPTH_STENCIL, 1, 1, 0, GraphicsContext3D::DEPTH_STENCIL, GraphicsContext3D::UNSIGNED_INT_24_8, buffer);
113 }
114 Platform3DObject depth = 0;
115 if (supportsDepth) {
116 depth = context->createTexture();
117 context->bindTexture(GraphicsContext3D::TEXTURE_2D, depth);
118 context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::DEPTH_COMPONENT, 1, 1, 0, GraphicsContext3D::DEPTH_COMPONENT, GraphicsContext3D::UNSIGNED_INT, buffer);
119 }
120
121 Vector<Platform3DObject> colors;
122 bool ok = true;
123 GC3Dint maxAllowedBuffers = std::min(maxDrawBuffers, maxColorAttachments);
124 for (GC3Dint i = 0; i < maxAllowedBuffers; ++i) {
125 Platform3DObject color = context->createTexture();
126 colors.append(color);
127 context->bindTexture(GraphicsContext3D::TEXTURE_2D, color);
128 context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, 1, 1, 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, buffer);
129 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0 + i, GraphicsContext3D::TEXTURE_2D, color, 0);
130 if (context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
131 ok = false;
132 break;
133 }
134 if (supportsDepth) {
135 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::TEXTURE_2D, depth, 0);
136 if (context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
137 ok = false;
138 break;
139 }
140 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::TEXTURE_2D, 0, 0);
141 }
142 if (supportsDepthStencil) {
143 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::TEXTURE_2D, depthStencil, 0);
144 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::TEXTURE_2D, depthStencil, 0);
145 if (context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
146 ok = false;
147 break;
148 }
149 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::TEXTURE_2D, 0, 0);
150 context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::TEXTURE_2D, 0, 0);
151 }
152 }
153
154 webglContext.restoreCurrentFramebuffer();
155 context->deleteFramebuffer(fbo);
156 webglContext.restoreCurrentTexture2D();
157 if (supportsDepth)
158 context->deleteTexture(depth);
159 if (supportsDepthStencil)
160 context->deleteTexture(depthStencil);
161 for (auto& color : colors)
162 context->deleteTexture(color);
163 return ok;
164}
165
166} // namespace WebCore
167
168#endif // ENABLE(WEBGL)
169