Cogs.Core
TexturesGL20.cpp
1#include "TexturesGL20.h"
2
3#include "GraphicsDeviceGL20.h"
4#include "FormatsGL20.h"
5
6#include "Foundation/Logging/Logger.h"
7
8#include <algorithm>
9#include <cmath>
10
11namespace
12{
13 static Cogs::Logging::Log logger = Cogs::Logging::getLogger("TexturesGL20");
14}
15
16Cogs::TexturesGL20::~TexturesGL20()
17{
18 if (this->textures.size()) {
19 LOG_WARNING(logger, "Texture resources not empty. %zu resources left.", this->textures.size());
20 }
21}
22
23void Cogs::TexturesGL20::initialize(GraphicsDeviceGL20 * device)
24{
25 CapabilitiesGL20* capabilities = device->getCapabilities();
26
27 useTextureStorage = glTexStorage2D ? true : false;
28 useAnisotropic = capabilities->EXT_texture_filter_anisotropic ? true : false;
29
30 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
31}
32
34{
35 if ((desc.target != ResourceDimensions::Texture2DMS) && (desc.target != ResourceDimensions::Texture2DMSArray) && (desc.samples > 1)) {
36 LOG_ERROR(logger, "Cannot create a non-multisampled texture with more than one sample.");
38 }
39
40 const bool generateMips = (desc.flags & TextureFlags::GenerateMipMaps) != 0;
41 const GLenum target = OpenGL20::TextureTarget[(int)desc.target];
42 const GLenum textureFormat = OpenGL20::TextureFormats[(int)desc.format];
43 const GLenum pixelType = OpenGL20::PixelTypes[(int)desc.format];
44
45 if (target == GL_INVALID_ENUM || textureFormat == GL_INVALID_ENUM || pixelType == GL_INVALID_ENUM) return TextureHandle::InvalidHandle;
46
47 TextureGL20 texture = {};
48 texture.type = target;
49 texture.textureId = GL_INVALID_VALUE;
50 texture.width = desc.width;
51 texture.height = desc.height;
52 texture.numSamples = desc.samples;
53 texture.arraySize = desc.layers;
54 texture.format = desc.format;
55 texture.flags = desc.flags;
56 texture.hasMipmaps = generateMips || desc.levels > 1;
57 GLboolean isCompressed = OpenGL20::CompressedFormats[(int)desc.format];
58
59 if (desc.flags & TextureFlags::ExternalTexture) {
60 texture.textureId = static_cast<GLuint>(data->externalHandle);
61 return this->textures.addResource(std::move(texture));
62 }
63 glGenTextures(1, &texture.textureId);
64 glBindTexture(target, texture.textureId);
65
66 // Figure out number of mipmaps needed in texture storage if mipmap generation is desired.
67 // See: http://oss.sgi.com/projects/ogl-sample/registry/ARB/texture_non_power_of_two.txt
68 const GLuint textureLevels = generateMips ? 1 + static_cast<GLuint>(std::floor(std::log((double)std::max(desc.width, desc.height)) / std::log(2.0))) : static_cast<GLuint>(desc.levels);
69
70 if (useTextureStorage) {
71 switch(target){
72 case GL_TEXTURE_1D:
73 glTexStorage1D(target, textureLevels, textureFormat, desc.width);
74 break;
75 case GL_TEXTURE_1D_ARRAY:
76 glTexStorage2D(target, textureLevels, textureFormat, desc.width, desc.layers);
77 break;
78 case GL_TEXTURE_2D:
79 glTexStorage2D(target, textureLevels, textureFormat, desc.width, desc.height);
80 break;
81 case GL_TEXTURE_2D_ARRAY:
82 glTexStorage3D(target, textureLevels, textureFormat, desc.width, desc.height, desc.layers);
83 break;
84#ifndef __APPLE__
85 case GL_TEXTURE_2D_MULTISAMPLE:
86 glTexStorage2DMultisample(target, desc.samples, textureFormat, desc.width, desc.height, GLboolean(GL_TRUE));
87 break;
88 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
89 glTexStorage3DMultisample(target, desc.samples, textureFormat, desc.width, desc.height, desc.layers, GLboolean(GL_TRUE));
90 break;
91#endif
92 case GL_TEXTURE_3D:
93 glTexStorage3D(target, textureLevels, textureFormat, desc.width, desc.height, desc.depth);
94 break;
95 case GL_TEXTURE_CUBE_MAP:
96 if (isCompressed) {
97 for (unsigned f = 0; f < 6; f++) {
98 glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, textureFormat, desc.width, desc.height, 0, GLsizei(data->getLevelSize(0)), data ? data->getData(0, f, 0) : nullptr);
99 }
100 }
101 else {
102 glTexStorage2D(target, textureLevels, textureFormat, desc.width, desc.height);
103 }
104 break;
105 case GL_TEXTURE_CUBE_MAP_ARRAY:
106 glTexStorage3D(target, textureLevels, textureFormat, desc.width, desc.height, desc.layers * 6);
107 break;
108 case GL_INVALID_ENUM:
109 break;
110 }
111 }
112 else{
113 assert(false); // TODO
114 // glTexImage2D
115 // glTexImage3D
116 // glTexImage2DMultisample
117 // glTexImage3DMultisample
118 }
119 if (texture.hasMipmaps && isCompressed && data) {
120 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, GLint(data->levels - 1));
121 }
122
123
124#if defined(OSOMP_EnableProfiler)
125 OsoMPAttribute profilerAttributes[7] = {
126 { COGSMEM_WidthAttribute, ATTRIBUTETYPE_Integer32, static_cast<int32_t>(desc.width) },
127 { COGSMEM_HeightAttribute, ATTRIBUTETYPE_Integer32, static_cast<int32_t>(desc.height) },
128 { COGSMEM_DepthAttribute, ATTRIBUTETYPE_Integer32, static_cast<int32_t>(desc.depth) },
129 { COGSMEM_LayersAttribute, ATTRIBUTETYPE_Integer32, static_cast<int32_t>(desc.layers) },
130 { COGSMEM_FacesAttribute, ATTRIBUTETYPE_Integer32, static_cast<int32_t>(desc.faces) },
131 { COGSMEM_LevelsAttribute, ATTRIBUTETYPE_Integer32, static_cast<int32_t>(desc.levels) },
132 { COGSMEM_FormatAttribute, ATTRIBUTETYPE_String },
133 };
134
135 profilerAttributes[6].mData.mText = getFormatInfo(desc.format)->name;
136 RegisterGPUResource(glTextureFakeAddress(texture.textureId), desc.estimateMemorySize(), MemBlockType::GPUTexture, profilerAttributes, 7);
137#endif
138
139 glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
140 if (!texture.hasMipmaps) {
141 // If no mipmaps are defined, ensure the texture is considered complete with only one
142 // level specified (#0) even if mipmap filtering is applied.
143 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 0);
144 }
145
146 if(data) uploadTextureData(texture, *data, 0, 0, 0);
147
148 glBindTexture(target, 0);
149
150 return this->textures.addResource(std::move(texture));
151}
152
154{
155 TextureGL20 & texture = this->textures[textureHandle];
156
157#if defined(OSOMP_EnableProfiler)
158 UnregisterGPUResource(glTextureFakeAddress(texture.textureId));
159#endif
160
161 glDeleteTextures(1, &texture.textureId);
162 this->textures.removeResource(textureHandle);
163}
164
165void Cogs::TexturesGL20::uploadTextureData(TextureGL20 &texture,
166 const TextureData &data,
167 uint32_t layer_offset,
168 uint32_t face_offset,
169 uint32_t level_offset)
170{
171 assert(face_offset == 0 && "face_offset not handled");
172 assert(level_offset == 0 && "level_offset not handled");
173
174 const bool isCompressed = OpenGL20::CompressedFormats[(int)texture.format];
175 const GLenum target = texture.type;
176 const GLenum textureFormat = OpenGL20::TextureFormats[(int)texture.format];
177 const GLenum pixelType = OpenGL20::PixelTypes[(int)texture.format];
178 const GLenum pixelFormat = OpenGL20::PixelFormats[(int)texture.format];
179
180 if(isCompressed){
181 switch(target){
182 case GL_TEXTURE_1D:
183 assert(false);
184 break;
185 case GL_TEXTURE_1D_ARRAY:
186 assert(false);
187 break;
188 case GL_TEXTURE_2D:
189 for (size_t j = 0; j < data.levels; ++j) {
190 Cogs::TextureExtent ext = data.getExtent(j);
191 glCompressedTexSubImage2D(target, static_cast<GLint>(j), 0, 0, ext.width, ext.height, textureFormat, static_cast<GLsizei>(data.getLevelSize(j)), data.getData(0, 0, j));
192 }
193 break;
194 case GL_TEXTURE_2D_ARRAY:
195 assert(false);
196 break;
197 case GL_TEXTURE_2D_MULTISAMPLE:
198 assert(false);
199 break;
200 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
201 assert(false);
202 break;
203 case GL_TEXTURE_3D:
204 assert(false);
205 break;
206 case GL_TEXTURE_CUBE_MAP:
207 for (size_t j = 0; j < data.levels; ++j) {
208 for (GLint c = 0; c < 6; ++c) {
209 Cogs::TextureExtent ext = data.getExtent(j);
210 glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + c, static_cast<GLint>(j), textureFormat, ext.width, ext.height, 0, GLsizei(data.getLevelSize(j)), data.getData(0, c, j));
211 }
212 }
213 break;
214 case GL_TEXTURE_CUBE_MAP_ARRAY:
215 assert(false);
216 break;
217 case GL_INVALID_ENUM:
218 break;
219 }
220 }
221 else{
222 switch(target){
223 case GL_TEXTURE_1D:
224 for (size_t j = 0; j < data.levels; ++j) {
225 Cogs::TextureExtent ext = data.getExtent(j);
226 glTexSubImage1D(target, static_cast<GLint>(j), 0, ext.width, pixelFormat, pixelType, data.getData(0, 0, j));
227 }
228 break;
229 case GL_TEXTURE_1D_ARRAY:
230 for (size_t i = 0; i < data.layers; ++i) {
231 for (size_t j = 0; j < data.levels; ++j) {
232 Cogs::TextureExtent ext = data.getExtent(j);
233 glTexSubImage2D(target, static_cast<GLint>(j), 0, static_cast<GLint>(layer_offset+i), ext.width, 1, pixelFormat, pixelType, data.getData(i, 0, j));
234 }
235 }
236 break;
237 case GL_TEXTURE_2D:
238 for (size_t j = 0; j < data.levels; ++j) {
239 Cogs::TextureExtent ext = data.getExtent(j);
240 glTexSubImage2D(target, static_cast<GLint>(j), 0, 0, ext.width, ext.height, pixelFormat, pixelType, data.getData(0, 0, j));
241 }
242 break;
243 case GL_TEXTURE_2D_ARRAY:
244 for (size_t i = 0; i < data.layers; ++i) {
245 for (size_t j = 0; j < data.levels; ++j) {
246 Cogs::TextureExtent ext = data.getExtent(j);
247 glTexSubImage3D(target, static_cast<GLint>(j),
248 0, 0, static_cast<GLint>(layer_offset+i), ext.width, ext.height, 1,
249 pixelFormat, pixelType, data.getData(i, 0, j));
250 }
251 }
252 break;
253 case GL_TEXTURE_2D_MULTISAMPLE:
254 assert(false);
255 break;
256 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
257 assert(false);
258 break;
259 case GL_TEXTURE_3D:
260 for (size_t j = 0; j < data.levels; ++j) {
261 Cogs::TextureExtent ext = data.getExtent(j);
262 glTexSubImage3D(target, static_cast<GLint>(j),
263 0, 0, 0, ext.width, ext.height, ext.depth,
264 pixelFormat, pixelType, data.getData(0, 0, j));
265 }
266 break;
267 case GL_TEXTURE_CUBE_MAP:
268 for (size_t j = 0; j < data.levels; ++j) {
269 for (GLint c = 0; c < 6; ++c) {
270 Cogs::TextureExtent ext = data.getExtent(j);
271 glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + c, (GLint)j,
272 0, 0, ext.width, ext.height,
273 pixelFormat, pixelType, data.getData(0, c, j));
274 }
275 }
276 break;
277 case GL_TEXTURE_CUBE_MAP_ARRAY:
278 for (size_t i = 0; i < data.layers; ++i) {
279 for (size_t j = 0; j < data.levels; ++j) {
280 for (GLint c = 0; c < 6; ++c) {
281 Cogs::TextureExtent ext = data.getExtent(j);
282 glTexSubImage3D(target, (GLint)j,
283 0, 0, (GLint)(layer_offset+i)*6 + c, ext.width, ext.height, 1,
284 pixelFormat, pixelType, data.getData(i, c, j));
285 }
286 }
287 }
288 break;
289 case GL_INVALID_ENUM:
290 break;
291 }
292 }
293
294 const bool generateMips = (texture.flags & TextureFlags::GenerateMipMaps) != 0;
295 if (generateMips) {
296 glGenerateMipmap(target);
297 }
298}
299void Cogs::TexturesGL20::uploadTextureData(TextureHandle textureHandle,
300 const TextureData &data,
301 uint32_t layer_offset,
302 uint32_t face_offset,
303 uint32_t level_offset)
304{
305 TextureGL20 & texture = this->textures[textureHandle];
306 const GLenum target = texture.type;
307 glBindTexture(target, texture.textureId);
308 uploadTextureData(texture, data, layer_offset, face_offset, level_offset);
309 glBindTexture(target, 0);
310}
311
313{
314 GLuint sampler;
315 glGenSamplers(1, &sampler);
316
317 glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, OpenGL20::AddressModes[state.addressModeS]);
318 glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, OpenGL20::AddressModes[state.addressModeT]);
319 glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, OpenGL20::AddressModes[state.addressModeW]);
320
321 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, OpenGL20::MipMinFilterModes[state.filter]);
322 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, OpenGL20::MagFilterModes[state.filter]);
323
324 glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, state.borderColor);
325
327 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
328 } else {
329 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_NONE);
330 }
331
332 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_FUNC, OpenGL20::ComparisonFunctions[state.comparisonFunction]);
333
334 if (state.maxAnisotropy && useAnisotropic) {
335 glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(state.maxAnisotropy));
336 } else if (useAnisotropic) {
337 glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
338 }
339
340 return SamplerStateHandle(sampler);
341}
342
344{
345 const GLuint sampler = static_cast<GLuint>(handle.handle);
346 glDeleteSamplers(1, &sampler);
347}
348
350{
351 TextureGL20 & texture = this->textures[desc.texture];
352 TextureGL20 textureView = texture;
353 textureView.arraySize = desc.numLayers;
354 if(desc.numLevels <= 1) textureView.hasMipmaps = false;
355 textureView.textureView = true;
356
357 if(desc.numLayers == 1){
358 if(textureView.type == GL_TEXTURE_1D_ARRAY)
359 textureView.type = GL_TEXTURE_1D;
360 if(textureView.type == GL_TEXTURE_2D_ARRAY)
361 textureView.type = GL_TEXTURE_2D;
362 if(textureView.type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
363 textureView.type = GL_TEXTURE_2D_MULTISAMPLE;
364 if(textureView.type == GL_TEXTURE_CUBE_MAP_ARRAY)
365 textureView.type = GL_TEXTURE_CUBE_MAP;
366 }
367
368 glGenTextures(1, &textureView.textureId);
369
370#ifndef __APPLE__
371 if(glTextureView){
372 const GLenum textureFormat = OpenGL20::TextureFormats[(int)texture.format];
373 glTextureView(textureView.textureId,
374 textureView.type,
375 texture.textureId,
376 textureFormat,
377 desc.levelIndex,
378 desc.numLevels,
379 desc.layerIndex,
380 desc.numLayers);
381 }
382 else
383#endif
384 {
385 assert(false);
386 }
387
388 GLenum target = textureView.type;
389 glBindTexture(target, textureView.textureId);
390
391 glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
392 if (!texture.hasMipmaps) {
393 // If no mipmaps are defined, ensure the texture is considered complete with only one
394 // level specified (#0) even if mipmap filtering is applied.
395 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 0);
396 }
397
398 glBindTexture(target, 0);
399
400 return TextureViewHandle(this->textures.addResource(std::move(textureView)).handle);
401}
402
404{
405 TextureGL20 & textureView = this->textures[TextureHandle(handle.handle)];
406 glDeleteTextures(1, &textureView.textureId);
407 this->textures.removeResource(TextureHandle(handle.handle));
408}
409
411{
412 TextureGL20 & texture = this->textures[texureHandle];
413 GLenum target = texture.type;
414 glBindTexture(target, texture.textureId);
415 glGenerateMipmap(target);
416 texture.hasMipmaps = true;
417 glBindTexture(target, 0);
418}
419
421{
422 std::vector<TextureHandle> handles;
423 for (TextureGL20& texture : textures) {
424 handles.push_back(textures.getHandle(texture));
425 }
426 for (TextureHandle & handle : handles) {
427 releaseTexture(handle);
428 }
429}
430
432{
433#ifndef __APPLE__
434 TextureGL20 & texture = this->textures[handle];
435 glObjectLabel(GL_TEXTURE, texture.textureId, static_cast<GLsizei>(name.size()), name.data());
436#else
437 (void)handle;
438 (void)name;
439#endif
440}
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:171
constexpr size_t size() const noexcept
Get the size of the string.
Definition: StringView.h:178
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
const char * name
Name as a color format (using RGBA as channels).
Definition: DataFormat.h:260
handle_type handle
Internal resource handle.
Definition: Common.h:74
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:80
Encapsulates state for texture sampling in a state object.
Definition: SamplerState.h:12
ComparisonFunction comparisonFunction
Specifies the comparison function to use when applying a comparison sampler.
Definition: SamplerState.h:73
unsigned int maxAnisotropy
Specifies the maximum number of anisotropic samples to use when sampling a texture.
Definition: SamplerState.h:76
AddressMode addressModeW
Specifies the addressing mode along the W axis in texture coordinate space.
Definition: SamplerState.h:67
AddressMode addressModeS
Specifies the addressing mode along the S axis in texture coordinate space.
Definition: SamplerState.h:63
float borderColor[4]
Definition: SamplerState.h:80
@ ComparisonMinMagMipPoint
Comparison filter for depth sample comparisons using point sampling.
Definition: SamplerState.h:38
@ ComparisonMinMagMipLinear
Comparison filter for depth sample comparisons using linear interpolation sampling.
Definition: SamplerState.h:40
FilterMode filter
Specifies the filter to use for texture sampling.
Definition: SamplerState.h:70
AddressMode addressModeT
Specifies the addressing mode along the T axis in texture coordinate space.
Definition: SamplerState.h:65
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
Definition: Flags.h:124
Describes how to fetch data from a texture in shaders.
Definition: ITextures.h:13
void releaseSamplerState(SamplerStateHandle handle) override
Release the sampler state with the given handle.
SamplerStateHandle loadSamplerState(const SamplerState &state) override
Load a sampler state object.
void generateMipmaps(TextureHandle texureHandle) override
Use the graphics device to generate mipmaps for the texture with the given texture handle.
TextureViewHandle createTextureView(TextureViewDescription &viewDescription) override
Create a texture view used to bind a limited view of the texture data to the rendering pipeline.
void releaseTextureView(const TextureViewHandle &handle) override
Release the given texture view.
void annotate(TextureHandle handle, const StringView &name) override
Associate a name with an object for use in graphics debugging.
void releaseResources() override
Release all allocated texture resources.
TextureHandle loadTexture(const TextureDescription &desc, const TextureData *data) override
Load a texture from the given description.
void releaseTexture(TextureHandle textureHandle) override
Release the texture with the given textureHandle.