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