Skip to content

Commit

Permalink
Add support for cubemap arrays in the backends
Browse files Browse the repository at this point in the history
This is not intended to be used yet because we're not currently
checking that cubemap array are actually supported, however, if they
are, they should work.
  • Loading branch information
pixelflinger committed Jul 26, 2022
1 parent 88b29b9 commit ec74cc2
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 42 deletions.
11 changes: 6 additions & 5 deletions filament/backend/include/backend/DriverEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,12 @@ enum class Precision : uint8_t {

//! Texture sampler type
enum class SamplerType : uint8_t {
SAMPLER_2D, //!< 2D texture
SAMPLER_2D_ARRAY, //!< 2D array texture
SAMPLER_CUBEMAP, //!< Cube map texture
SAMPLER_EXTERNAL, //!< External texture
SAMPLER_3D, //!< 3D texture
SAMPLER_2D, //!< 2D texture
SAMPLER_2D_ARRAY, //!< 2D array texture
SAMPLER_CUBEMAP, //!< Cube map texture
SAMPLER_EXTERNAL, //!< External texture
SAMPLER_3D, //!< 3D texture
SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2)
};

//! Subpass type
Expand Down
2 changes: 2 additions & 0 deletions filament/backend/src/metal/MetalEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ constexpr inline MTLTextureType getMetalType(SamplerType target) {
return MTLTextureTypeCube;
case SamplerType::SAMPLER_3D:
return MTLTextureType3D;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return MTLTextureTypeCubeArray;
}
}

Expand Down
34 changes: 14 additions & 20 deletions filament/backend/src/metal/MetalHandles.mm
Original file line number Diff line number Diff line change
Expand Up @@ -433,36 +433,27 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) {

const BOOL mipmapped = levels > 1;
const BOOL multisampled = samples > 1;
const BOOL textureArray = target == SamplerType::SAMPLER_2D_ARRAY;

#if defined(IOS)
const BOOL textureArray = target == SamplerType::SAMPLER_2D_ARRAY;
ASSERT_PRECONDITION(!textureArray || !multisampled,
"iOS does not support multisampled texture arrays.");
#endif

const auto get2DTextureType = [](bool isArray, bool isMultisampled) {
uint8_t value = 0;
if (isMultisampled) {
value |= 0b10u;
}
if (isArray) {
value |= 0b01u;
const auto get2DTextureType = [](SamplerType target, bool isMultisampled) {
if (!isMultisampled) {
return getMetalType(target);
}
switch (value) {
default:
case 0b00:
return MTLTextureType2D;
case 0b01:
return MTLTextureType2DArray;
case 0b10:
switch (target) {
case SamplerType::SAMPLER_2D:
return MTLTextureType2DMultisample;
case 0b11:
#if !defined(IOS)
case SamplerType::SAMPLER_2D_ARRAY:
return MTLTextureType2DMultisampleArray;
#else
// should not get here
return MTLTextureType2DArray;
#endif
default:
// should not get here
return getMetalType(target);
}
};

Expand All @@ -472,7 +463,7 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) {
case SamplerType::SAMPLER_2D_ARRAY:
descriptor = [MTLTextureDescriptor new];
descriptor.pixelFormat = devicePixelFormat;
descriptor.textureType = get2DTextureType(textureArray, multisampled);
descriptor.textureType = get2DTextureType(target, multisampled);
descriptor.width = width;
descriptor.height = height;
descriptor.arrayLength = depth;
Expand All @@ -484,11 +475,13 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) {
ASSERT_POSTCONDITION(texture != nil, "Could not create Metal texture. Out of memory?");
break;
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
ASSERT_POSTCONDITION(!multisampled, "Multisampled cubemap faces not supported.");
ASSERT_POSTCONDITION(width == height, "Cubemap faces must be square.");
descriptor = [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:devicePixelFormat
size:width
mipmapped:mipmapped];
descriptor.arrayLength = depth;
descriptor.mipmapLevelCount = levels;
descriptor.usage = getMetalTextureUsage(usage);
descriptor.storageMode = MTLStorageModePrivate;
Expand Down Expand Up @@ -642,6 +635,7 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) {
}

case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
case SamplerType::SAMPLER_2D_ARRAY: {
// Metal uses 'slice' (not z offset) to index into individual layers of a texture array.
const uint32_t slice = region.origin.z;
Expand Down
18 changes: 10 additions & 8 deletions filament/backend/src/opengl/OpenGLContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ class OpenGLContext {
GLuint sampler = 0;
struct {
GLuint texture_id = 0;
} targets[6]; // this must match getIndexForTextureTarget()
} targets[7]; // this must match getIndexForTextureTarget()
} units[MAX_TEXTURE_UNIT_COUNT];
} textures;

Expand Down Expand Up @@ -399,13 +399,15 @@ class OpenGLContext {
constexpr size_t OpenGLContext::getIndexForTextureTarget(GLuint target) noexcept {
// this must match state.textures[].targets[]
switch (target) {
case GL_TEXTURE_2D: return 0;
case GL_TEXTURE_2D_ARRAY: return 1;
case GL_TEXTURE_CUBE_MAP: return 2;
case GL_TEXTURE_2D_MULTISAMPLE: return 3;
case GL_TEXTURE_EXTERNAL_OES: return 4;
case GL_TEXTURE_3D: return 5;
default: return 0;
case GL_TEXTURE_2D: return 0;
case GL_TEXTURE_2D_ARRAY: return 1;
case GL_TEXTURE_CUBE_MAP: return 2;
case GL_TEXTURE_2D_MULTISAMPLE: return 3;
case GL_TEXTURE_EXTERNAL_OES: return 4;
case GL_TEXTURE_3D: return 5;
case GL_TEXTURE_CUBE_MAP_ARRAY: return 6;
default:
return 0;
}
}

Expand Down
29 changes: 25 additions & 4 deletions filament/backend/src/opengl/OpenGLDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
GLsizei(width), GLsizei(height), GLsizei(depth));
break;
}
case GL_TEXTURE_CUBE_MAP_ARRAY: {
glTexStorage3D(t->gl.target, GLsizei(t->levels), t->gl.internalFormat,
GLsizei(width), GLsizei(height), GLsizei(depth) * 6);
break;
}
case GL_TEXTURE_2D_MULTISAMPLE:
if constexpr (TEXTURE_2D_MULTISAMPLE_SUPPORTED) {
// NOTE: if there is a mix of texture and renderbuffers, "fixed_sample_locations" must be true
Expand Down Expand Up @@ -566,6 +571,10 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
t->gl.target = GL_TEXTURE_CUBE_MAP;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
break;
}

if (t->samples > 1) {
Expand Down Expand Up @@ -662,6 +671,10 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
t->gl.target = GL_TEXTURE_CUBE_MAP;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
break;
}

if (t->samples > 1) {
Expand Down Expand Up @@ -800,6 +813,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
switch (t->target) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
// this could be GL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_ARRAY
target = t->gl.target;
// note: multi-sampled textures can't have mipmaps
Expand All @@ -813,7 +827,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
}
}

