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 WebGLAttachment(AttachmentType::RENDER_BUFFER, attachment, id);
87     if (attachmentObject == nullptr) {
88         return false;
89     }
90     LOGD("AddAttachment target %{public}u attachment [%{public}u %{public}u]", target, attachment, id);
91     if (attachments_[attachment]) {
92         DoDetachment(target, attachments_[attachment]);
93         delete attachments_[attachment];
94     }
95     attachments_[attachment] = attachmentObject;
96     return true;
97 }
98 
AddAttachment(GLenum target,GLenum attachment,GLuint id,GLenum textureTarget,GLint level)99 bool WebGLFramebuffer::AddAttachment(GLenum target, GLenum attachment, GLuint id, GLenum textureTarget, GLint level)
100 {
101     AttachmentTexture* attachmentObject = new AttachmentTexture(AttachmentType::TEXTURE, attachment, id);
102     if (attachmentObject == nullptr) {
103         return false;
104     }
105     LOGD("AddAttachment target %{public}u attachment [%{public}u %{public}u] texture [%{public}u %{public}d]", target,
106         attachment, id, textureTarget, level);
107     if (attachments_[attachment]) {
108         DoDetachment(target, attachments_[attachment]);
109         delete attachments_[attachment];
110     }
111     attachments_[attachment] = attachmentObject;
112     attachmentObject->target = textureTarget;
113     attachmentObject->level = level;
114     return true;
115 }
116 
DoDetachment(GLenum target,WebGLAttachment * attachment)117 void WebGLFramebuffer::DoDetachment(GLenum target, WebGLAttachment* attachment)
118 {
119     if (attachment == nullptr) {
120         return;
121     }
122     LOGD("DoDetachment target %{public}u attachment [%{public}u %{public}u] %{public}u",
123         target, attachment->attachment, attachment->id, attachment->type);
124     if (attachment->IsRenderBuffer()) {
125         if (attachment->attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
126             glFramebufferRenderbuffer(target, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
127             glFramebufferRenderbuffer(target, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
128         } else {
129             glFramebufferRenderbuffer(target, attachment->attachment, GL_RENDERBUFFER, 0);
130         }
131     } else {
132         auto textureAttachment = static_cast<const AttachmentTexture*>(attachment);
133         if (attachment->attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
134             glFramebufferTexture2D(target,
135                 GL_DEPTH_ATTACHMENT, textureAttachment->target, 0, textureAttachment->level);
136             glFramebufferTexture2D(target,
137                 GL_STENCIL_ATTACHMENT, textureAttachment->target, 0, textureAttachment->level);
138         } else {
139             glFramebufferTexture2D(target,
140                 attachment->attachment, textureAttachment->target, 0, textureAttachment->level);
141         }
142     }
143 }
144 
RemoveAttachment(GLenum target,GLuint id,AttachmentType type)145 void WebGLFramebuffer::RemoveAttachment(GLenum target, GLuint id, AttachmentType type)
146 {
147     for (auto iter = attachments_.begin(); iter != attachments_.end(); iter++) {
148         auto object = iter->second;
149         if (object != nullptr && object->id == id && type == object->type) {
150             DoDetachment(target, iter->second);
151             delete iter->second;
152             attachments_.erase(iter);
153             break;
154         }
155     }
156 }
157 
GetAttachment(GLenum attachment) const158 WebGLAttachment* WebGLFramebuffer::GetAttachment(GLenum attachment) const
159 {
160     auto it = attachments_.find(attachment);
161     if (it != attachments_.end()) {
162         return it->second;
163     }
164     return nullptr;
165 }
166 
IsDepthRenderAble(GLenum internalFormat,bool includesDepthStencil) const167 bool WebGLFramebuffer::IsDepthRenderAble(GLenum internalFormat, bool includesDepthStencil) const
168 {
169     switch (internalFormat) {
170         case GL_DEPTH_COMPONENT:
171         case GL_DEPTH_COMPONENT16:
172         case GL_DEPTH_COMPONENT24:
173         case GL_DEPTH_COMPONENT32F:
174             return true;
175         case GL_DEPTH_STENCIL:
176         case GL_DEPTH24_STENCIL8:
177         case GL_DEPTH32F_STENCIL8:
178             return includesDepthStencil;
179         default:
180             return false;
181     }
182 }
183 
IsColorRenderAble(GLenum internalFormat) const184 bool WebGLFramebuffer::IsColorRenderAble(GLenum internalFormat) const
185 {
186     switch (internalFormat) {
187         case WebGLRenderingContextBase::RGB:
188         case WebGLRenderingContextBase::RGBA:
189         case WebGL2RenderingContextBase::R8:
190         case WebGL2RenderingContextBase::R8UI:
191         case WebGL2RenderingContextBase::R8I:
192         case WebGL2RenderingContextBase::R16UI:
193         case WebGL2RenderingContextBase::R16I:
194         case WebGL2RenderingContextBase::R32UI:
195         case WebGL2RenderingContextBase::R32I:
196         case WebGL2RenderingContextBase::RG8:
197         case WebGL2RenderingContextBase::RG8UI:
198         case WebGL2RenderingContextBase::RG8I:
199         case WebGL2RenderingContextBase::RG16UI:
200         case WebGL2RenderingContextBase::RG16I:
201         case WebGL2RenderingContextBase::RG32UI:
202         case WebGL2RenderingContextBase::RG32I:
203         case WebGL2RenderingContextBase::RGB8:
204         case WebGLRenderingContextBase::RGB565:
205         case WebGL2RenderingContextBase::RGBA8:
206         case WebGL2RenderingContextBase::SRGB8_ALPHA8:
207         case WebGLRenderingContextBase::RGB5_A1:
208         case WebGLRenderingContextBase::RGBA4:
209         case WebGL2RenderingContextBase::RGB10_A2:
210         case WebGL2RenderingContextBase::RGBA8UI:
211         case WebGL2RenderingContextBase::RGBA8I:
212         case WebGL2RenderingContextBase::RGB10_A2UI:
213         case WebGL2RenderingContextBase::RGBA16UI:
214         case WebGL2RenderingContextBase::RGBA16I:
215         case WebGL2RenderingContextBase::RGBA32UI:
216         case WebGL2RenderingContextBase::RGBA32I:
217             return true;
218         default:
219             return false;
220     }
221 }
222 
IsStencilRenderAble(GLenum internalFormat,bool includesDepthStencil) const223 bool WebGLFramebuffer::IsStencilRenderAble(GLenum internalFormat, bool includesDepthStencil) const
224 {
225     switch (internalFormat) {
226         case WebGLRenderingContextBase::STENCIL_INDEX8:
227             return true;
228         case WebGLRenderingContextBase::DEPTH_STENCIL:
229         case WebGL2RenderingContextBase::DEPTH24_STENCIL8:
230         case WebGL2RenderingContextBase::DEPTH32F_STENCIL8:
231             return includesDepthStencil;
232         default:
233             return false;
234     }
235 }
236 
GetWebGLAttachmentInfo(napi_env env,Impl::WebGLRenderingContextBaseImpl * context,const WebGLAttachment * attachedObject,WebGLAttachmentInfo & info) const237 bool WebGLFramebuffer::GetWebGLAttachmentInfo(napi_env env, Impl::WebGLRenderingContextBaseImpl* context,
238     const WebGLAttachment* attachedObject, WebGLAttachmentInfo& info) const
239 {
240     if (attachedObject == nullptr) {
241         return false;
242     }
243 
244     LOGD("GetWebGLAttachmentInfo %{public}u %{public}d %{public}lu",
245          static_cast<unsigned int>(attachedObject->type), attachedObject->attachment,
246          static_cast<unsigned long>(attachedObject->id));
247     if (attachedObject->IsRenderBuffer()) {
248         WebGLRenderbuffer* renderBuffer =
249             context->GetObjectInstance<WebGLRenderbuffer>(env, static_cast<uint64_t>(attachedObject->id));
250         if (renderBuffer == nullptr) {
251             return false;
252         }
253         info.format = renderBuffer->GetInternalFormat();
254         info.width = renderBuffer->GetWidth();
255         info.height = renderBuffer->GetHeight();
256         info.type = 0;
257         return true;
258     }
259     if (attachedObject->IsTexture()) {
260         WebGLTexture* texture = context->GetObjectInstance<WebGLTexture>(
261             env, static_cast<uint64_t>(attachedObject->id));
262         if (texture == nullptr) {
263             return false;
264         }
265         auto textureAttachment = static_cast<const AttachmentTexture *>(attachedObject);
266         LOGD("GetWebGLAttachmentInfo textureAttachment target %{public}u %{public}d",
267             textureAttachment->target, textureAttachment->level);
268         info.format = texture->GetInternalFormat(textureAttachment->target, textureAttachment->level);
269         info.width = texture->GetWidth(textureAttachment->target, textureAttachment->level);
270         info.height = texture->GetHeight(textureAttachment->target, textureAttachment->level);
271         info.type = texture->GetType(textureAttachment->target, textureAttachment->level);
272         return true;
273     }
274     return false;
275 }
276 
CheckAttachmentComplete(bool isHighWebGL,WebGLAttachment * attachedObject,const WebGLAttachmentInfo & info,std::vector<WebGLAttachment * > & attachments) const277 bool WebGLFramebuffer::CheckAttachmentComplete(bool isHighWebGL, WebGLAttachment* attachedObject,
278     const WebGLAttachmentInfo& info, std::vector<WebGLAttachment*>& attachments) const
279 {
280     switch (attachedObject->attachment) {
281         case WebGLRenderingContextBase::DEPTH_ATTACHMENT: {
282             if (!IsDepthRenderAble(info.format, isHighWebGL)) {
283                 return false;
284             }
285             attachments[0] = attachedObject; // attachments[0]: depthAttachment
286             break;
287         }
288         case WebGLRenderingContextBase::STENCIL_ATTACHMENT: {
289             if (!IsStencilRenderAble(info.format, isHighWebGL)) {
290                 return false;
291             }
292             attachments[1] = attachedObject; // attachments[1]: stencilAttachment
293             break;
294         }
295         case WebGLRenderingContextBase::DEPTH_STENCIL_ATTACHMENT: {
296             if (info.format != GL_DEPTH_STENCIL_OES) {
297                 return false;
298             }
299             attachments[2] = attachedObject; // attachments[2]: depthStencilAttachment
300             break;
301         }
302         default:
303             if (!IsColorRenderAble(info.format)) {
304                 return false;
305             }
306             break;
307     }
308     return true;
309 }
310 
CheckStatus(napi_env env,Impl::WebGLRenderingContextBaseImpl * context,WebGLAttachmentInfo info,std::vector<WebGLAttachment * > & attachments,WebGLAttachment * attachedObject) const311 GLenum WebGLFramebuffer::CheckStatus(napi_env env, Impl::WebGLRenderingContextBaseImpl* context,
312     WebGLAttachmentInfo info, std::vector<WebGLAttachment*>& attachments, WebGLAttachment* attachedObject) const
313 {
314     if (!GetWebGLAttachmentInfo(env, context, attachedObject, info)) {
315         LOGE("GetWebGLAttachmentInfo failed.");
316         return WebGLRenderingContextBase::FRAMEBUFFER_UNSUPPORTED;
317     }
318     if (!info.format || !info.width || !info.height) {
319         LOGE("CheckStatus info failed.");
320         return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
321     }
322     if (!CheckAttachmentComplete(context->IsHighWebGL(), attachedObject, info, attachments)) {
323         LOGE("CheckAttachmentComplete failed.");
324         return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
325     }
326     return WebGLRenderingContextBase::FRAMEBUFFER_COMPLETE;
327 }
328 
CheckAttachStatus(Impl::WebGLRenderingContextBaseImpl * context,std::vector<WebGLAttachment * > & attachments) const329 GLenum WebGLFramebuffer::CheckAttachStatus(Impl::WebGLRenderingContextBaseImpl* context,
330     std::vector<WebGLAttachment*>& attachments) const
331 {
332     // WebGL 1 specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
333     // 0 depthAttachment, 1 stencilAttachment, 2 depthStencilAttachment
334     if (!context->IsHighWebGL() && ((attachments[2] && (attachments[0] || attachments[1])) ||
335         (attachments[0] && attachments[1]))) {
336         LOGE("Check status depthStencilAttachment failed.");
337         return WebGLRenderingContextBase::FRAMEBUFFER_UNSUPPORTED;
338     }
339     if (context->IsHighWebGL() && (attachments[0] && attachments[1] && attachments[0]->id != attachments[1]->id)) {
340         LOGE("Check status depthAttachment failed.");
341         return WebGLRenderingContextBase::FRAMEBUFFER_UNSUPPORTED;
342     }
343     return WebGLRenderingContextBase::FRAMEBUFFER_COMPLETE;
344 }
345 
CheckStatus(napi_env env,Impl::WebGLRenderingContextBaseImpl * context) const346 GLenum WebGLFramebuffer::CheckStatus(napi_env env, Impl::WebGLRenderingContextBaseImpl* context) const
347 {
348     uint32_t count = 0;
349     GLsizei width = 0;
350     GLsizei height = 0;
351     std::vector<WebGLAttachment*> attachments = {nullptr, nullptr, nullptr};
352     WebGLAttachmentInfo info;
353     GLenum result;
354     LOGD("CheckStatus %{public}u", static_cast<unsigned int>(attachments_.size()));
355     for (const auto& it : attachments_) {
356         WebGLAttachment* attachedObject = it.second;
357         result = CheckStatus(env, context, info, attachments, attachedObject);
358         if (result != WebGLRenderingContextBase::FRAMEBUFFER_COMPLETE) {
359             return result;
360         }
361         if (!context->IsHighWebGL()) {
362             if (!count) {
363                 width = info.width;
364                 height = info.height;
365             } else if (width != info.width || height != info.height) {
366                 LOGE("CheckStatus attachmentInfo failed.");
367                 return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
368             }
369         }
370         ++count;
371     }
372     if (!count) {
373         return WebGLRenderingContextBase::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
374     }
375 
376     return CheckAttachStatus(context, attachments);
377 }
378 } // namespace Rosen
379 } // namespace OHOS
380