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