// We can't use FramebufferTexture2DMultisampleEXT with GL_TEXTURE_2D_ARRAY.
// We can't use FramebufferTexture2DMultisampleEXT with GL_TEXTURE_2D_ARRAY or GL_TEXTURE_CUBE_MAP_ARRAY.
if (!(target == GL_TEXTURE_2D ||
target == GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X ||
Expand All @@ -834,7 +848,8 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
const bool disableMultisampling =
gl.bugs.disable_sidecar_blit_into_texture_array &&
rt->gl.samples > 1 && t->samples <= 1 &&
target == GL_TEXTURE_2D_ARRAY; // implies MSRTT is not available
(target == GL_TEXTURE_2D_ARRAY ||
target == GL_TEXTURE_CUBE_MAP_ARRAY); // implies MSRTT is not available

if (rt->gl.samples <= 1 ||
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture) ||
Expand Down Expand Up @@ -862,6 +877,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
}
break;
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
Expand Down Expand Up @@ -959,6 +975,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
}
break;
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
break;
Expand Down Expand Up @@ -1860,11 +1877,13 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
break;
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
assert_invariant(zoffset + depth <= t->depth);
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY);
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY ||
t->gl.target == GL_TEXTURE_CUBE_MAP_ARRAY);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
GLsizei(width), GLsizei(height), GLsizei(depth), glFormat, glType, p.buffer);
Expand Down Expand Up @@ -1953,7 +1972,9 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
t->gl.internalFormat, imageSize, p.buffer);
break;
case SamplerType::SAMPLER_2D_ARRAY:
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY);
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY ||
t->gl.target == GL_TEXTURE_CUBE_MAP_ARRAY);
glCompressedTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
GLsizei(width), GLsizei(height), GLsizei(depth),
Expand Down
26 changes: 23 additions & 3 deletions filament/backend/src/opengl/gl_headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@
#ifdef GL_EXT_clip_control
extern PFNGLCLIPCONTROLEXTPROC glClipControl;
#ifndef GL_LOWER_LEFT
#define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
#endif
#ifndef GL_ZERO_TO_ONE
#define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
#endif
#endif
}
Expand All @@ -82,7 +82,17 @@

