1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "webgl/webgl_framebuffer.h"
17
18 #include "context/webgl2_rendering_context_base.h"
19 #include "context/webgl_rendering_context_base.h"
20 #include "context/webgl_rendering_context_base_impl.h"
21 #include "napi/n_class.h"
22 #include "napi/n_func_arg.h"
23 #include "napi/n_val.h"
24 #include "util/util.h"
25
26 namespace OHOS {
27 namespace Rosen {
28 using namespace std;
Constructor(napi_env env,napi_callback_info info)29 napi_value WebGLFramebuffer::Constructor(napi_env env, napi_callback_info info)
30 {
31 NFuncArg funcArg(env, info);
32 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
33 return nullptr;
34 }
35
36 unique_ptr<WebGLFramebuffer> webGlFramebuffer = make_unique<WebGLFramebuffer>();
37 if (!NClass::SetEntityFor<WebGLFramebuffer>(env, funcArg.GetThisVar(), move(webGlFramebuffer))) {
38 LOGE("SetEntityFor webGlFramebuffer failed.");
39 return nullptr;
40 }
41 return funcArg.GetThisVar();
42 }
43
Export(napi_env env,napi_value exports)44 bool WebGLFramebuffer::Export(napi_env env, napi_value exports)
45 {
46 vector<napi_property_descriptor> props = {};
47
48 string className = GetClassName();
49 bool succ = false;
50 napi_value clas = nullptr;
51 tie(succ, clas) = NClass::DefineClass(exports_.env_, className, WebGLFramebuffer::Constructor, std::move(props));
52 if (!succ) {
53 LOGE("DefineClass webGlFramebuffer failed.");
54 return false;
55 }
56 succ = NClass::SaveClass(exports_.env_, className, clas);
57 if (!succ) {
58 LOGE("SaveClass webGlFramebuffer failed.");
59 return false;
60 }
61
62 return exports_.AddProp(className, clas);
63 }
64
GetClassName()65 string WebGLFramebuffer::GetClassName()
66 {
67 return WebGLFramebuffer::className;
68 }
69
CreateObjectInstance(napi_env env,WebGLFramebuffer ** instance)70 NVal WebGLFramebuffer::CreateObjectInstance(napi_env env, WebGLFramebuffer** instance)
71 {
72 return WebGLObject::CreateObjectInstance<WebGLFramebuffer>(env, instance);
73 }
74
~WebGLFramebuffer()75 WebGLFramebuffer::~WebGLFramebuffer()
76 {
77 auto it = attachments_.begin();
78 while (it != attachments_.end()) {
79 delete it->second;
80 it = attachments_.erase(it);
81 }
82 }
83
AddAttachment(GLenum target,GLenum attachment,GLuint id)84 bool WebGLFramebuffer::AddAttachment(GLenum target, GLenum attachment, GLuint id)
85 {
86 WebGLAttachment* attachmentObject = new(std::nothrow) WebGLAttachment(
87 AttachmentType::RENDER_BUFFER, attachment, id);
88 if (attachmentObject == nullptr) {
89 return false;
90 }
91 LOGD("AddAttachment target %{public}u attachment [%{public}u %{public}u]", target, attachment, id);
92 if (attachments_[attachment]) {
93 DoDetachment(target, attachments_[attachment]);
94 delete attachments_[attachment];
95 }
96 attachments_[attachment] = attachmentObject;
97 return true;
98 }
99
AddAttachment(GLenum target,GLenum attachment,GLuint id,GLenum textureTarget,GLint level)100 bool WebGLFramebuffer::AddAttachment(GLenum target, GLenum attachment, GLuint id, GLenum textureTarget, GLint level)
101 {
102 AttachmentTexture* attachmentObject = new(std::nothrow) AttachmentTexture(AttachmentType::TEXTURE, attachment, id);
103 if (attachmentObject == nullptr) {
104 return false;
105 }
106 LOGD("AddAttachment target %{public}u attachment [%{public}u %{public}u] texture [%{public}u %{public}d]", target,
107 attachment, id, textureTarget, level);
108 if (attachments_[attachment]) {
109 DoDetachment(target, attachments_[attachment]);
110 delete attachments_[attachment];
111 }
112 attachments_[attachment] = attachmentObject;
113 attachmentObject->target = textureTarget;
114 attachmentObject->level = level;
115 return true;
116 }
117
DoDetachment(GLenum target,WebGLAttachment * attachment)118 void WebGLFramebuffer::DoDetachment(GLenum target, WebGLAttachment* attachment)
119 {
120 if (attachment == nullptr) {
121 return;
122 }
123 LOGD("DoDetachment target %{public}u attachment [%{public}u %{public}u] %{public}u",
124 target, attachment->attachment, attachment->id, attachment->type);
125 if (attachment->IsRenderBuffer()) {
126 if (attachment->attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
127 glFramebufferRenderbuffer(target, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
128 glFramebufferRenderbuffer(target, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
129 } else {
130 glFramebufferRenderbuffer(target, attachment->attachment, GL_RENDERBUFFER, 0);
131 }
132 } else {
133 auto textureAttachment = static_cast<const AttachmentTexture*>(attachment);
134 if (attachment->attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
135 glFramebufferTexture2D(target,
136 GL_DEPTH_ATTACHMENT, textureAttachment->target, 0, textureAttachment->level);
137 glFramebufferTexture2D(target,
138 GL_STENCIL_ATTACHMENT, textureAttachment->target, 0, textureAttachment->level);
139 } else {
140 glFramebufferTexture2D(target,
141 attachment->attachment, textureAttachment->target, 0, textureAttachment->level);
142 }
143 }
144 }
145
RemoveAttachment(GLenum target,GLuint id,AttachmentType type)146 void WebGLFramebuffer::RemoveAttachment(GLenum target, GLuint id, AttachmentType type)
147 {
148 for (auto iter = attachments_.begin(); iter != attachments_.end(); iter++) {
149 auto object = iter->second;
150 if (object != nullptr && object->id == id && type == object->type) {
151 DoDetachment(target, iter->second);
152 delete iter->second;
153 attachments_.erase(iter);
154 break;
155 }
156 }
157 }
158
GetAttachment(GLenum attachment) const159 WebGLAttachment* WebGLFramebuffer::GetAttachment(GLenum attachment) const
160 {
161 auto it = attachments_.find(attachment);
162 if (it != attachments_.end()) {
163 return it->second;
164 }
165 return nullptr;
166 }
167
IsDepthRenderAble(GLenum internalFormat,bool includesDepthStencil) const168 bool WebGLFramebuffer::IsDepthRenderAble(GLenum internalFormat, bool includesDepthStencil) const
169 {
170 switch (internalFormat) {
171 case GL_DEPTH_COMPONENT:
172 case GL_DEPTH_COMPONENT16:
173 case GL_DEPTH_COMPONENT24:
174 case GL_DEPTH_COMPONENT32F:
175 return true;
176 case GL_DEPTH_STENCIL:
177 case GL_DEPTH24_STENCIL8:
178 case GL_DEPTH32F_STENCIL8:
179 return includesDepthStencil;
180 default:
181 return false;
182 }
183 }
184
IsColorRenderAble(GLenum internalFormat) const185 bool WebGLFramebuffer::IsColorRenderAble(GLenum internalFormat) const
186 {
187 switch (internalFormat) {
188 case WebGLRenderingContextBase::RGB:
189 case WebGLRenderingContextBase::RGBA:
190 case WebGL2RenderingContextBase::R8:
191 case WebGL2RenderingContextBase::R8UI:
192 case WebGL2RenderingContextBase::R8I:
193 case WebGL2RenderingContextBase::R16UI:
194 case WebGL2RenderingContextBase::R16I:
195 case WebGL2RenderingContextBase::R32UI:
196 case WebGL2RenderingContextBase::R32I:
197 case WebGL2RenderingContextBase::RG8:
198 case WebGL2RenderingContextBase::RG8UI:
199 case WebGL2RenderingContextBase::RG8I:
200 case WebGL2RenderingContextBase::RG16UI:
201 case WebGL2RenderingContextBase::RG16I:
202 case WebGL2RenderingContextBase::RG32UI:
203 case WebGL2RenderingContextBase::RG32I:
204 case WebGL2RenderingContextBase::RGB8:
205 case WebGLRenderingContextBase::RGB565:
206 case WebGL2RenderingContextBase::RGBA8:
207 case WebGL2RenderingContextBase::SRGB8_ALPHA8:
208 case WebGLRenderingContextBase::RGB5_A1:
209 case WebGLRenderingContextBase::RGBA4:
210 case WebGL2RenderingContextBase::RGB10_A2:
211 case WebGL2RenderingContextBase::RGBA8UI:
212 case WebGL2RenderingContextBase::RGBA8I:
213 case WebGL2RenderingContextBase::RGB10_A2UI:
214 case WebGL2RenderingContextBase::RGBA16UI:
215 case WebGL2RenderingContextBase::RGBA16I:
216 case WebGL2RenderingContextBase::RGBA32UI:
217 case WebGL2RenderingContextBase::RGBA32I:
218 return true;
219 default:
220 return false;
221 }
222 }
223
IsStencilRenderAble(GLenum internalFormat,bool includesDepthStencil) const224 bool WebGLFramebuffer::IsStencilRenderAble(GLenum internalFormat, bool includesDepthStencil) const
225 {
226 switch (internalFormat) {
227 case WebGLRenderingContextBase::STENCIL_INDEX8:
228 return true;
229 case WebGLRenderingContextBase::DEPTH_STENCIL:
230 case WebGL2RenderingContextBase::DEPTH24_STENCIL8:
231 case WebGL2RenderingContextBase::DEPTH32F_STENCIL8:
232 return includesDepthStencil;
233 default:
234 return false;
235 }
236 }
237
GetWebGLAttachmentInfo(napi_env env,Impl::WebGLRenderingContextBaseImpl * context,const WebGLAttachment * attachedObject,WebGLAttachmentInfo & info) const238 bool WebGLFramebuffer::GetWebGLAttachmentInfo(napi_env env, Impl::WebGLRenderingContextBaseImpl* context,
239 const WebGLAttachment* attachedObject, WebGLAttachmentInfo& info) const
240 {
241 if (attachedObject == nullptr) {
242 return false;
243 }
244
245 LOGD("GetWebGLAttachmentInfo %{public}u %{public}d %{public}lu",
246 static_cast<unsigned int>(attachedObject->type), attachedObject->attachment,
247 static_cast<unsigned long>(attachedObject->id));
248 if (attachedObject->IsRenderBuffer()) {
249 WebGLRenderbuffer* renderBuffer =
250 context->GetObjectInstance<WebGLRenderbuffer>(env, static_cast<uint64_t>(attachedObject->id));
251 if (renderBuffer == nullptr) {
252 return false;
253 }
254 info.format = renderBuffer->GetInternalFormat();
255 info.width = renderBuffer->GetWidth();
256 info.height = renderBuffer->GetHeight();
257 info.type = 0;
258 return true;
259 }
260 if (attachedObject->IsTexture()) {
261 WebGLTexture* texture = context->GetObjectInstance<WebGLTexture>(
262 env, static_cast<uint64_t>(attachedObject->id));
263 if (texture == nullptr) {
264 return false;
265 }
266 auto textureAttachment = static_cast<const AttachmentTexture *>(attachedObject);
267 LOGD("GetWebGLAttachmentInfo textureAttachment target %{public}u %{public}d",
268 textureAttachment->target, textureAttachment->level);
269 info.format = texture->GetInternalFormat(textureAttachment->target, textureAttachment->level);
270 info.width = texture->GetWidth(textureAttachment->target, textureAttachment->level);
271 info.height = texture->GetHeight(textureAttachment->target, textureAttachment->level);
272 info.type = texture->GetType(textureAttachment->target, textureAttachment->level);
273 return true;
274 }
275 return false;
276 }
277
CheckAttachmentComplete(bool isHighWebGL,WebGLAttachment * attachedObject,const WebGLAttachmentInfo & info,std::vector<WebGLAttachment * > & attachments) const278 bool WebGLFramebuffer::CheckAttachmentComplete(bool isHighWebGL, WebGLAttachment* attachedObject,
279 const WebGLAttachmentInfo& info, std::vector<WebGLAttachment*>& attachments) const
280 {
281 switch (attachedObject->attachment) {
282 case WebGLRenderingContextBase::DEPTH_ATTACHMENT: {
283 if (!IsDepthRenderAble(info.format, isHighWebGL)) {
284 return false;
285 }
286 attachments[0] = attachedObject; // attachments[0]: depthAttachment
287 break;
288 }
289 case WebGLRenderingContextBase::STENCIL_ATTACHMENT: {
290 if (!IsStencilRenderAble(info.format, isHighWebGL)) {
291 return false;
292 }
293 attachments[1] = attachedObject; // attachments[1]: stencilAttachment
294 break;
295 }
296 case WebGLRenderingContextBase::DEPTH_STENCIL_ATTACHMENT: {
297 if (info.format != GL_DEPTH_STENCIL_OES) {
298 return false;
299 }
300 attachments[2] = attachedObject; // attachments[2]: depthStencilAttachment
301 break;
302 }
303 default:
304 if (!IsColorRenderAble(info.format)) {
305 return false;
306 }
307 break;
308 }
309 return true;
310 }
311
CheckStatus(napi_env env,Impl::WebGLRenderingContextBaseImpl * context,WebGLAttachmentInfo info,std::vector<WebGLAttachment * > & attachments,WebGLAttachment * attachedObject) const312 GLenum WebGLFramebuffer::CheckStatus(napi_env env, Impl::WebGLRenderingContextBaseImpl* context,
313 WebGLAttachmentInfo info, std::vector<WebGLAttachment*>& attachments, WebGLAttachment* attachedObject) const
314 {
315 if (!GetWebGLAttachmentInfo(env, context, attachedObject, info)) {
316 LOGE("GetWebGLAttachmentInfo failed.");
317 return WebGLRenderingContextBase::FRAMEBUFFER_UNSUPPORTED;
318 }
319 if (!info.format || !info.width || !info.height) {
320 LOGE("CheckStatus info failed.");
321 return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
322 }
323 if (!CheckAttachmentComplete(context->IsHighWebGL(), attachedObject, info, attachments)) {
324 LOGE("CheckAttachmentComplete failed.");
325 return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
326 }
327 return WebGLRenderingContextBase::FRAMEBUFFER_COMPLETE;
328 }
329
CheckAttachStatus(Impl::WebGLRenderingContextBaseImpl * context,std::vector<WebGLAttachment * > & attachments) const330 GLenum WebGLFramebuffer::CheckAttachStatus(Impl::WebGLRenderingContextBaseImpl* context,
331 std::vector<WebGLAttachment*>& attachments) const
332 {
333 // WebGL 1 specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
334 // 0 depthAttachment, 1 stencilAttachment, 2 depthStencilAttachment
335 if (!context->IsHighWebGL() && ((attachments[2] && (attachments[0] || attachments[1])) ||
336 (attachments[0] && attachments[1]))) {
337 LOGE("Check status depthStencilAttachment failed.");
338 return WebGLRenderingContextBase::FRAMEBUFFER_UNSUPPORTED;
339 }
340 if (context->IsHighWebGL() && (attachments[0] && attachments[1] && attachments[0]->id != attachments[1]->id)) {
341 LOGE("Check status depthAttachment failed.");
342 return WebGLRenderingContextBase::FRAMEBUFFER_UNSUPPORTED;
343 }
344 return WebGLRenderingContextBase::FRAMEBUFFER_COMPLETE;
345 }
346
CheckStatus(napi_env env,Impl::WebGLRenderingContextBaseImpl * context) const347 GLenum WebGLFramebuffer::CheckStatus(napi_env env, Impl::WebGLRenderingContextBaseImpl* context) const
348 {
349 uint32_t count = 0;
350 GLsizei width = 0;
351 GLsizei height = 0;
352 std::vector<WebGLAttachment*> attachments = {nullptr, nullptr, nullptr};
353 WebGLAttachmentInfo info;
354 GLenum result;
355 LOGD("CheckStatus %{public}u", static_cast<unsigned int>(attachments_.size()));
356 for (const auto& it : attachments_) {
357 WebGLAttachment* attachedObject = it.second;
358 result = CheckStatus(env, context, info, attachments, attachedObject);
359 if (result != WebGLRenderingContextBase::FRAMEBUFFER_COMPLETE) {
360 return result;
361 }
362 if (!context->IsHighWebGL()) {
363 if (!count) {
364 width = info.width;
365 height = info.height;
366 } else if (width != info.width || height != info.height) {
367 LOGE("CheckStatus attachmentInfo failed.");
368 return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
369 }
370 }
371 ++count;
372 }
373 if (!count) {
374 return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
375 }
376
377 return CheckAttachStatus(context, attachments);
378 }
379 } // namespace Rosen
380 } // namespace OHOS
381