1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
22 #include "lib/code_annotation.h"
23 #include "lib/config2.h"
24 #include "renderer/backend/gl/Device.h"
25 #include "renderer/backend/gl/DeviceCommandContext.h"
26 #include "renderer/backend/gl/Mapping.h"
42 GLint
CalculateMinFilter(const Sampler::Desc
& defaultSamplerDesc
, const uint32_t MIPLevelCount
)
44 if (MIPLevelCount
== 1)
45 return defaultSamplerDesc
.minFilter
== Sampler::Filter::LINEAR
? GL_LINEAR
: GL_NEAREST
;
47 if (defaultSamplerDesc
.minFilter
== Sampler::Filter::LINEAR
)
48 return defaultSamplerDesc
.mipFilter
== Sampler::Filter::LINEAR
? GL_LINEAR_MIPMAP_LINEAR
: GL_LINEAR_MIPMAP_NEAREST
;
50 return defaultSamplerDesc
.mipFilter
== Sampler::Filter::LINEAR
? GL_NEAREST_MIPMAP_LINEAR
: GL_NEAREST_MIPMAP_NEAREST
;
53 GLint
AddressModeToGLEnum(Sampler::AddressMode addressMode
)
57 case Sampler::AddressMode::REPEAT
: return GL_REPEAT
;
58 case Sampler::AddressMode::MIRRORED_REPEAT
: return GL_MIRRORED_REPEAT
;
59 case Sampler::AddressMode::CLAMP_TO_EDGE
: return GL_CLAMP_TO_EDGE
;
60 case Sampler::AddressMode::CLAMP_TO_BORDER
: return GL_CLAMP_TO_BORDER
;
65 GLenum
TypeToGLEnum(CTexture::Type type
)
67 GLenum target
= GL_TEXTURE_2D
;
70 case CTexture::Type::TEXTURE_2D
:
71 target
= GL_TEXTURE_2D
;
73 case CTexture::Type::TEXTURE_2D_MULTISAMPLE
:
75 ENSURE(false && "Multisample textures are unsupported on GLES");
77 target
= GL_TEXTURE_2D_MULTISAMPLE
;
80 case CTexture::Type::TEXTURE_CUBE
:
81 target
= GL_TEXTURE_CUBE_MAP
;
87 } // anonymous namespace
90 std::unique_ptr
<CTexture
> CTexture::Create(
91 CDevice
* device
, const char* name
, const Type type
, const uint32_t usage
,
92 const Format format
, const uint32_t width
, const uint32_t height
,
93 const Sampler::Desc
& defaultSamplerDesc
, const uint32_t MIPLevelCount
, const uint32_t sampleCount
)
95 std::unique_ptr
<CTexture
> texture(new CTexture());
97 ENSURE(format
!= Format::UNDEFINED
);
98 ENSURE(width
> 0 && height
> 0 && MIPLevelCount
> 0);
99 ENSURE((type
== Type::TEXTURE_2D_MULTISAMPLE
&& sampleCount
> 1 && !(usage
& ITexture::Usage::SAMPLED
)) || sampleCount
== 1);
101 texture
->m_Device
= device
;
102 texture
->m_Type
= type
;
103 texture
->m_Usage
= usage
;
104 texture
->m_Format
= format
;
105 texture
->m_Width
= width
;
106 texture
->m_Height
= height
;
107 texture
->m_MIPLevelCount
= MIPLevelCount
;
109 glGenTextures(1, &texture
->m_Handle
);
113 const GLenum target
= TypeToGLEnum(type
);
115 CDeviceCommandContext::ScopedBind
scopedBind(
116 texture
->m_Device
->GetActiveCommandContext(), target
, texture
->m_Handle
);
118 // It's forbidden to set sampler state for multisample textures.
119 if (type
!= Type::TEXTURE_2D_MULTISAMPLE
)
121 glTexParameteri(target
, GL_TEXTURE_MIN_FILTER
, CalculateMinFilter(defaultSamplerDesc
, MIPLevelCount
));
122 glTexParameteri(target
, GL_TEXTURE_MAG_FILTER
, defaultSamplerDesc
.magFilter
== Sampler::Filter::LINEAR
? GL_LINEAR
: GL_NEAREST
);
126 glTexParameteri(target
, GL_TEXTURE_WRAP_S
, AddressModeToGLEnum(defaultSamplerDesc
.addressModeU
));
127 glTexParameteri(target
, GL_TEXTURE_WRAP_T
, AddressModeToGLEnum(defaultSamplerDesc
.addressModeV
));
131 if (type
== Type::TEXTURE_CUBE
)
132 glTexParameteri(target
, GL_TEXTURE_WRAP_R
, AddressModeToGLEnum(defaultSamplerDesc
.addressModeW
));
138 glTexParameteri(target
, GL_TEXTURE_BASE_LEVEL
, 0);
139 glTexParameteri(target
, GL_TEXTURE_MAX_LEVEL
, MIPLevelCount
- 1);
141 if (defaultSamplerDesc
.mipLODBias
!= 0.0f
)
142 glTexParameteri(target
, GL_TEXTURE_LOD_BIAS
, defaultSamplerDesc
.mipLODBias
);
143 #endif // !CONFIG2_GLES
145 if (type
== Type::TEXTURE_2D
&& defaultSamplerDesc
.anisotropyEnabled
)
147 ENSURE(texture
->m_Device
->GetCapabilities().anisotropicFiltering
);
148 const float maxAnisotropy
= std::min(
149 defaultSamplerDesc
.maxAnisotropy
, texture
->m_Device
->GetCapabilities().maxAnisotropy
);
150 glTexParameterf(target
, GL_TEXTURE_MAX_ANISOTROPY_EXT
, maxAnisotropy
);
153 if (defaultSamplerDesc
.addressModeU
== Sampler::AddressMode::CLAMP_TO_BORDER
||
154 defaultSamplerDesc
.addressModeV
== Sampler::AddressMode::CLAMP_TO_BORDER
||
155 defaultSamplerDesc
.addressModeW
== Sampler::AddressMode::CLAMP_TO_BORDER
)
157 CColor
borderColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);
158 switch (defaultSamplerDesc
.borderColor
)
160 case Sampler::BorderColor::TRANSPARENT_BLACK
:
162 case Sampler::BorderColor::OPAQUE_BLACK
:
163 borderColor
= CColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
165 case Sampler::BorderColor::OPAQUE_WHITE
:
166 borderColor
= CColor(1.0f
, 1.0f
, 1.0f
, 1.0f
);
169 glTexParameterfv(target
, GL_TEXTURE_BORDER_COLOR
, borderColor
.AsFloatArray().data());
174 if (type
== CTexture::Type::TEXTURE_2D
)
176 bool compressedFormat
= false;
177 GLint internalFormat
= GL_RGBA
;
178 // Actually pixel data is nullptr so it doesn't make sense to account
179 // it, but in theory some buggy drivers might complain about invalid
181 GLenum pixelFormat
= GL_RGBA
;
182 GLenum pixelType
= GL_UNSIGNED_BYTE
;
185 case Format::UNDEFINED
:
186 debug_warn("Texture should defined format");
188 case Format::R8G8B8A8_UNORM
:
190 case Format::R8G8B8_UNORM
:
191 internalFormat
= GL_RGB
;
192 pixelFormat
= GL_RGB
;
193 pixelType
= GL_UNSIGNED_BYTE
;
196 case Format::R8_UNORM
:
197 internalFormat
= GL_RED
;
198 pixelFormat
= GL_RED
;
199 pixelType
= GL_UNSIGNED_BYTE
;
202 case Format::A8_UNORM
:
203 internalFormat
= GL_ALPHA
;
204 pixelFormat
= GL_ALPHA
;
205 pixelType
= GL_UNSIGNED_BYTE
;
207 case Format::L8_UNORM
:
208 internalFormat
= GL_LUMINANCE
;
209 pixelFormat
= GL_LUMINANCE
;
210 pixelType
= GL_UNSIGNED_BYTE
;
213 // GLES requires pixel type == UNSIGNED_SHORT or UNSIGNED_INT for depth.
214 case Format::D16_UNORM
: FALLTHROUGH
;
215 case Format::D24_UNORM
: FALLTHROUGH
;
216 case Format::D32_SFLOAT
:
217 internalFormat
= GL_DEPTH_COMPONENT
;
218 pixelFormat
= GL_DEPTH_COMPONENT
;
219 pixelType
= GL_UNSIGNED_SHORT
;
221 case Format::D24_UNORM_S8_UINT
:
222 debug_warn("Unsupported format");
225 case Format::D16_UNORM
:
226 internalFormat
= GL_DEPTH_COMPONENT16
;
227 pixelFormat
= GL_DEPTH_COMPONENT
;
228 pixelType
= GL_UNSIGNED_SHORT
;
230 case Format::D24_UNORM
:
231 internalFormat
= GL_DEPTH_COMPONENT24
;
232 pixelFormat
= GL_DEPTH_COMPONENT
;
233 pixelType
= GL_UNSIGNED_SHORT
;
235 case Format::D32_SFLOAT
:
236 internalFormat
= GL_DEPTH_COMPONENT32
;
237 pixelFormat
= GL_DEPTH_COMPONENT
;
238 pixelType
= GL_UNSIGNED_SHORT
;
240 case Format::D24_UNORM_S8_UINT
:
241 internalFormat
= GL_DEPTH24_STENCIL8_EXT
;
242 pixelFormat
= GL_DEPTH_STENCIL_EXT
;
243 pixelType
= GL_UNSIGNED_INT_24_8_EXT
;
246 case Format::BC1_RGB_UNORM
: FALLTHROUGH
;
247 case Format::BC1_RGBA_UNORM
: FALLTHROUGH
;
248 case Format::BC2_UNORM
: FALLTHROUGH
;
249 case Format::BC3_UNORM
:
250 compressedFormat
= true;
253 debug_warn("Unsupported format.");
255 // glCompressedTexImage2D can't accept a null data, so we will initialize it during uploading.
256 if (!compressedFormat
)
258 for (uint32_t level
= 0; level
< MIPLevelCount
; ++level
)
260 glTexImage2D(target
, level
, internalFormat
,
261 std::max(1u, width
>> level
), std::max(1u, height
>> level
),
262 0, pixelFormat
, pixelType
, nullptr);
266 else if (type
== CTexture::Type::TEXTURE_2D_MULTISAMPLE
)
268 ENSURE(MIPLevelCount
== 1);
270 if (format
== Format::R8G8B8A8_UNORM
)
272 glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE
, sampleCount
, GL_RGBA8
, width
, height
, GL_TRUE
);
274 else if (format
== Format::D24_UNORM_S8_UINT
)
276 glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE
, sampleCount
, GL_DEPTH24_STENCIL8_EXT
, width
, height
, GL_TRUE
);
279 #endif // !CONFIG2_GLES
281 debug_warn("Unsupported format");
287 if (IsDepthFormat(format
))
289 if (defaultSamplerDesc
.compareEnabled
)
291 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_MODE
, GL_COMPARE_R_TO_TEXTURE
);
293 GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_FUNC
,
294 Mapping::FromCompareOp(defaultSamplerDesc
.compareOp
));
297 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_MODE
, GL_NONE
);
303 if (texture
->m_Device
->GetCapabilities().debugLabels
)
305 glObjectLabel(GL_TEXTURE
, texture
->m_Handle
, -1, name
);
311 CTexture::CTexture() = default;
313 CTexture::~CTexture()
315 m_Device
->GetActiveCommandContext()->OnTextureDestroy(this);
317 glDeleteTextures(1, &m_Handle
);
320 IDevice
* CTexture::GetDevice()
327 } // namespace Backend
329 } // namespace Renderer