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