#define glDebugMessageCallback glext::glDebugMessageCallbackKHR

using namespace glext;
#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A

using namespace glext;

#elif defined(IOS)

Expand All @@ -97,6 +107,16 @@
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
#define GL_TIME_ELAPSED 0x88BF

#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A

#ifdef GL_EXT_multisampled_render_to_texture
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
extern PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT;
Expand Down
1 change: 1 addition & 0 deletions filament/backend/src/ostream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ io::ostream& operator<<(io::ostream& out, SamplerType type) {
CASE(SamplerType, SAMPLER_2D_ARRAY)
CASE(SamplerType, SAMPLER_3D)
CASE(SamplerType, SAMPLER_CUBEMAP)
CASE(SamplerType, SAMPLER_CUBEMAP_ARRAY)
CASE(SamplerType, SAMPLER_EXTERNAL)
}
return out;
Expand Down
13 changes: 11 additions & 2 deletions filament/backend/src/vulkan/VulkanTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
// In other words, the "arrayness" of the texture is an aspect of the VkImageView,
// not the VkImage.
}
if (target == SamplerType::SAMPLER_CUBEMAP_ARRAY) {
imageInfo.arrayLayers = depth * 6;
imageInfo.extent.depth = 1;
}

// Filament expects blit() to work with any texture, so we almost always set these usage flags.
// TODO: investigate performance implications of setting these flags.
Expand Down Expand Up @@ -169,6 +173,8 @@ VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t
mPrimaryViewRange.baseArrayLayer = 0;
if (target == SamplerType::SAMPLER_CUBEMAP) {
mPrimaryViewRange.layerCount = 6;
} else if (target == SamplerType::SAMPLER_CUBEMAP_ARRAY) {
mPrimaryViewRange.layerCount = depth * 6;
} else if (target == SamplerType::SAMPLER_2D_ARRAY) {
mPrimaryViewRange.layerCount = depth;
} else if (target == SamplerType::SAMPLER_3D) {
Expand Down Expand Up @@ -206,7 +212,8 @@ VulkanTexture::~VulkanTexture() {
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
assert_invariant(width <= this->width && height <= this->height);
assert_invariant(depth <= this->depth * (target == SamplerType::SAMPLER_CUBEMAP ? 6 : 1));
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));

const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
Expand Down Expand Up @@ -262,7 +269,9 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
};

// Vulkan specifies subregions for 3D textures differently than from 2D arrays.
if (target == SamplerType::SAMPLER_2D_ARRAY || target == SamplerType::SAMPLER_CUBEMAP) {
if ( target == SamplerType::SAMPLER_2D_ARRAY ||
target == SamplerType::SAMPLER_CUBEMAP ||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) {
copyRegion.imageOffset.z = 0;
copyRegion.imageExtent.depth = 1;
copyRegion.imageSubresource.baseArrayLayer = zoffset;
Expand Down
2 changes: 2 additions & 0 deletions filament/backend/src/vulkan/VulkanUtility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,8 @@ VkImageViewType getImageViewType(SamplerType target) {
return VK_IMAGE_VIEW_TYPE_CUBE;
case SamplerType::SAMPLER_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case SamplerType::SAMPLER_3D:
return VK_IMAGE_VIEW_TYPE_3D;
default:
Expand Down
2 changes: 2 additions & 0 deletions filament/backend/test/test_LoadImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ static const char* getSamplerTypeName(SamplerType samplerType) {
return "samplerCube";
case SamplerType::SAMPLER_3D:
return "sampler3D";
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return "samplerCubeArray";
}
}

Expand Down
Loading

0 comments on commit ec74cc2

Please sign in to comment.