1#include "TexturesGLES30.h"
5#include "FormatsGLES30.h"
6#include "CapabilitiesGLES30.h"
7#include "ContextGLES30.h"
9#include "Foundation/Logging/Logger.h"
20 LOG_ERROR(logger,
"Illegal %s description (width=%u, height=%u, depth=%u, layers=%u, faces=%u, levels=%u, samples=%u)",
21 getResourceDimensionsName(desc.target), desc.width, desc.height, desc.depth, desc.layers, desc.faces, desc.levels, desc.samples);
26 uint32_t clampFormatSampleCount(
CapabilitiesGLES30* caps, Cogs::TextureFormat format, uint8_t numSamples)
31 GLint maxSampleCount = 0;
32 glGetInternalformativ(GL_RENDERBUFFER, fmt.internalFormat, GL_SAMPLES, 1, &maxSampleCount);
33 assert(0 <= maxSampleCount);
36 LOG_WARNING_ONCE(logger,
"getInternalFormat(fmt=0x%04x) returned maxSampleCount=%d, but glGetIntegerv(GL_MAX_SAMPLES) returned %u, clamping.",
40 if (uint32_t(maxSampleCount) < numSamples) {
41 LOG_DEBUG_ONCE(logger,
"Requested sample count %u greater than maximum %d for format %s, clamping.",
42 numSamples, maxSampleCount, getFormatInfo(format)->name);
43 return maxSampleCount;
55 texture.width = desc.width;
56 texture.height = desc.height;
58 texture.estimatedByteSize = uint32_t(desc.estimateMemorySize());
59 texture.flags = desc.flags;
60 texture.numSamples = uint8_t(desc.samples);
61 texture.textureId = 0;
62 texture.renderBuffer = 0;
64 texture.format = desc.format;
65 texture.cubeMap =
false;
66 texture.hasMipmaps = generateMips || desc.levels > 1;
70 if (fmt.isTextureFormat == 0) {
73 if (data && fmt.isCompressed) {
74 texture.estimatedByteSize = uint32_t(data->getSize());
77 switch (desc.target) {
78 case ResourceDimensions::Unknown:
79 case ResourceDimensions::Buffer:
80 case ResourceDimensions::Texture1D:
81 case ResourceDimensions::Texture1DArray:
82 case ResourceDimensions::Texture3DArray:
83 case ResourceDimensions::TextureCubeArray:
84 case ResourceDimensions::Texture2DMSArray:
85 LOG_ERROR(logger,
"Unsupported texture resource dimension %s", getResourceDimensionsName(desc.target));
88 case ResourceDimensions::Texture2DMS:
89 if (desc.width == 0 || desc.height == 0 || desc.depth != 1 || desc.layers != 1 || desc.faces != 1 || desc.samples == 0) {
90 return logAndReturnInvalidHandle(desc);
93 texture.target = GL_TEXTURE_2D_MULTISAMPLE;
95 texture.numSamples =
static_cast<uint8_t
>(clampFormatSampleCount(caps, texture.format, texture.numSamples));
98 glGenRenderbuffers(1, &texture.renderBuffer);
99 glBindRenderbuffer(GL_RENDERBUFFER, texture.renderBuffer);
100 glRenderbufferStorageMultisample(GL_RENDERBUFFER, texture.numSamples, fmt.internalFormat, texture.width, texture.height);
101 glBindRenderbuffer(GL_RENDERBUFFER, 0);
105 glGenFramebuffers(1, &texture.fbo);
106 glBindFramebuffer(GL_READ_FRAMEBUFFER, texture.fbo);
107 glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, framebufferAttachment, GL_RENDERBUFFER, texture.renderBuffer);
108 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
110 GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
111 if (status != GL_FRAMEBUFFER_COMPLETE) {
112 LOG_ERROR(logger,
"Failed to create multisample texture rendertarget: %s", OpenGLES30::framebuffersStatusString(status));
113 glDeleteFramebuffers(1, &texture.fbo);
114 glDeleteRenderbuffers(1, &texture.renderBuffer);
118 return textures.addResource(texture);
122 case ResourceDimensions::Texture2D:
123 if (desc.width == 0 || desc.height == 0 || desc.depth != 1 || desc.layers != 1 || desc.faces != 1 || desc.samples != 1) {
124 return logAndReturnInvalidHandle(desc);
126 texture.target = GL_TEXTURE_2D;
128 case ResourceDimensions::Texture2DArray:
129 if (desc.width == 0 || desc.height == 0 || desc.depth != 1 || desc.layers == 0 || desc.faces != 1 || desc.samples != 1) {
130 return logAndReturnInvalidHandle(desc);
132 texture.target = GL_TEXTURE_2D_ARRAY;
133 texture.depth = desc.layers;
135 case ResourceDimensions::Texture3D:
136 if (desc.width == 0 || desc.height == 0 || desc.depth == 0 || desc.layers != 1 || desc.faces != 1 || desc.samples != 1) {
137 return logAndReturnInvalidHandle(desc);
139 texture.target = GL_TEXTURE_3D;
140 texture.depth = desc.depth;
142 case ResourceDimensions::TextureCube:
143 if (desc.width == 0 || desc.height == 0 || desc.depth != 1 || desc.layers != 1 || desc.faces != 6 || desc.samples != 1) {
144 return logAndReturnInvalidHandle(desc);
146 texture.target = GL_TEXTURE_CUBE_MAP;
147 texture.depth = desc.depth;
148 texture.cubeMap =
true;
151 case ResourceDimensions::RenderBuffer:
152 if (desc.width == 0 || desc.height == 0 || desc.depth != 1 || desc.layers != 1 || desc.faces != 1 || desc.levels != 1) {
153 return logAndReturnInvalidHandle(desc);
156 LOG_ERROR(logger,
"RenderBuffers do not support mipmaps");
159 texture.target = GL_RENDERBUFFER;
160 texture.numSamples =
static_cast<uint8_t
>(clampFormatSampleCount(caps, texture.format, texture.numSamples));
163 assert(
false &&
"Invalid enum");
167 GLuint textureLevels;
169 textureLevels = 1 +
static_cast<GLuint
>(std::floor(std::log((
double)std::max(desc.width, desc.height)) / std::log(2.0)));
172 textureLevels =
static_cast<GLuint
>(desc.levels);
179 if (texture.target == GL_RENDERBUFFER) {
181 texture.textureId = 0;
183 assert(data !=
nullptr);
184 texture.renderBuffer =
static_cast<GLuint
>(data->externalHandle);
187 glGenRenderbuffers(1, &texture.renderBuffer);
188 glBindRenderbuffer(GL_RENDERBUFFER, texture.renderBuffer);
189 glRenderbufferStorageMultisample(GL_RENDERBUFFER, texture.numSamples, fmt.internalFormat, texture.width, texture.height);
190 glBindRenderbuffer(GL_RENDERBUFFER, 0);
192 textureHandle = textures.addResource(texture);
198 assert(data !=
nullptr);
199 texture.textureId =
static_cast<GLuint
>(data->externalHandle);
200 textureHandle = textures.addResource(texture);
203 if (
TextureGLES30* tex = context->bindTexture(textureHandle); tex) {
204 glGenerateMipmap(tex->target);
209 glGenTextures(1, &texture.textureId);
210 textureHandle = textures.addResource(texture);
212 if (
TextureGLES30* tex = context->bindTexture(textureHandle); tex) {
213 defineTextureData(tex, textureLevels);
215 uploadTextureData(*tex, *data, 0, 0, 0);
217 else if (generateMips) {
218 glGenerateMipmap(tex->target);
224 textureMemoryConsumption += texture.estimatedByteSize;
225 return textureHandle;
228void Cogs::TexturesGLES30::uploadTextureData(
TextureGLES30& tex,
230 uint32_t layer_offset,
231 uint32_t face_offset,
232 uint32_t level_offset)
234 assert(face_offset == 0 &&
"face_offset not handled");
235 assert(level_offset == 0 &&
"level_offset not handled");
238 const bool isCompressed = fmt.isCompressed;
239 const GLenum target = tex.target;
240 const GLenum textureFormat = fmt.internalFormat;
241 const GLenum pixelType = fmt.type;
242 const GLenum pixelFormat = fmt.format;
244 if(context->uploadStatisticsEnabled) context->uploadStatisticsTextureUpload(data.getSize());
249 for (
size_t j = 0; j < data.levels; ++j) {
251 glCompressedTexSubImage2D(target,
static_cast<GLint
>(j), 0, 0, ext.width, ext.height, textureFormat,
static_cast<GLsizei
>(data.getLevelSize(j)), data.getData(0, 0, j));
254 case GL_TEXTURE_CUBE_MAP:
255 for (
size_t j = 0; j < data.levels; ++j) {
256 for (GLint c = 0; c < 6; ++c) {
258 glCompressedTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + c,
static_cast<GLint
>(j), 0, 0, ext.width, ext.height, textureFormat, GLsizei(data.getLevelSize(j)), data.getData(0, c, j));
262 case GL_TEXTURE_2D_ARRAY: [[fallthrough]];
263 case GL_TEXTURE_2D_MULTISAMPLE: [[fallthrough]];
264 case GL_TEXTURE_3D: [[fallthrough]];
265 case GL_INVALID_ENUM:
266 LOG_ERROR(logger,
"Texture target #%04x cannot use compressed formats", target);
273 for (
size_t j = 0; j < data.levels; ++j) {
275 glTexSubImage2D(target,
static_cast<GLint
>(j), 0, 0, ext.width, ext.height, pixelFormat, pixelType, data.getData(0, 0, j));
278 case GL_TEXTURE_2D_ARRAY:
279 for (
size_t i = 0; i < data.layers; ++i) {
280 for (
size_t j = 0; j < data.levels; ++j) {
282 glTexSubImage3D(target,
static_cast<GLint
>(j),
283 0, 0,
static_cast<GLint
>(layer_offset + i), ext.width, ext.height, 1,
284 pixelFormat, pixelType, data.getData(i, 0, j));
289 for (
size_t j = 0; j < data.levels; ++j) {
291 glTexSubImage3D(target,
static_cast<GLint
>(j),
292 0, 0, 0, ext.width, ext.height, ext.depth,
293 pixelFormat, pixelType, data.getData(0, 0, j));
296 case GL_TEXTURE_CUBE_MAP:
297 for (
size_t j = 0; j < data.levels; ++j) {
298 for (GLint c = 0; c < 6; ++c) {
300 glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + c, (GLint)j,
301 0, 0, ext.width, ext.height,
302 pixelFormat, pixelType, data.getData(0, c, j));
306 case GL_TEXTURE_2D_MULTISAMPLE: [[fallthrough]];
307 case GL_INVALID_ENUM:
308 LOG_ERROR(logger,
"Texture target #%04x cannot get data uploaded", target);
315 glGenerateMipmap(target);
321 uint32_t layer_offset,
322 uint32_t face_offset,
323 uint32_t level_offset)
326 const GLenum target = texture.target;
327 glBindTexture(target, texture.textureId);
328 uploadTextureData(texture, data, layer_offset, face_offset, level_offset);
329 glBindTexture(target, 0);
333void Cogs::TexturesGLES30::defineTextureData(
TextureGLES30* tex, GLuint textureLevels)
336 assert(fmt.isTextureFormat);
338 switch (tex->target) {
341 glTexStorage2D(GL_TEXTURE_2D, textureLevels, fmt.internalFormat, tex->width, tex->height);
344 case GL_TEXTURE_CUBE_MAP:
345 glTexStorage2D(tex->target, textureLevels, fmt.internalFormat, tex->width, tex->height);
348 case GL_TEXTURE_2D_ARRAY:
349 glTexStorage3D(tex->target, textureLevels, fmt.internalFormat, tex->width, tex->height, tex->depth);
353 glTexStorage3D(tex->target, textureLevels, fmt.internalFormat, tex->width, tex->height, tex->depth);
356 case GL_TEXTURE_2D_MULTISAMPLE: [[fallthrough]];
358 LOG_ERROR(logger,
"Texture target #%04x cannot get data uploaded", tex->target);
365 if (HandleIsValid(textureHandle)) {
368 for (GLuint i = 0; i < OpenGLES30::maxTexUnits; i++) {
369 if (context->texUnits[i].texture == textureHandle) {
370 if (context->activeTexUnit != i) {
371 context->activeTexUnit = i;
372 glActiveTexture(GL_TEXTURE0 + i);
374 glBindTexture(context->texUnits[i].target, 0);
376 context->texUnits[i].target = GL_TEXTURE_2D;
386 glDeleteFramebuffers(1, &texture.fbo);
389 if (texture.renderBuffer) {
393 glDeleteRenderbuffers(1, &texture.renderBuffer);
395 texture.renderBuffer = 0;
399 glDeleteTextures(1, &texture.textureId);
401 if (texture.textureId != 0) {
402 textureMemoryConsumption -= texture.estimatedByteSize;
404 texture.textureId = 0;
406 textures.removeResource(textureHandle);
412 GLuint
id = (GLuint)(intptr_t)nativeHandle;
413 glDeleteTextures(1, &
id);
419 LOG_WARNING_ONCE(logger,
"loadSamplerState: ES30 does not support border color.");
423 glGenSamplers(1, &sampler);
425 static const GLenum AddressModes[] = {
431 static_assert(
sizeof(AddressModes) ==
sizeof(AddressModes[0]) * Cogs::SamplerState::AddressMode_Size);
433 glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, AddressModes[state.
addressModeS]);
434 glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, AddressModes[state.
addressModeT]);
435 glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, AddressModes[state.
addressModeW]);
437 static const GLenum ComparisonFunctions[] = {
447 static_assert(
sizeof(ComparisonFunctions) ==
sizeof(ComparisonFunctions[0]) * Cogs::SamplerState::ComparisonFunction_Size);
451 float maxAnisotropy = std::min(deviceCaps.MaxAnisotropy,
float(state.
maxAnisotropy));
452 if (1.f < maxAnisotropy) {
453 glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy);
459 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
460 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
461 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_NONE);
464 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
465 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
466 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_NONE);
469 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
470 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
471 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
472 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_FUNC, ComparisonFunctions[state.
comparisonFunction]);
476 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
477 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
478 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
479 glSamplerParameteri(sampler, GL_TEXTURE_COMPARE_FUNC, ComparisonFunctions[state.
comparisonFunction]);
490 if (HandleIsValid(samplerHandle)) {
492 glDeleteSamplers(1, &sampler.glName);
493 samplers.removeResource(samplerHandle);
500 LOG_WARNING_ONCE(logger,
"Views with nonzero layer index not supported, ignoring");
502 return views.addResource(viewDescription);
507 if (HandleIsValid(handle)) {
508 views.removeResource(handle);
514 if (HandleIsValid(textureHandle)) {
516 if (tex.target == GL_RENDERBUFFER) {
517 return reinterpret_cast<void*
>(
static_cast<size_t>(tex.renderBuffer));
519 return reinterpret_cast<void*
>(
static_cast<size_t>(tex.textureId));
526 TextureGLES30* texture = context->bindTexture(textureHandle);
528 glGenerateMipmap(texture->target);
534 std::vector<TextureHandle> handles;
536 handles.push_back(textures.getHandle(texture));
539 releaseTexture(handle);
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
const GraphicsDeviceCapabilities & getDeviceCapabilities() const override
Gets the device capabilities in a structure.
Contains device capabilities.
static const Handle_t NoHandle
Represents a handle to nothing.
static const Handle_t InvalidHandle
Represents an invalid handle.
Encapsulates state for texture sampling in a state object.
ComparisonFunction comparisonFunction
Specifies the comparison function to use when applying a comparison sampler.
unsigned int maxAnisotropy
Specifies the maximum number of anisotropic samples to use when sampling a texture.
AddressMode addressModeW
Specifies the addressing mode along the W axis in texture coordinate space.
AddressMode addressModeS
Specifies the addressing mode along the S axis in texture coordinate space.
@ Border
Texture color is set to the border color when outside [0, 1] range.
@ ComparisonMinMagMipPoint
Comparison filter for depth sample comparisons using point sampling.
@ ComparisonMinMagMipLinear
Comparison filter for depth sample comparisons using linear interpolation sampling.
@ MinMagMipPoint
Point sampling for both minification and magnification.
@ MinMagMipLinear
Linear sampling for both minification and magnification.
FilterMode filter
Specifies the filter to use for texture sampling.
AddressMode addressModeT
Specifies the addressing mode along the T axis in texture coordinate space.
@ DepthBuffer
The texture can be used as a depth target and have depth buffer values written into.
@ NoDelete
The ownership of the underlying texture resource is outside of cogs and cogs will not delete it.
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
Describes how to fetch data from a texture in shaders.
uint32_t layerIndex
Index of the first layer (if array) to fetch from.
void releaseNativeTexture(TextureNativeHandle nativeHandle) override
Release a native texture handle.
void releaseSamplerState(SamplerStateHandle handle)
Release the sampler state with the given handle.
void releaseTexture(TextureHandle textureHandle)
Release the texture with the given textureHandle.
void generateMipmaps(TextureHandle texureHandle)
Use the graphics device to generate mipmaps for the texture with the given texture handle.
void releaseResources()
Release all allocated texture resources.
TextureViewHandle createTextureView(TextureViewDescription &viewDescription) override
Create a texture view used to bind a limited view of the texture data to the rendering pipeline.
void * getNativeHandle(TextureHandle textureHandle) override
Get the device-specific handle (D3D texture pointer, OpenGL texture ID etc) associated with the given...
TextureHandle loadTexture(const TextureDescription &desc, const TextureData *data)
Load a texture from the given description.
void releaseTextureView(const TextureViewHandle &handle) override
Release the given texture view.
SamplerStateHandle loadSamplerState(const SamplerState &state)
Load a sampler state object.