• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "node_context_pool_manager_gles.h"
17 
18 #include <algorithm>
19 
20 #include <render/namespace.h>
21 
22 #include "device/gpu_resource_manager.h"
23 #include "gles/device_gles.h"
24 #include "gles/gl_functions.h"
25 #include "gles/gpu_image_gles.h"
26 #include "nodecontext/render_command_list.h" // RenderPassBeginInfo...
27 #include "util/log.h"
28 
29 using namespace BASE_NS;
30 
31 RENDER_BEGIN_NAMESPACE()
32 namespace {
33 constexpr const bool VERBOSE_LOGGING = false;
34 constexpr const bool HASH_LAYOUTS = false;
35 constexpr const uint32_t EMPTY_ATTACHMENT = ~0u;
36 
37 struct BindImage {
38     uint32_t layer;
39     uint32_t mipLevel;
40     const GpuImageGLES* image;
41 };
42 
43 struct FboHash {
44     uint64_t hash;
45     GLuint fbo;
46 };
47 
HighestBit(uint32_t value)48 uint32_t HighestBit(uint32_t value)
49 {
50     uint32_t count = 0;
51     while (value) {
52         ++count;
53         value >>= 1U;
54     }
55     return count;
56 }
57 
UpdateBindImages(const RenderCommandBeginRenderPass & beginRenderPass,array_view<BindImage> images,GpuResourceManager & gpuResourceMgr_)58 bool UpdateBindImages(const RenderCommandBeginRenderPass& beginRenderPass, array_view<BindImage> images,
59     GpuResourceManager& gpuResourceMgr_)
60 {
61     const auto& renderPassDesc = beginRenderPass.renderPassDesc;
62     for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
63         images[idx].layer = renderPassDesc.attachments[idx].layer;
64         images[idx].mipLevel = renderPassDesc.attachments[idx].mipLevel;
65         images[idx].image = gpuResourceMgr_.GetImage<GpuImageGLES>(renderPassDesc.attachmentHandles[idx]);
66         if (!images[idx].image) {
67             return false;
68         }
69     }
70     return true;
71 }
72 
HashRPD(const RenderCommandBeginRenderPass & beginRenderPass,GpuResourceManager & gpuResourceMgr)73 uint64_t HashRPD(const RenderCommandBeginRenderPass& beginRenderPass, GpuResourceManager& gpuResourceMgr)
74 {
75     const auto& renderPassDesc = beginRenderPass.renderPassDesc;
76     uint64_t rpHash = 0;
77     // hash engine gpu handle
78     {
79         for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
80             HashCombine(rpHash, renderPassDesc.attachments[idx].layer);
81             HashCombine(rpHash, renderPassDesc.attachments[idx].mipLevel);
82             // generation counters and hashing with handles is not enough
83             // the reason is that e.g. shallow handles can point to to different GPU handles
84             // and have counter of zero in their index
85             // this can lead with handle re-use to situations where the gen counter is zero
86             // NOTE: we hash with our own gpuHandle and gl image (if gl image id would be re-used)
87             const RenderHandle clientHandle = renderPassDesc.attachmentHandles[idx];
88             const EngineResourceHandle gpuHandle = gpuResourceMgr.GetGpuHandle(clientHandle);
89             uint64_t imageId = clientHandle.id;
90             if (const GpuImageGLES* image = gpuResourceMgr.GetImage<GpuImageGLES>(clientHandle); image) {
91                 imageId = (uint64_t)(image->GetPlatformData().image);
92             }
93             HashCombine(rpHash, gpuHandle.id, imageId);
94         }
95     }
96 
97     // hash input and output layouts (ignored since, they do not contribute in GL/GLES at all, they are a artifact
98     // of vulkan)
99     if constexpr (HASH_LAYOUTS) {
100         const RenderPassImageLayouts& renderPassImageLayouts = beginRenderPass.imageLayouts;
101         for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
102             HashCombine(rpHash, renderPassImageLayouts.attachmentInitialLayouts[idx],
103                 renderPassImageLayouts.attachmentFinalLayouts[idx]);
104         }
105     }
106 
107     // hash subpasses
108     PLUGIN_ASSERT(renderPassDesc.subpassCount <= beginRenderPass.subpasses.size());
109     for (const RenderPassSubpassDesc& subpass : beginRenderPass.subpasses) {
110         HashRange(
111             rpHash, subpass.inputAttachmentIndices, subpass.inputAttachmentIndices + subpass.inputAttachmentCount);
112         HashRange(
113             rpHash, subpass.colorAttachmentIndices, subpass.colorAttachmentIndices + subpass.colorAttachmentCount);
114         if (subpass.depthAttachmentCount) {
115             HashCombine(rpHash, (uint64_t)subpass.depthAttachmentIndex);
116         }
117         HashCombine(rpHash, (uint64_t)subpass.viewMask);
118     }
119     return rpHash;
120 }
121 
VerifyFBO()122 bool VerifyFBO()
123 {
124     GLenum status;
125     status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
126     if (status != GL_FRAMEBUFFER_COMPLETE) {
127         // failed!
128 #if (RENDER_VALIDATION_ENABLED == 1)
129         switch (status) {
130             case GL_FRAMEBUFFER_UNDEFINED:
131                 // is returned if target is the default framebuffer, but the default framebuffer does not exist.
132                 PLUGIN_LOG_E("Framebuffer undefined");
133                 break;
134             case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
135                 // is returned if any of the framebuffer attachment points are framebuffer incomplete.
136                 PLUGIN_LOG_E("Framebuffer incomplete attachment");
137                 break;
138             case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
139                 // is returned if the framebuffer does not haveat least one image attached to it.
140                 PLUGIN_LOG_E("Framebuffer incomplete missing attachment");
141                 break;
142             case GL_FRAMEBUFFER_UNSUPPORTED:
143                 // is returned if depth and stencil attachments, if present, are not the same renderbuffer, or
144                 // if the combination of internal formats of the attached images violates an
145                 // implementation-dependent set of restrictions.
146                 PLUGIN_LOG_E("Framebuffer unsupported");
147                 break;
148             case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
149                 // is returned if the value of GL_RENDERBUFFER_SAMPLES  is not the same for all attached
150                 // renderbuffers or, if the attached images are a mix of renderbuffers and textures, the value
151                 // of GL_RENDERBUFFER_SAMPLES is not zero.
152                 PLUGIN_LOG_E("Framebuffer incomplete multisample");
153                 break;
154             case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
155                 // is returned if any framebuffer attachment is layered, and any populated attachment is not
156                 // layered, or if all populated color attachments are not from textures of the same target.
157                 PLUGIN_LOG_E("Framebuffer incomplete layer targets");
158                 break;
159             case GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR:
160                 PLUGIN_LOG_E("Framebuffer incomplete view targets");
161                 break;
162             default: {
163                 PLUGIN_LOG_E("Framebuffer other error: %x", status);
164                 break;
165             }
166         }
167 #endif
168         return false;
169     }
170     return true;
171 }
172 
HasStencil(const GpuImageGLES * image)173 bool HasStencil(const GpuImageGLES* image)
174 {
175     const auto& dplat = static_cast<const GpuImagePlatformDataGL&>(image->GetPlatformData());
176     return (dplat.format == GL_STENCIL_INDEX) || (dplat.format == GL_DEPTH_STENCIL);
177 }
178 
HasDepth(const GpuImageGLES * image)179 bool HasDepth(const GpuImageGLES* image)
180 {
181     const auto& dplat = static_cast<const GpuImagePlatformDataGL&>(image->GetPlatformData());
182     return (dplat.format == GL_DEPTH_COMPONENT) || (dplat.format == GL_DEPTH_STENCIL);
183 }
184 
185 #if RENDER_GL_FLIP_Y_SWAPCHAIN == 0
IsDefaultAttachment(array_view<const BindImage> images,const RenderPassSubpassDesc & sb)186 bool IsDefaultAttachment(array_view<const BindImage> images, const RenderPassSubpassDesc& sb)
187 {
188     // Valid backbuffer configurations are.
189     // 1. color only
190     // 2. color + depth (stencil).
191     // It is not allowed to mix custom render targets and backbuffer!
192     if (sb.colorAttachmentCount == 1) {
193         // okay, looks good. one color...
194         if (const auto* color = images[sb.colorAttachmentIndices[0]].image) {
195             const auto& plat = static_cast<const GpuImagePlatformDataGL&>(color->GetPlatformData());
196             if ((plat.image == 0) &&                // not texture
197                 (plat.renderBuffer == 0))           // not renderbuffer
198             {                                       // Colorattachment is backbuffer
199                 if (sb.depthAttachmentCount == 1) { // and has one depth.
200                     const auto depth = images[sb.depthAttachmentIndex].image;
201                     const auto& dPlat = static_cast<const GpuImagePlatformDataGL&>(depth->GetPlatformData());
202                     // NOTE: CORE_DEFAULT_BACKBUFFER_DEPTH is not used legacy way anymore
203                     if ((dPlat.image == 0) && (dPlat.renderBuffer == 0)) { // depth attachment is backbuffer depth.
204                         return true;
205                     } else {
206                         // Invalid configuration. (this should be caught earlier already)
207 #if (RENDER_VALIDATION_ENABLED == 1)
208                         PLUGIN_LOG_ONCE_I("backbuffer_depth_gles_mixing" + to_string(plat.image),
209                             "RENDER_VALIDATION: Depth target dropped due to using swapchain buffer (depth)");
210 #endif
211                         return true;
212                     }
213                 } else {
214                     return true;
215                 }
216             }
217         }
218     }
219     return false;
220 }
221 
IsDefaultResolve(array_view<const BindImage> images,const RenderPassSubpassDesc & sb)222 bool IsDefaultResolve(array_view<const BindImage> images, const RenderPassSubpassDesc& sb)
223 {
224     if (sb.resolveAttachmentCount == 1 && sb.depthResolveAttachmentCount == 0) {
225         // looks good, one color
226         if (sb.resolveAttachmentIndices[0U] < static_cast<uint32_t>(images.size())) {
227             if (const GpuImageGLES* color = images[sb.resolveAttachmentIndices[0]].image; color) {
228                 const auto& plat = static_cast<const GpuImagePlatformDataGL&>(color->GetPlatformData());
229                 if ((plat.image == 0) && (plat.renderBuffer == 0)) {
230                     return true;
231                 }
232             }
233         }
234     }
235     if (sb.depthResolveAttachmentCount == 1) {
236         if (sb.depthResolveAttachmentIndex < static_cast<uint32_t>(images.size())) {
237             // looks good, one depth
238             if (const GpuImageGLES* depth = images[sb.depthResolveAttachmentIndex].image; depth) {
239                 const auto& plat = static_cast<const GpuImagePlatformDataGL&>(depth->GetPlatformData());
240                 if ((plat.image == 0) && (plat.renderBuffer == 0)) {
241                     return true;
242                 }
243             }
244         }
245     }
246     return false;
247 }
248 #endif
249 
DeleteFbos(DeviceGLES & device,LowlevelFramebufferGL & ref)250 void DeleteFbos(DeviceGLES& device, LowlevelFramebufferGL& ref)
251 {
252     for (uint32_t i = 0; i < ref.fbos.size(); i++) {
253         GLuint f, r;
254         f = ref.fbos[i].fbo;
255         r = ref.fbos[i].resolve;
256         if (f != 0) {
257             device.DeleteFrameBuffer(f);
258         }
259         if (r != 0) {
260             device.DeleteFrameBuffer(r);
261         }
262         // the same fbos can be used multiple render passes, so clean those references too.
263         for (auto& fbo : ref.fbos) {
264             if (f == fbo.fbo) {
265                 fbo.fbo = 0;
266             }
267             if (r == fbo.resolve) {
268                 fbo.resolve = 0;
269             }
270         }
271     }
272     ref.fbos.clear();
273 }
274 
BindToFbo(GLenum attachType,const BindImage & image,uint32_t & width,uint32_t & height,uint32_t views,bool isStarted)275 void BindToFbo(
276     GLenum attachType, const BindImage& image, uint32_t& width, uint32_t& height, uint32_t views, bool isStarted)
277 {
278     const auto& plat = static_cast<const GpuImagePlatformDataGL&>(image.image->GetPlatformData());
279     const GpuImageDesc& desc = image.image->GetDesc();
280     if (isStarted) {
281 #if (RENDER_VALIDATION_ENABLED == 1)
282         if (width != desc.width) {
283             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.width, width);
284         }
285         if (height != desc.height) {
286             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.height, height);
287         }
288 #endif
289         width = Math::min(width, desc.width);
290         height = Math::min(height, desc.height);
291     } else {
292         width = desc.width;
293         height = desc.height;
294     }
295     const bool isSrc = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_SRC_BIT);
296     const bool isDst = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_DST_BIT);
297     const bool isSample = (desc.usageFlags & CORE_IMAGE_USAGE_SAMPLED_BIT);
298     const bool isStorage = (desc.usageFlags & CORE_IMAGE_USAGE_STORAGE_BIT);
299     // could check for bool isColor = (desc.usageFlags & CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
300     // could check for isDepth = (desc.usageFlags & CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
301     // could check for bool isTrans = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
302     const bool isInput = (desc.usageFlags & CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
303     PLUGIN_UNUSED(isSrc);
304     PLUGIN_UNUSED(isDst);
305     PLUGIN_UNUSED(isSample);
306     PLUGIN_UNUSED(isStorage);
307     PLUGIN_UNUSED(isInput);
308     if (plat.renderBuffer) {
309         PLUGIN_ASSERT((!isSrc) && (!isDst) && (!isSample) && (!isStorage) && (!isInput));
310         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachType, GL_RENDERBUFFER, plat.renderBuffer);
311     } else {
312         if ((plat.type == GL_TEXTURE_2D_ARRAY) || (plat.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) {
313             if (views) {
314                 glFramebufferTextureMultiviewOVR(
315                     GL_FRAMEBUFFER, attachType, plat.image, (GLint)image.mipLevel, (GLint)image.layer, (GLsizei)views);
316             } else {
317                 glFramebufferTextureLayer(
318                     GL_FRAMEBUFFER, attachType, plat.image, (GLint)image.mipLevel, (GLint)image.layer);
319             }
320         } else {
321             glFramebufferTexture2D(GL_FRAMEBUFFER, attachType, plat.type, plat.image, (GLint)image.mipLevel);
322         }
323     }
324 }
325 
BindToFboMultisampled(GLenum attachType,const BindImage & image,const BindImage & resolveImage,uint32_t & width,uint32_t & height,uint32_t views,bool isStarted,bool multisampledRenderToTexture)326 void BindToFboMultisampled(GLenum attachType, const BindImage& image, const BindImage& resolveImage, uint32_t& width,
327     uint32_t& height, uint32_t views, bool isStarted, bool multisampledRenderToTexture)
328 {
329     const auto& plat = static_cast<const GpuImagePlatformDataGL&>(resolveImage.image->GetPlatformData());
330     const GpuImageDesc& desc = image.image->GetDesc();
331     if (isStarted) {
332 #if (RENDER_VALIDATION_ENABLED == 1)
333         if (width != desc.width) {
334             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.width, width);
335         }
336         if (height != desc.height) {
337             PLUGIN_LOG_W("Attachment is not the same size (%d) as other attachments (%u)", desc.height, height);
338         }
339 #endif
340         width = Math::min(width, desc.width);
341         height = Math::min(height, desc.height);
342     } else {
343         width = desc.width;
344         height = desc.height;
345     }
346     const bool isSrc = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_SRC_BIT);
347     const bool isDst = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSFER_DST_BIT);
348     const bool isSample = (desc.usageFlags & CORE_IMAGE_USAGE_SAMPLED_BIT);
349     const bool isStorage = (desc.usageFlags & CORE_IMAGE_USAGE_STORAGE_BIT);
350     // could check for bool isColor = (desc.usageFlags & CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
351     // could check for isDepth = (desc.usageFlags & CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
352     const bool isTrans = (desc.usageFlags & CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
353     const bool isInput = (desc.usageFlags & CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
354     PLUGIN_UNUSED(isSrc);
355     PLUGIN_UNUSED(isDst);
356     PLUGIN_UNUSED(isSample);
357     PLUGIN_UNUSED(isStorage);
358     PLUGIN_UNUSED(isTrans);
359     PLUGIN_UNUSED(isInput);
360     if (plat.renderBuffer) {
361         PLUGIN_ASSERT((!isSrc) && (!isDst) && (!isSample) && (!isStorage) && (!isInput));
362         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachType, GL_RENDERBUFFER, plat.renderBuffer);
363     } else if ((plat.type == GL_TEXTURE_2D_ARRAY) || (plat.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) {
364 #if RENDER_HAS_GLES_BACKEND
365         if (views && multisampledRenderToTexture && isTrans &&
366             ((plat.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) || (desc.sampleCountFlags & ~CORE_SAMPLE_COUNT_1_BIT))) {
367             const auto samples = (desc.sampleCountFlags & CORE_SAMPLE_COUNT_8_BIT)
368                                      ? 8
369                                      : ((desc.sampleCountFlags & CORE_SAMPLE_COUNT_4_BIT) ? 4 : 2);
370             glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, attachType, plat.image, GLint(image.mipLevel),
371                 GLint(samples), GLint(image.layer), GLsizei(views));
372         } else {
373 #endif
374             if (views) {
375                 glFramebufferTextureMultiviewOVR(
376                     GL_FRAMEBUFFER, attachType, plat.image, GLint(image.mipLevel), GLint(image.layer), GLsizei(views));
377             } else {
378                 glFramebufferTextureLayer(
379                     GL_FRAMEBUFFER, attachType, plat.image, GLint(image.mipLevel), GLint(image.layer));
380             }
381 #if RENDER_HAS_GLES_BACKEND
382         }
383     } else if (multisampledRenderToTexture && isTrans &&
384                ((plat.type == GL_TEXTURE_2D_MULTISAMPLE) || (desc.sampleCountFlags & ~CORE_SAMPLE_COUNT_1_BIT))) {
385         const auto samples = (desc.sampleCountFlags & CORE_SAMPLE_COUNT_8_BIT)
386                                  ? 8
387                                  : ((desc.sampleCountFlags & CORE_SAMPLE_COUNT_4_BIT) ? 4 : 2);
388         glFramebufferTexture2DMultisampleEXT(
389             GL_FRAMEBUFFER, attachType, plat.type, plat.image, (GLint)image.mipLevel, samples);
390 #endif
391     } else {
392         glFramebufferTexture2D(GL_FRAMEBUFFER, attachType, plat.type, plat.image, (GLint)image.mipLevel);
393     }
394 }
395 
HashAttachments(const RenderPassSubpassDesc & sb)396 uint64_t HashAttachments(const RenderPassSubpassDesc& sb)
397 {
398     // generate hash for attachments.
399     uint64_t subHash = 0;
400     HashRange(subHash, sb.colorAttachmentIndices, sb.colorAttachmentIndices + sb.colorAttachmentCount);
401     if (sb.depthAttachmentCount) {
402         HashCombine(subHash, (uint64_t)sb.depthAttachmentIndex);
403     }
404     return subHash;
405 }
406 
BindType(const GpuImageGLES * image)407 GLenum BindType(const GpuImageGLES* image)
408 {
409     GLenum bindType = GL_NONE;
410     const bool depth = HasDepth(image);
411     const bool stencil = HasStencil(image);
412     if (depth && stencil) {
413         bindType = GL_DEPTH_STENCIL_ATTACHMENT;
414     } else if (depth) {
415         bindType = GL_DEPTH_ATTACHMENT;
416     } else if (stencil) {
417         bindType = GL_STENCIL_ATTACHMENT;
418     }
419     return bindType;
420 }
421 
GenerateSubPassFBO(DeviceGLES & device,LowlevelFramebufferGL & framebuffer,const RenderPassSubpassDesc & sb,const array_view<const BindImage> images,const size_t resolveAttachmentCount,const array_view<const uint32_t> imageMap,bool multisampledRenderToTexture)422 uint32_t GenerateSubPassFBO(DeviceGLES& device, LowlevelFramebufferGL& framebuffer, const RenderPassSubpassDesc& sb,
423     const array_view<const BindImage> images, const size_t resolveAttachmentCount,
424     const array_view<const uint32_t> imageMap, bool multisampledRenderToTexture)
425 {
426     // generate fbo for subpass (depth/color).
427     GLuint fbo;
428     glGenFramebuffers(1, &fbo);
429 #if (RENDER_DEBUG_GPU_RESOURCE_IDS == 1)
430     PLUGIN_LOG_D("fbo id >: %u", fbo);
431 #endif
432     device.BindFrameBuffer(fbo);
433     GLenum drawBuffers[PipelineStateConstants::MAX_COLOR_ATTACHMENT_COUNT] = { GL_NONE };
434     GLenum colorAttachmentCount = 0;
435     const auto views = HighestBit(sb.viewMask);
436     for (uint32_t idx = 0; idx < sb.colorAttachmentCount; ++idx) {
437         const uint32_t ci = sb.colorAttachmentIndices[idx];
438         const uint32_t original = (ci < imageMap.size()) ? imageMap[ci] : EMPTY_ATTACHMENT;
439         if (images[ci].image) {
440             drawBuffers[idx] = GL_COLOR_ATTACHMENT0 + colorAttachmentCount;
441             if (original == EMPTY_ATTACHMENT) {
442                 BindToFbo(drawBuffers[idx], images[ci], framebuffer.width, framebuffer.height, views,
443                     (colorAttachmentCount) || (resolveAttachmentCount));
444             } else {
445                 BindToFboMultisampled(drawBuffers[idx], images[original], images[ci], framebuffer.width,
446                     framebuffer.height, views, (colorAttachmentCount) || (resolveAttachmentCount),
447                     multisampledRenderToTexture);
448             }
449             ++colorAttachmentCount;
450         } else {
451 #if (RENDER_VALIDATION_ENABLED == 1)
452             PLUGIN_LOG_E("RENDER_VALIDATION: no image for color attachment %u %u", idx, ci);
453 #endif
454             drawBuffers[idx] = GL_NONE;
455         }
456     }
457     glDrawBuffers((GLsizei)sb.colorAttachmentCount, drawBuffers);
458     if (sb.depthAttachmentCount == 1) {
459         const uint32_t di = sb.depthAttachmentIndex;
460         const auto* image = images[di].image;
461         uint32_t original = (di < imageMap.size()) ? imageMap[di] : EMPTY_ATTACHMENT;
462         if (original == EMPTY_ATTACHMENT) {
463             original = di;
464         }
465         if (image) {
466             const GLenum bindType = BindType(image);
467             PLUGIN_ASSERT_MSG(bindType != GL_NONE, "Depth attachment has no stencil or depth");
468             BindToFboMultisampled(bindType, images[original], images[di], framebuffer.width, framebuffer.height, views,
469                 (colorAttachmentCount) || (resolveAttachmentCount), multisampledRenderToTexture);
470         } else {
471             PLUGIN_LOG_E("no image for depth attachment");
472         }
473     }
474     if (!VerifyFBO()) {
475 #if (RENDER_VALIDATION_ENABLED == 1)
476         PLUGIN_LOG_E("RENDER_VALIDATION: Failed to create subpass FBO size [%u %u] [color:%u depth:%u resolve:%u]",
477             framebuffer.width, framebuffer.height, sb.colorAttachmentCount, sb.depthAttachmentCount,
478             sb.resolveAttachmentCount);
479 #endif
480         device.BindFrameBuffer(0U);
481         glDeleteFramebuffers(1, &fbo);
482         fbo = 0U;
483     } else if constexpr (VERBOSE_LOGGING) {
484         PLUGIN_LOG_V("Created subpass FBO size [%u %u] [color:%u depth:%u resolve:%u]", framebuffer.width,
485             framebuffer.height, sb.colorAttachmentCount, sb.depthAttachmentCount, sb.resolveAttachmentCount);
486     }
487     return fbo;
488 }
489 
490 struct ResolvePair {
491     uint32_t resolveAttachmentCount;
492     uint32_t resolveFbo;
493 };
494 
GenerateResolveFBO(DeviceGLES & device,LowlevelFramebufferGL & framebuffer,const RenderPassSubpassDesc & sb,array_view<const BindImage> images)495 ResolvePair GenerateResolveFBO(DeviceGLES& device, LowlevelFramebufferGL& framebuffer, const RenderPassSubpassDesc& sb,
496     array_view<const BindImage> images)
497 {
498     // generate fbos for resolve attachments if needed.
499     if ((sb.resolveAttachmentCount == 0) && (sb.depthResolveAttachmentCount == 0)) {
500         return { 0, 0 };
501     }
502 #if RENDER_GL_FLIP_Y_SWAPCHAIN == 0
503     // currently resolving to backbuffer AND other attachments at the same time is not possible.
504     if (IsDefaultResolve(images, sb)) {
505         // resolving from custom render target to default fbo.
506         const auto* color = images[sb.colorAttachmentIndices[0]].image;
507         if (color) {
508             const auto& desc = color->GetDesc();
509             framebuffer.width = desc.width;
510             framebuffer.height = desc.height;
511         }
512         return { 1, 0 };
513     }
514 #endif
515     // all subpasses with resolve will get a special resolve fbo.. (expecting that no more than one
516     // subpass resolves to a single attachment. if more than one subpass resolves to a single
517     // attachment we have extra fbos.)
518     ResolvePair rp { 0, 0 };
519     glGenFramebuffers(1, &rp.resolveFbo);
520 #if (RENDER_DEBUG_GPU_RESOURCE_IDS == 1)
521     PLUGIN_LOG_D("fbo id >: %u", rp.resolveFbo);
522 #endif
523     const auto views = HighestBit(sb.viewMask);
524     device.BindFrameBuffer(rp.resolveFbo);
525     GLenum drawBuffers[PipelineStateConstants::MAX_COLOR_ATTACHMENT_COUNT] = { GL_NONE };
526     for (uint32_t idx = 0; idx < sb.resolveAttachmentCount; ++idx) {
527         const uint32_t ci = sb.resolveAttachmentIndices[idx];
528         const auto* image = images[ci].image;
529         if (image) {
530             drawBuffers[idx] = GL_COLOR_ATTACHMENT0 + rp.resolveAttachmentCount;
531             BindToFbo(drawBuffers[idx], images[ci], framebuffer.width, framebuffer.height, views,
532                 (rp.resolveAttachmentCount > 0));
533             ++rp.resolveAttachmentCount;
534         } else {
535             PLUGIN_LOG_E("no image for resolve attachment %u %u", idx, ci);
536             drawBuffers[idx] = GL_NONE;
537         }
538     }
539     glDrawBuffers((GLsizei)sb.resolveAttachmentCount, drawBuffers);
540     for (uint32_t idx = 0; idx < sb.depthResolveAttachmentCount; ++idx) {
541         const uint32_t ci = sb.depthResolveAttachmentIndex;
542         const auto* image = images[ci].image;
543         if (image) {
544             BindToFbo(BindType(image), images[ci], framebuffer.width, framebuffer.height, views,
545                 (rp.resolveAttachmentCount > 0));
546         } else {
547             PLUGIN_LOG_E("no image for depth resolve attachment %u %u", idx, ci);
548         }
549     }
550     return rp;
551 }
552 
ProcessSubPass(DeviceGLES & device,LowlevelFramebufferGL & framebuffer,vector<FboHash> & fboMap,array_view<const BindImage> images,const array_view<const uint32_t> imageMap,const RenderPassSubpassDesc & sb,bool multisampledRenderToTexture)553 LowlevelFramebufferGL::SubPassPair ProcessSubPass(DeviceGLES& device, LowlevelFramebufferGL& framebuffer,
554     vector<FboHash>& fboMap, array_view<const BindImage> images, const array_view<const uint32_t> imageMap,
555     const RenderPassSubpassDesc& sb, bool multisampledRenderToTexture)
556 {
557 #if RENDER_GL_FLIP_Y_SWAPCHAIN == 0
558     if (IsDefaultAttachment(images, sb)) {
559         // This subpass uses backbuffer!
560         const auto* color = images[sb.colorAttachmentIndices[0]].image;
561         if (color) {
562             const auto& desc = color->GetDesc();
563             framebuffer.width = desc.width;
564             framebuffer.height = desc.height;
565         }
566         // NOTE: it is technically possible to resolve from backbuffer to a custom render target.
567         // but we do not support it now.
568         if (sb.resolveAttachmentCount != 0) {
569             PLUGIN_LOG_E("No resolving from default framebuffer");
570         }
571         return { 0, 0 };
572     }
573 #endif
574     // This subpass uses custom render targets.
575     PLUGIN_ASSERT((sb.colorAttachmentCount + sb.depthAttachmentCount) <
576                   (PipelineStateConstants::MAX_COLOR_ATTACHMENT_COUNT + 1)); // +1 for depth
577     uint32_t fbo = 0;
578     const auto resolveResult = GenerateResolveFBO(device, framebuffer, sb, images);
579     const uint64_t subHash = HashAttachments(sb);
580     if (const auto it = std::find_if(
581             fboMap.begin(), fboMap.end(), [subHash](const FboHash& fboHash) { return fboHash.hash == subHash; });
582         it != fboMap.end()) {
583         // matching fbo created already, re-use
584         fbo = it->fbo;
585     } else {
586         fbo = GenerateSubPassFBO(device, framebuffer, sb, images, resolveResult.resolveAttachmentCount, imageMap,
587             multisampledRenderToTexture);
588         fboMap.push_back({ subHash, fbo });
589     }
590     return { fbo, resolveResult.resolveFbo };
591 }
592 
593 #if RENDER_HAS_GLES_BACKEND
MapColorAttachments(array_view<RenderPassSubpassDesc>::iterator begin,array_view<RenderPassSubpassDesc>::iterator pos,array_view<const BindImage> images,array_view<uint32_t> imageMap,array_view<RenderPassDesc::AttachmentDesc> attachments)594 void MapColorAttachments([[maybe_unused]] array_view<RenderPassSubpassDesc>::iterator begin,
595     array_view<RenderPassSubpassDesc>::iterator pos, array_view<const BindImage> images, array_view<uint32_t> imageMap,
596     array_view<RenderPassDesc::AttachmentDesc> attachments)
597 {
598     auto resolveAttachmentIndices = array_view(pos->resolveAttachmentIndices, pos->resolveAttachmentCount);
599     const auto colorAttachmentIndices = array_view(pos->colorAttachmentIndices, pos->colorAttachmentCount);
600     for (auto i = 0U; i < pos->resolveAttachmentCount; ++i) {
601         const auto color = colorAttachmentIndices[i];
602         // if the attachment can be used as an input attachment we can't render directly to the resolve attachment.
603         if (images[color].image &&
604             (images[color].image->GetDesc().usageFlags & ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) {
605 #if (RENDER_VALIDATION_ENABLED == 1)
606             const auto subpassIdx = static_cast<uint32_t>(pos - begin);
607             const auto unique = to_string(subpassIdx) + " " + to_string(color);
608             PLUGIN_LOG_ONCE_W(unique,
609                 "Subpass %u attachment %u might be used as input attachment, cannot render directly to "
610                 "texture.",
611                 subpassIdx, color);
612 #endif
613             continue;
614         }
615         const auto resolve = exchange(resolveAttachmentIndices[i], EMPTY_ATTACHMENT);
616         // map the original attachment as the resolve
617         imageMap[color] = resolve;
618 
619         // update the resolve attachment's loadOp and clearValue to match the original attachment.
620         attachments[resolve].loadOp = attachments[color].loadOp;
621         attachments[resolve].clearValue = attachments[color].clearValue;
622     }
623 }
624 
MapDepthAttachments(array_view<RenderPassSubpassDesc>::iterator begin,array_view<RenderPassSubpassDesc>::iterator pos,array_view<const BindImage> images,array_view<uint32_t> imageMap,array_view<RenderPassDesc::AttachmentDesc> attachments)625 void MapDepthAttachments([[maybe_unused]] array_view<RenderPassSubpassDesc>::iterator begin,
626     array_view<RenderPassSubpassDesc>::iterator pos, array_view<const BindImage> images, array_view<uint32_t> imageMap,
627     array_view<RenderPassDesc::AttachmentDesc> attachments)
628 {
629     if ((pos->depthResolveAttachmentCount > 0) && (pos->depthResolveModeFlags || pos->stencilResolveModeFlags)) {
630         const auto depth = pos->depthAttachmentIndex;
631         // if the attachment can be used as an input attachment we can't render directly to the resolve attachment.
632         if (images[depth].image &&
633             (images[depth].image->GetDesc().usageFlags & ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) {
634 #if (RENDER_VALIDATION_ENABLED == 1)
635             const auto subpassIdx = static_cast<uint32_t>(pos - begin);
636             const auto unique = to_string(subpassIdx) + " " + to_string(depth);
637             PLUGIN_LOG_ONCE_W(unique,
638                 "Subpass %u attachment %u might be used as input attachment, cannot render directly to "
639                 "texture.",
640                 subpassIdx, depth);
641 #endif
642             return;
643         }
644         const auto resolve = pos->depthResolveAttachmentIndex;
645         // map the original attachment as the resolve
646         imageMap[depth] = resolve;
647 
648         // update the resolve attachment's loadOp and clearValue to match the original attachment.
649         attachments[resolve].loadOp = attachments[depth].loadOp;
650         attachments[resolve].clearValue = attachments[depth].clearValue;
651     }
652 }
653 
UpdateSubpassAttachments(array_view<RenderPassSubpassDesc>::iterator begin,array_view<RenderPassSubpassDesc>::iterator end,array_view<uint32_t> imageMap)654 void UpdateSubpassAttachments(array_view<RenderPassSubpassDesc>::iterator begin,
655     array_view<RenderPassSubpassDesc>::iterator end, array_view<uint32_t> imageMap)
656 {
657     // update all the attachment indices in the subpasses according to the mapping.
658     for (auto i = begin; i != end; ++i) {
659         for (auto ci = 0U; ci < i->colorAttachmentCount; ++ci) {
660             const auto oldColor = i->colorAttachmentIndices[ci];
661             if (const auto newColor = imageMap[oldColor]; newColor != EMPTY_ATTACHMENT) {
662                 i->colorAttachmentIndices[ci] = newColor;
663             }
664         }
665         // check what is the last index with a valid resolve attachment
666         auto resolveCount = i->resolveAttachmentCount;
667         for (; resolveCount > 0u; --resolveCount) {
668             if (i->resolveAttachmentIndices[resolveCount - 1] != EMPTY_ATTACHMENT) {
669                 break;
670             }
671         }
672         i->resolveAttachmentCount = resolveCount;
673 
674         const auto oldDepth = i->depthAttachmentIndex;
675         if (oldDepth != EMPTY_ATTACHMENT) {
676             if (const auto newDepth = imageMap[oldDepth]; newDepth != EMPTY_ATTACHMENT) {
677                 i->depthAttachmentIndex = newDepth;
678                 i->depthResolveAttachmentCount = 0;
679             }
680         }
681     }
682 }
683 #endif
684 } // namespace
685 
NodeContextPoolManagerGLES(Device & device,GpuResourceManager & gpuResourceManager)686 NodeContextPoolManagerGLES::NodeContextPoolManagerGLES(Device& device, GpuResourceManager& gpuResourceManager)
687     : NodeContextPoolManager(), device_ { (DeviceGLES&)device }, gpuResourceMgr_ { gpuResourceManager }
688 {
689     bufferingCount_ = device_.GetCommandBufferingCount();
690 #if RENDER_HAS_GLES_BACKEND
691     if (device_.GetBackendType() == DeviceBackendType::OPENGLES) {
692         multisampledRenderToTexture_ = device_.HasExtension("GL_EXT_multisampled_render_to_texture2");
693         multiViewMultisampledRenderToTexture_ = device_.HasExtension("GL_OVR_multiview_multisampled_render_to_texture");
694     }
695 #endif
696     multiView_ = device_.HasExtension("GL_OVR_multiview2");
697 }
698 
~NodeContextPoolManagerGLES()699 NodeContextPoolManagerGLES::~NodeContextPoolManagerGLES()
700 {
701     if (!framebufferCache_.framebuffers.empty()) {
702         PLUGIN_ASSERT(device_.IsActive());
703         for (auto& ref : framebufferCache_.framebuffers) {
704             DeleteFbos(device_, ref);
705         }
706     }
707 }
708 
BeginFrame()709 void NodeContextPoolManagerGLES::BeginFrame()
710 {
711 #if (RENDER_VALIDATION_ENABLED == 1)
712     frameIndexFront_ = device_.GetFrameCount();
713 #endif
714 }
715 
BeginBackendFrame()716 void NodeContextPoolManagerGLES::BeginBackendFrame()
717 {
718     const uint64_t frameCount = device_.GetFrameCount();
719 
720 #if (RENDER_VALIDATION_ENABLED == 1)
721     PLUGIN_ASSERT(frameIndexBack_ != frameCount); // prevent multiple calls per frame
722     frameIndexBack_ = frameCount;
723     PLUGIN_ASSERT(frameIndexFront_ == frameIndexBack_);
724 #endif
725 
726     // not used
727     bufferingIndex_ = 0;
728 
729     const auto maxAge = 2;
730     const auto minAge = device_.GetCommandBufferingCount() + maxAge;
731     const auto ageLimit = (frameCount < minAge) ? 0 : (frameCount - minAge);
732     const size_t limit = framebufferCache_.frameBufferFrameUseIndex.size();
733     for (size_t index = 0; index < limit; ++index) {
734         auto const useIndex = framebufferCache_.frameBufferFrameUseIndex[index];
735         auto& ref = framebufferCache_.framebuffers[index];
736         if (useIndex < ageLimit && (!ref.fbos.empty())) {
737             DeleteFbos(device_, ref);
738             auto const pos = std::find_if(framebufferCache_.renderPassHashToIndex.begin(),
739                 framebufferCache_.renderPassHashToIndex.end(),
740                 [index](auto const& hashToIndex) { return hashToIndex.second == index; });
741             if (pos != framebufferCache_.renderPassHashToIndex.end()) {
742                 framebufferCache_.renderPassHashToIndex.erase(pos);
743             }
744         }
745     }
746 }
747 
GetFramebufferHandle(const RenderCommandBeginRenderPass & beginRenderPass)748 EngineResourceHandle NodeContextPoolManagerGLES::GetFramebufferHandle(
749     const RenderCommandBeginRenderPass& beginRenderPass)
750 {
751     PLUGIN_ASSERT(device_.IsActive());
752     const uint64_t rpHash = HashRPD(beginRenderPass, gpuResourceMgr_);
753     if (const auto iter = framebufferCache_.renderPassHashToIndex.find(rpHash);
754         iter != framebufferCache_.renderPassHashToIndex.cend()) {
755         PLUGIN_ASSERT(iter->second < framebufferCache_.framebuffers.size());
756         // store frame index for usage
757         framebufferCache_.frameBufferFrameUseIndex[iter->second] = device_.GetFrameCount();
758         return RenderHandleUtil::CreateEngineResourceHandle(RenderHandleType::UNDEFINED, iter->second, 0);
759     }
760 
761     BindImage images[PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT];
762     if (!UpdateBindImages(beginRenderPass, images, gpuResourceMgr_)) {
763         return {};
764     }
765 
766     // Create fbo for each subpass
767     LowlevelFramebufferGL fb;
768 
769     const auto& rpd = beginRenderPass.renderPassDesc;
770     fb.fbos.resize(rpd.subpassCount);
771     if constexpr (VERBOSE_LOGGING) {
772         PLUGIN_LOG_V("Creating framebuffer with %u subpasses", rpd.subpassCount);
773     }
774     // NOTE: we currently expect that resolve, color and depth attachments are regular 2d textures (or
775     // renderbuffers)
776     vector<FboHash> fboMap;
777     std::transform(std::begin(beginRenderPass.subpasses), std::begin(beginRenderPass.subpasses) + rpd.subpassCount,
778         fb.fbos.data(),
779         [this, &fb, &fboMap, images = array_view<const BindImage>(images),
780             imageMap = array_view<const uint32_t>(imageMap_)](const RenderPassSubpassDesc& subpass) {
781             return ProcessSubPass(device_, fb, fboMap, images, imageMap, subpass,
782                 subpass.viewMask ? multiViewMultisampledRenderToTexture_ : multisampledRenderToTexture_);
783         });
784     if constexpr (VERBOSE_LOGGING) {
785         PLUGIN_LOG_V("Created framebuffer with %u subpasses at size [%u %u]", rpd.subpassCount, fb.width, fb.height);
786     }
787     if (!fb.width || !fb.height) {
788         return {};
789     }
790     uint32_t arrayIndex = 0;
791     if (auto const pos = std::find_if(framebufferCache_.framebuffers.begin(), framebufferCache_.framebuffers.end(),
792                                       [](auto const &framebuffer) { return framebuffer.fbos.empty(); });
793         pos != framebufferCache_.framebuffers.end()) {
794         arrayIndex = (uint32_t)std::distance(framebufferCache_.framebuffers.begin(), pos);
795         *pos = move(fb);
796         framebufferCache_.frameBufferFrameUseIndex[arrayIndex] = device_.GetFrameCount();
797     } else {
798         framebufferCache_.framebuffers.push_back(move(fb));
799         framebufferCache_.frameBufferFrameUseIndex.push_back(device_.GetFrameCount());
800         arrayIndex = (uint32_t)framebufferCache_.framebuffers.size() - 1;
801     }
802     framebufferCache_.renderPassHashToIndex[rpHash] = arrayIndex;
803     return RenderHandleUtil::CreateEngineResourceHandle(RenderHandleType::UNDEFINED, arrayIndex, 0);
804 }
805 
GetFramebuffer(const EngineResourceHandle handle) const806 const LowlevelFramebufferGL* NodeContextPoolManagerGLES::GetFramebuffer(const EngineResourceHandle handle) const
807 {
808     if (RenderHandleUtil::IsValid(handle)) {
809         if (const uint32_t index = RenderHandleUtil::GetIndexPart(handle);
810             (index < framebufferCache_.framebuffers.size())) {
811             return &framebufferCache_.framebuffers[index];
812         }
813     }
814     return nullptr;
815 }
816 
FilterRenderPass(RenderCommandBeginRenderPass & beginRenderPass)817 void NodeContextPoolManagerGLES::FilterRenderPass(RenderCommandBeginRenderPass& beginRenderPass)
818 {
819 #if RENDER_HAS_GLES_BACKEND
820     if (!multisampledRenderToTexture_) {
821         return;
822     }
823     if (!multiViewMultisampledRenderToTexture_ &&
824         std::any_of(beginRenderPass.subpasses.cbegin(), beginRenderPass.subpasses.cend(),
825             [](const RenderPassSubpassDesc& subpass) { return subpass.viewMask != 0; })) {
826         return;
827     }
828     imageMap_.clear();
829     imageMap_.insert(
830         imageMap_.end(), static_cast<size_t>(beginRenderPass.renderPassDesc.attachmentCount), EMPTY_ATTACHMENT);
831     auto begin = beginRenderPass.subpasses.begin();
832     auto end = beginRenderPass.subpasses.end();
833     auto pos = std::find_if(begin, end, [](const RenderPassSubpassDesc& subpass) {
834         return (subpass.resolveAttachmentCount > 0) ||
835                ((subpass.depthResolveAttachmentCount > 0) &&
836                    (subpass.depthResolveModeFlags || subpass.stencilResolveModeFlags));
837     });
838     if (pos != end) {
839         BindImage images[PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT];
840         UpdateBindImages(beginRenderPass, images, gpuResourceMgr_);
841         if (!IsDefaultResolve(images, *pos)) {
842             // build a mapping which attachments can be skipped and render directly to the resolve
843             MapColorAttachments(begin, pos, images, imageMap_, beginRenderPass.renderPassDesc.attachments);
844             if (device_.IsDepthResolveSupported()) {
845                 MapDepthAttachments(begin, pos, images, imageMap_, beginRenderPass.renderPassDesc.attachments);
846             }
847             UpdateSubpassAttachments(begin, pos + 1, imageMap_);
848 
849             // flip the mapping from attachment -> resolve to resolve -> attachment. this will be used when selecting
850             // what images are bound as attachments at the begining of the renderpass.
851             vector<uint32_t> map(imageMap_.size(), EMPTY_ATTACHMENT);
852             for (uint32_t color = 0U, last = static_cast<uint32_t>(imageMap_.size()); color < last; ++color) {
853                 const auto resolve = imageMap_[color];
854                 if (resolve != EMPTY_ATTACHMENT) {
855                     map[resolve] = color;
856                 }
857             }
858             imageMap_ = map;
859         }
860     }
861 #endif
862 }
863 RENDER_END_NAMESPACE()
864