From 3abddc4584d63ce72bb8e2ccb11d41af490c0cc8 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 13 Mar 2025 14:30:09 -0700 Subject: [PATCH] APIs to query the texture limit sizes. Assert textures dimensions are in range when creating a Texture object. FIXES=[398901038] --- .../filament-android/src/main/cpp/Texture.cpp | 15 +++++++++++++ .../com/google/android/filament/Texture.java | 20 +++++++++++++++++ .../include/private/backend/DriverAPI.inc | 2 ++ filament/backend/src/metal/MetalDriver.mm | 10 +++++++++ filament/backend/src/noop/NoopDriver.cpp | 8 +++++++ filament/backend/src/opengl/OpenGLContext.cpp | 20 +++++++++++++---- filament/backend/src/opengl/OpenGLContext.h | 4 ++++ filament/backend/src/opengl/OpenGLDriver.cpp | 20 +++++++++++++++++ filament/backend/src/vulkan/VulkanDriver.cpp | 10 +++++++++ filament/backend/src/webgpu/WebGPUDriver.cpp | 10 ++++++++- filament/include/filament/Texture.h | 7 ++++++ filament/src/Texture.cpp | 8 +++++++ filament/src/details/Texture.cpp | 22 +++++++++++++++++++ filament/src/details/Texture.h | 4 ++++ 14 files changed, 155 insertions(+), 5 deletions(-) diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp index cac8d94d87..ea12b66b67 100644 --- a/android/filament-android/src/main/cpp/Texture.cpp +++ b/android/filament-android/src/main/cpp/Texture.cpp @@ -68,6 +68,21 @@ Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jcl return (jboolean) Texture::isTextureSwizzleSupported(*engine); } + +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_Texture_nGetMaxTextureSize(JNIEnv *, jclass, + jlong nativeEngine, jint sampler) { + Engine *engine = (Engine *) nativeEngine; + return Texture::getMaxTextureSize(*engine, (Texture::Sampler)sampler); +} + +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_Texture_nGetMaxArrayTextureLayers(JNIEnv *, jclass, + jlong nativeEngine) { + Engine *engine = (Engine *) nativeEngine; + return Texture::getMaxArrayTextureLayers(*engine); +} + extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass, jint internalFormat, jint pixelDataFormat, jint pixelDataType) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/Texture.java b/android/filament-android/src/main/java/com/google/android/filament/Texture.java index 15f7c72b7e..d62386e489 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Texture.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Texture.java @@ -691,6 +691,24 @@ public class Texture { pixelDataType.ordinal()); } + /** + * @param engine {@link Engine} + * @param type Texture sampler type + * @return The maximum size in texels of a texture of type \p type. At least 2048 for + * 2D textures, 256 for 3D textures + */ + public static int getMaxTextureSize(@NonNull Engine engine, Sampler type) { + return nGetMaxTextureSize(engine.getNativeObject(), type.ordinal()); + } + + /** + * @param engine {@link Engine} + * @return The maximum number of layers supported by texture arrays. At least 256. + */ + public static int getMaxArrayTextureLayers(@NonNull Engine engine) { + return nGetMaxArrayTextureLayers(engine.getNativeObject()); + } + /** * Use Builder to construct a Texture object instance. */ @@ -1289,6 +1307,8 @@ public class Texture { private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat); private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat); private static native boolean nIsTextureSwizzleSupported(long nativeEngine); + private static native int nGetMaxTextureSize(long nativeObject, int ordinal); + private static native int nGetMaxArrayTextureLayers(long nativeObject); private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat, int pixelDataType); diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index f87d18b4f6..476ec1557d 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -362,6 +362,8 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported) DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers) DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize) +DECL_DRIVER_API_SYNCHRONOUS_N(size_t, getMaxTextureSize, backend::SamplerType, target) +DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers) DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams) DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image) DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index b2c9173ce7..1cb97d0ca3 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1177,6 +1177,16 @@ size_t MetalDriver::getMaxUniformBufferSize() { return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec } +size_t MetalDriver::getMaxTextureSize(SamplerType) { + // TODO: return the actual size instead of hardcoding the minspec + return 2048; +} + +size_t MetalDriver::getMaxArrayTextureLayers() { + // TODO: return the actual size instead of hardcoding the minspec + return 256; +} + void MetalDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& data, uint32_t byteOffset) { FILAMENT_CHECK_PRECONDITION(data.buffer) diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 88b42a48a4..f78a8fa87a 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -240,6 +240,14 @@ size_t NoopDriver::getMaxUniformBufferSize() { return 16384u; } +size_t NoopDriver::getMaxTextureSize(SamplerType target) { + return 2048u; +} + +size_t NoopDriver::getMaxArrayTextureLayers() { + return 256u; +} + void NoopDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& p, uint32_t byteOffset) { scheduleDestroy(std::move(p)); diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index f652d92659..6e121f3f00 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -94,18 +94,22 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform, } #endif - OpenGLContext::initExtensions(&ext, state.major, state.minor); + initExtensions(&ext, state.major, state.minor); - OpenGLContext::initProcs(&procs, ext, state.major, state.minor); + initProcs(&procs, ext, state.major, state.minor); - OpenGLContext::initBugs(&bugs, ext, state.major, state.minor, + initBugs(&bugs, ext, state.major, state.minor, state.vendor, state.renderer, state.version, state.shader); glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units); + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gets.max_texture_size); + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gets.max_cubemap_texture_size); + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gets.max_3d_texture_size); + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gets.max_array_texture_layers); - mFeatureLevel = OpenGLContext::resolveFeatureLevel(state.major, state.minor, ext, gets, bugs); + mFeatureLevel = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs); #ifdef BACKEND_OPENGL_VERSION_GLES mShaderModel = ShaderModel::MOBILE; @@ -177,6 +181,14 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform, << gets.max_anisotropy << '\n' << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = " << gets.max_combined_texture_image_units << '\n' + << "GL_MAX_TEXTURE_SIZE = " + << gets.max_texture_size << '\n' + << "GL_MAX_CUBE_MAP_TEXTURE_SIZE = " + << gets.max_cubemap_texture_size << '\n' + << "GL_MAX_3D_TEXTURE_SIZE = " + << gets.max_3d_texture_size << '\n' + << "GL_MAX_ARRAY_TEXTURE_LAYERS = " + << gets.max_array_texture_layers << '\n' << "GL_MAX_DRAW_BUFFERS = " << gets.max_draw_buffers << '\n' << "GL_MAX_RENDERBUFFER_SIZE = " diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index 72e3f8f2b1..e83469c5ab 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -203,6 +203,10 @@ public: GLint max_renderbuffer_size; GLint max_samples; GLint max_texture_image_units; + GLint max_texture_size; + GLint max_cubemap_texture_size; + GLint max_3d_texture_size; + GLint max_array_texture_layers; GLint max_transform_feedback_separate_attribs; GLint max_uniform_block_size; GLint max_uniform_buffer_bindings; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 326a84b76b..9500c124c7 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -2460,6 +2460,26 @@ size_t OpenGLDriver::getMaxUniformBufferSize() { return mContext.gets.max_uniform_block_size; } +size_t OpenGLDriver::getMaxTextureSize(SamplerType target) { + switch (target) { + case SamplerType::SAMPLER_2D: + case SamplerType::SAMPLER_2D_ARRAY: + case SamplerType::SAMPLER_EXTERNAL: + return mContext.gets.max_texture_size; + case SamplerType::SAMPLER_CUBEMAP: + return mContext.gets.max_cubemap_texture_size; + case SamplerType::SAMPLER_3D: + return mContext.gets.max_3d_texture_size; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: + return mContext.gets.max_cubemap_texture_size; + } + return 0; +} + +size_t OpenGLDriver::getMaxArrayTextureLayers() { + return mContext.gets.max_array_texture_layers; +} + // ------------------------------------------------------------------------------------------------ // Swap chains // ------------------------------------------------------------------------------------------------ diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 7958c67def..e386b92421 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1130,6 +1130,16 @@ size_t VulkanDriver::getMaxUniformBufferSize() { return 32768; } +size_t VulkanDriver::getMaxTextureSize(SamplerType) { + // TODO: return the actual size instead of hardcoded value + return 2048; +} + +size_t VulkanDriver::getMaxArrayTextureLayers() { + // TODO: return the actual size instead of hardcoded value + return 256; +} + void VulkanDriver::setVertexBufferObject(Handle vbh, uint32_t index, Handle boh) { auto vb = resource_ptr::cast(&mResourceManager, vbh); diff --git a/filament/backend/src/webgpu/WebGPUDriver.cpp b/filament/backend/src/webgpu/WebGPUDriver.cpp index f399753dc5..c3eaeb7785 100644 --- a/filament/backend/src/webgpu/WebGPUDriver.cpp +++ b/filament/backend/src/webgpu/WebGPUDriver.cpp @@ -651,10 +651,18 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() { return MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; } -size_t WebGPUDriver::getMaxUniformBufferSize() { +size_t WebGPUDriver::getMaxUniformBufferSize(SamplerType) { return 16384u; } +size_t WebGPUDriver::getMaxTextureSize() { + return 2048u; +} + +size_t WebGPUDriver::getMaxArrayTextureLayers() { + return 256u; +} + void WebGPUDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& p, uint32_t byteOffset) { scheduleDestroy(std::move(p)); diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h index caac008472..3241d9a932 100644 --- a/filament/include/filament/Texture.h +++ b/filament/include/filament/Texture.h @@ -106,6 +106,13 @@ public: /** @return Whether a combination of texture format, pixel format and type is valid. */ static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept; + /** @return the maximum size in texels of a texture of type \p type. At least 2048 for + * 2D textures, 256 for 3D textures. */ + static size_t getMaxTextureSize(Engine& engine, Sampler type) noexcept; + + /** @return the maximum number of layers supported by texture arrays. At least 256. */ + static size_t getMaxArrayTextureLayers(Engine& engine) noexcept; + /** * Options for environment prefiltering into reflection map * diff --git a/filament/src/Texture.cpp b/filament/src/Texture.cpp index 02086855a6..9b00f8322b 100644 --- a/filament/src/Texture.cpp +++ b/filament/src/Texture.cpp @@ -108,4 +108,12 @@ bool Texture::validatePixelFormatAndType(InternalFormat internalFormat, Format f return FTexture::validatePixelFormatAndType(internalFormat, format, type); } +size_t Texture::getMaxTextureSize(Engine& engine, Sampler type) noexcept { + return FTexture::getMaxTextureSize(downcast(engine), type); + +} +size_t Texture::getMaxArrayTextureLayers(Engine& engine) noexcept { + return FTexture::getMaxArrayTextureLayers(downcast(engine)); +} + } // namespace filament diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 3749344d35..e9becb0ec7 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -164,6 +164,20 @@ Texture* Texture::Builder::build(Engine& engine) { (isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory) << "Texture is PROTECTED but protected textures are not supported"; + size_t const maxTextureDimension = getMaxTextureSize(engine, mImpl->mTarget); + size_t const maxTextureDepth = (mImpl->mTarget == Sampler::SAMPLER_2D_ARRAY || + mImpl->mTarget == Sampler::SAMPLER_CUBEMAP_ARRAY) + ? getMaxArrayTextureLayers(engine) + : maxTextureDimension; + + FILAMENT_CHECK_PRECONDITION( + mImpl->mWidth <= maxTextureDimension && + mImpl->mHeight <= maxTextureDimension && + mImpl->mDepth <= maxTextureDepth) << "Texture dimensions out of range: " + << "width= " << mImpl->mWidth << " (>" << maxTextureDimension << ")" + <<", height= " << mImpl->mHeight << " (>" << maxTextureDimension << ")" + << ", depth= " << mImpl->mDepth << " (>" << maxTextureDepth << ")"; + const auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool { switch (sampler) { case SamplerType::SAMPLER_2D: @@ -672,6 +686,14 @@ bool FTexture::isTextureSwizzleSupported(FEngine& engine) noexcept { return engine.getDriverApi().isTextureSwizzleSupported(); } +size_t FTexture::getMaxTextureSize(FEngine& engine, Sampler type) noexcept { + return engine.getDriverApi().getMaxTextureSize(type); +} + +size_t FTexture::getMaxArrayTextureLayers(FEngine& engine) noexcept { + return engine.getDriverApi().getMaxArrayTextureLayers(); +} + size_t FTexture::computeTextureDataSize(Format const format, Type const type, size_t const stride, size_t const height, size_t const alignment) noexcept { return PixelBufferDescriptor::computeDataSize(format, type, stride, height, alignment); diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index 7e463e7834..d5aa46bb4d 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -124,6 +124,10 @@ public: static bool validatePixelFormatAndType(backend::TextureFormat internalFormat, backend::PixelDataFormat format, backend::PixelDataType type) noexcept; + static size_t getMaxTextureSize(FEngine& engine, Sampler type) noexcept; + + static size_t getMaxArrayTextureLayers(FEngine& engine) noexcept; + bool textureHandleCanMutate() const noexcept; void updateLodRange(uint8_t level) noexcept;