1 /*
2  * Copyright (c) 2024 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 "gles/swapchain_gles.h"
17 
18 #include <base/math/vector.h>
19 #include <core/intf_engine.h>
20 #include <render/namespace.h>
21 
22 #include "gles/device_gles.h"
23 #include "gles/gl_functions.h"
24 #include "gles/gpu_image_gles.h"
25 #include "gles/surface_information.h"
26 #include "util/log.h"
27 
28 using namespace BASE_NS;
29 
30 // NOTE: think more about the initialization. (context creation etc..)
31 RENDER_BEGIN_NAMESPACE()
32 namespace {
33 struct FormatInfo {
34     Format format;
35     uint32_t r, g, b, a;
36     GLenum glFormat;  // texture
37     GLenum glFormat2; // renderable
38 };
39 
40 // NOTE: add more mappings if needed.
41 static constexpr FormatInfo FORMATS[] = { { BASE_FORMAT_R8G8B8_UNORM, 8, 8, 8, 0, GL_RGB8, GL_RGB8 },
42     { BASE_FORMAT_R8G8B8A8_UNORM, 8, 8, 8, 8, GL_RGBA8, GL_RGBA8 },
43 #if RENDER_HAS_GL_BACKEND
44     { BASE_FORMAT_R16G16B16_UNORM, 16, 16, 16, 0, GL_RGB16, GL_RGB16 },
45     { BASE_FORMAT_R16G16B16A16_UNORM, 16, 16, 16, 16, GL_RGBA16, GL_RGBA16 },
46 #endif
47     { BASE_FORMAT_UNDEFINED, 0, 0, 0, 0, GL_NONE, GL_NONE } };
48 
49 static constexpr FormatInfo FORMATS_SRGB[] = { { BASE_FORMAT_R8G8B8_SRGB, 8, 8, 8, 0, GL_SRGB8, GL_SRGB8_ALPHA8 },
50     { BASE_FORMAT_R8G8B8A8_SRGB, 8, 8, 8, 8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8 },
51     { BASE_FORMAT_UNDEFINED, 0, 0, 0, 0, GL_NONE, GL_NONE } };
52 
SamplesToSampleCountFlags(uint32_t samples)53 SampleCountFlags SamplesToSampleCountFlags(uint32_t samples)
54 {
55     SampleCountFlags sampleCount = 0;
56     const SampleCountFlagBits bits[] = { CORE_SAMPLE_COUNT_1_BIT, CORE_SAMPLE_COUNT_2_BIT, CORE_SAMPLE_COUNT_4_BIT,
57         CORE_SAMPLE_COUNT_8_BIT, CORE_SAMPLE_COUNT_16_BIT, CORE_SAMPLE_COUNT_32_BIT, CORE_SAMPLE_COUNT_64_BIT };
58     for (int id = 0;; id++) {
59         if (bits[id] == 0) {
60             break;
61         }
62         if (samples == 0) {
63             break;
64         }
65         if (samples & 1) {
66             sampleCount |= (SampleCountFlags)bits[id];
67         }
68         samples >>= 1;
69     }
70     if (sampleCount == 0) {
71         sampleCount = CORE_SAMPLE_COUNT_1_BIT;
72     }
73     return sampleCount;
74 }
GetDepthFormat(uint32_t depthSize,uint32_t stencilSize)75 Format GetDepthFormat(uint32_t depthSize, uint32_t stencilSize)
76 {
77     auto depthFormat = BASE_FORMAT_UNDEFINED;
78     // This mapping might not be exact.
79     if (stencilSize == 0) {
80         if (depthSize == 16) {
81             depthFormat = BASE_FORMAT_D16_UNORM;
82         } else if (depthSize == 24) {
83             depthFormat = BASE_FORMAT_X8_D24_UNORM_PACK32;
84         } else if (depthSize == 32) {
85             depthFormat = BASE_FORMAT_D32_SFLOAT;
86         }
87     } else {
88         if (depthSize == 16) {
89             PLUGIN_LOG_E("unsupported depth stencil format D16_UNORM_S8_UINT fallback to D24_UNORM_S8_UINT");
90             depthFormat = BASE_FORMAT_D24_UNORM_S8_UINT;
91         } else if (depthSize == 24) {
92             depthFormat = BASE_FORMAT_D24_UNORM_S8_UINT;
93         } else if (depthSize == 32) {
94             PLUGIN_LOG_E("unsupported depth stencil format D32_SFLOAT_S8_UINT fallback to D24_UNORM_S8_UINT");
95             depthFormat = BASE_FORMAT_D24_UNORM_S8_UINT;
96         }
97     }
98     return depthFormat;
99 }
100 
RgbToFormat(uint32_t r,uint32_t g,uint32_t b,uint32_t a,bool srgb)101 Format RgbToFormat(uint32_t r, uint32_t g, uint32_t b, uint32_t a, bool srgb)
102 {
103     const FormatInfo* format = nullptr;
104     if (!srgb) {
105         format = FORMATS;
106     } else {
107         format = FORMATS_SRGB;
108     }
109     for (int i = 0;; i++) {
110         if (format[i].format == BASE_FORMAT_UNDEFINED) {
111             // no match.
112             break;
113         }
114         if ((format[i].r == r) && (format[i].g == g) && (format[i].b == b) && (format[i].a == a)) {
115             return format[i].format;
116         }
117     }
118     PLUGIN_LOG_E("Unsupported swapchain color channels");
119     return BASE_FORMAT_UNDEFINED;
120 }
121 
122 #if RENDER_GL_FLIP_Y_SWAPCHAIN
FormatToGlFormat(Format colorFormat)123 GLenum FormatToGlFormat(Format colorFormat)
124 {
125     const FormatInfo* steps[] = { FORMATS, FORMATS_SRGB, nullptr };
126     for (int step = 0;; step++) {
127         const FormatInfo* format = steps[step];
128         if (steps[step] == nullptr) {
129             // no match in any lists..
130             break;
131         }
132 
133         for (int i = 0;; i++) {
134             if (format[i].format == BASE_FORMAT_MAX_ENUM) {
135                 // no match.
136                 break;
137             }
138             if (format[i].format == colorFormat) {
139                 return format[i].glFormat2;
140             }
141         }
142     }
143     PLUGIN_LOG_E("Unsupported swapchain format");
144     return GL_NONE;
145 }
146 #endif
ExtractInfo(DeviceGLES & device,const uint64_t surfaceHandle)147 GlesImplementation::SurfaceInfo ExtractInfo(DeviceGLES& device, const uint64_t surfaceHandle) noexcept
148 {
149     GlesImplementation::SurfaceInfo info;
150     const auto& EGLState = device.GetEglState();
151 #if RENDER_HAS_GLES_BACKEND
152     if (device.GetBackendType() == DeviceBackendType::OPENGLES) {
153         const auto& devicePlatformData = (const DevicePlatformDataGLES&)device.GetPlatformData();
154         EGLSurface surface = (EGLSurface)surfaceHandle;
155         if (!EGLState.GetSurfaceInformation(devicePlatformData, surface, info)) {
156             PLUGIN_LOG_E("Could not query surface information");
157         }
158         if (!surface) {
159             PLUGIN_LOG_E("Surface is null");
160         }
161     }
162 #endif
163 #if RENDER_HAS_GL_BACKEND
164     if (device.GetBackendType() == DeviceBackendType::OPENGL) {
165 #if _WIN32
166         HDC surface = (HDC)surfaceHandle;
167         if (!EGLState.GetSurfaceInformation(surface, info)) {
168             PLUGIN_LOG_E("Could not query surface information");
169         }
170         if (!surface) {
171             PLUGIN_LOG_E("Surface is null");
172         }
173 #else
174 #error Core::DeviceBackendType::OPENGL not implemented for this platform yet.
175 #endif
176     }
177 #endif
178     return info;
179 }
180 
GenerateDescriptor(bool depth,Format format,uint32_t width,uint32_t height,uint32_t samples)181 GpuImageDesc GenerateDescriptor(bool depth, Format format, uint32_t width, uint32_t height, uint32_t samples) noexcept
182 {
183     GpuImageDesc res = {
184         ImageType::CORE_IMAGE_TYPE_2D,                                            // imageType
185         ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,                                   // imageViewType
186         format,                                                                   // format
187         ImageTiling::CORE_IMAGE_TILING_OPTIMAL,                                   // imageTiling
188         0,                                                                        // usageFlags
189         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,            // memoryPropertyFlags
190         0,                                                                        // createFlags
191         EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, // engineCreationFlags
192         width,                                                                    // width
193         height,                                                                   // height
194         1,                                                                        // depth
195         1,                                                                        // mipCount
196         1,                                                                        // layerCount
197         SamplesToSampleCountFlags(samples),                                       // sampleCountFlags
198         {}                                                                        // componentMapping
199     };
200     if (depth) {
201         res.usageFlags = ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
202                          ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
203                          ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
204         res.memoryPropertyFlags |= MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
205     } else {
206         res.usageFlags = ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
207                          ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
208         res.engineCreationFlags |= EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_RESET_STATE_ON_FRAME_BORDERS;
209     }
210     return res;
211 }
212 #if RENDER_GL_FLIP_Y_SWAPCHAIN
GenerateTextures(DeviceGLES & device,uint32_t count,GLenum colorf,uint32_t sampleCount,SwapchainPlatformDataGL & plat)213 void GenerateTextures(
214     DeviceGLES& device, uint32_t count, GLenum colorf, uint32_t sampleCount, SwapchainPlatformDataGL& plat)
215 {
216     constexpr const uint32_t TEMP_BIND_UNIT = 15; // Use texture unit 15 as a temporary bind spot.
217     uint32_t binding;
218     if (sampleCount > 1) {
219         binding = device.BoundTexture(TEMP_BIND_UNIT, GL_TEXTURE_2D_MULTISAMPLE);
220     } else {
221         binding = device.BoundTexture(TEMP_BIND_UNIT, GL_TEXTURE_2D);
222     }
223     Math::UVec2 size { plat.swapchainImages.width, plat.swapchainImages.height };
224     plat.swapchainImages.images.resize(1); // Supports multiple images, see if it helps performance.
225 
226     glGenTextures(static_cast<GLsizei>(plat.swapchainImages.images.size()), plat.swapchainImages.images.data());
227     for (size_t i = 0; i < plat.swapchainImages.images.size(); i++) {
228         GLuint tid = plat.swapchainImages.images[i];
229         if (sampleCount > 1) {
230             device.TexStorage2DMultisample(tid, GL_TEXTURE_2D_MULTISAMPLE, sampleCount, colorf, size, false);
231         } else {
232             device.TexStorage2D(tid, GL_TEXTURE_2D, 1, colorf, size);
233         }
234         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
235         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
236         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
237         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
238     }
239     // Currently missing pseudo depth buffer for "CORE_DEFAULT_BACKBUFFER_DEPTH"
240     if (sampleCount > 1) {
241         device.BindTexture(TEMP_BIND_UNIT, GL_TEXTURE_2D_MULTISAMPLE, binding);
242     } else {
243         device.BindTexture(TEMP_BIND_UNIT, GL_TEXTURE_2D, binding);
244     }
245 }
GenerateFBO(DeviceGLES & device,SwapchainPlatformDataGL & plat,bool msaa)246 void GenerateFBO(DeviceGLES& device, SwapchainPlatformDataGL& plat, bool msaa)
247 {
248     // create fbos to be used during present (blit from this fbo to backbuffer)
249     plat.fbos.resize(plat.swapchainImages.images.size());
250     glGenFramebuffers(static_cast<GLsizei>(plat.fbos.size()), plat.fbos.data());
251     for (size_t i = 0; i < plat.fbos.size(); i++) {
252         device.BindFrameBuffer(plat.fbos[i]);
253         GLenum texType;
254         if (msaa) {
255             texType = GL_TEXTURE_2D_MULTISAMPLE;
256         } else {
257             texType = GL_TEXTURE_2D;
258         }
259         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texType, plat.swapchainImages.images[i], 0);
260         GLenum status;
261         status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
262         if (status != GL_FRAMEBUFFER_COMPLETE) {
263             PLUGIN_LOG_E("Framebuffer incomplete (status: %x)", status);
264         }
265     }
266     device.BindFrameBuffer(0);
267 }
268 #endif
269 } // namespace
270 
SwapchainGLES(Device & device,const SwapchainCreateInfo & swapchainCreateInfo)271 SwapchainGLES::SwapchainGLES(Device& device, const SwapchainCreateInfo& swapchainCreateInfo)
272     : device_((DeviceGLES&)device), flags_(swapchainCreateInfo.swapchainFlags)
273 {
274     // check for surface creation automatically
275     if ((swapchainCreateInfo.surfaceHandle == 0) && swapchainCreateInfo.window.window) {
276         plat_.surface =
277             device_.GetEglState().CreateSurface(swapchainCreateInfo.window.window, swapchainCreateInfo.window.instance);
278         ownsSurface_ = true;
279     } else {
280         plat_.surface = (uintptr_t)swapchainCreateInfo.surfaceHandle;
281     }
282 
283     // Fetch information about the backbuffer.
284     // Create pseudo handles (or actual handles, depending if direct backbuffer rendering is enabled or not)
285     GlesImplementation::SurfaceInfo info = ExtractInfo(device_, plat_.surface);
286     const Format colorFormat = RgbToFormat(info.red_size, info.green_size, info.blue_size, info.alpha_size, info.srgb);
287     PLUGIN_LOG_I("Input surface for swapchain is [%x] %dx%d R:%d G:%d B:%d A:%d D:%d S:%d samples:%d srgb:%s",
288         info.configId, info.width, info.height, info.red_size, info.green_size, info.blue_size, info.alpha_size,
289         info.depth_size, info.stencil_size, info.samples, info.srgb ? "true" : "false");
290     info.width = Math::max(info.width, 1u);
291     info.height = Math::max(info.height, 1u);
292     plat_.swapchainImageIndex = 0;
293     plat_.vsync = (flags_ & SwapchainFlagBits::CORE_SWAPCHAIN_VSYNC_BIT);
294     plat_.swapchainImages.width = info.width;
295     plat_.swapchainImages.height = info.height;
296 #if RENDER_GL_FLIP_Y_SWAPCHAIN
297     // Create "swapchain" images to be used.
298     // These are actual textures to be used during rendering. these will be blitted/flipped to backbuffer during
299     // present.
300     GenerateTextures(device_, 1, FormatToGlFormat(colorFormat), info.samples, plat_);
301     GenerateFBO(device_, plat_, (info.samples > 1));
302 #else
303     // Initialize one "swapchain" texture with GL name '0'
304     // It will be identified based on that in nodecontextpoolmanager which creates (or in this case just uses default)
305     // FBO.
306     plat_.swapchainImages.images.resize(1);
307 #endif
308     desc_ = GenerateDescriptor(false, colorFormat, info.width, info.height, info.samples);
309     if (flags_ & SwapchainFlagBits::CORE_SWAPCHAIN_DEPTH_BUFFER_BIT) {
310         if (info.depth_size == 0) {
311             PLUGIN_LOG_V("Default backbuffer has no depth, but depth requested!");
312         } else {
313             const auto depthFormat = GetDepthFormat(info.depth_size, info.stencil_size);
314             plat_.depthPlatformData = GpuImageGLES::GetPlatformData(device_, depthFormat);
315             descDepthBuffer_ = GenerateDescriptor(true, depthFormat, info.width, info.height, info.samples);
316         }
317     }
318 }
319 
~SwapchainGLES()320 SwapchainGLES::~SwapchainGLES()
321 {
322 #if RENDER_GL_FLIP_Y_SWAPCHAIN
323     device_.Activate();
324     for (auto id : plat_.fbos) {
325         device_.DeleteFrameBuffer(id);
326     }
327     for (size_t i = 0; i < plat_.swapchainImages.images.size(); i++) {
328         device_.DeleteTexture(plat_.swapchainImages.images[i]);
329     }
330     device_.Deactivate();
331 #endif
332     if (ownsSurface_) {
333         device_.GetEglState().DestroySurface(plat_.surface);
334     }
335 }
336 
GetPlatformData() const337 const SwapchainPlatformDataGL& SwapchainGLES::GetPlatformData() const
338 {
339     return plat_;
340 }
341 
GetDesc() const342 const GpuImageDesc& SwapchainGLES::GetDesc() const
343 {
344     return desc_;
345 }
346 
GetDescDepthBuffer() const347 const GpuImageDesc& SwapchainGLES::GetDescDepthBuffer() const
348 {
349     return descDepthBuffer_;
350 }
351 
GetFlags() const352 uint32_t SwapchainGLES::GetFlags() const
353 {
354     return flags_;
355 }
356 
GetNextImage() const357 uint32_t SwapchainGLES::GetNextImage() const
358 {
359     PLUGIN_ASSERT(plat_.swapchainImages.images.size() <= 1U);
360     // NOTE: does not flip images
361     return plat_.swapchainImageIndex;
362 }
363 
GetSurfaceHandle() const364 uint64_t SwapchainGLES::GetSurfaceHandle() const
365 {
366     return static_cast<uint64_t>(plat_.surface);
367 }
368 RENDER_END_NAMESPACE()
369