fp32 texture formats are not necessarily filterable (#9855)
* fp32 texture formats are not necessarily filterable Mobile devices don't always support filtering of fp32 textures. This PR adds a query at the driver level. High precision EVSM is now conditional to this feature being supported. * use MTLDevice supports32BitFloatFiltering if available
This commit is contained in:
@@ -2115,9 +2115,10 @@ public class View {
|
||||
public int msaaSamples = 1;
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
* precision is rarely needed, but it does reduce light leaks as well as "fading"
|
||||
* of the shadows in some situations. Setting highPrecision to true for a single
|
||||
* shadow map will double the memory usage of all shadow maps.
|
||||
* This may not be supported on all mobile devices.
|
||||
*/
|
||||
public boolean highPrecision = false;
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,7 @@ class ostream;
|
||||
/**
|
||||
* Types and enums used by filament's driver.
|
||||
*
|
||||
* Effectively these types are public but should not be used directly. Instead use public classes
|
||||
* Effectively these types are public but should not be used directly. Instead, use public classes
|
||||
* internal redeclaration of these types.
|
||||
* For e.g. Use Texture::Sampler instead of filament::SamplerType.
|
||||
*/
|
||||
@@ -1147,6 +1147,19 @@ constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
//! returns whether this format a 32-bit float format
|
||||
constexpr bool isFp32ColorFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::R32F:
|
||||
case TextureFormat::RG32F:
|
||||
case TextureFormat::RGB32F:
|
||||
case TextureFormat::RGBA32F:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::STENCIL8:
|
||||
|
||||
@@ -398,6 +398,7 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, getPlatformSync, backend::SyncHandle, sh,
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isTextureSwizzleSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatMipmappable, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatFilterable, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isRenderTargetFormatSupported, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
|
||||
|
||||
@@ -1259,6 +1259,21 @@ bool MetalDriver::isTextureFormatMipmappable(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalDriver::isTextureFormatFilterable(TextureFormat format) {
|
||||
if (isFp32ColorFormat(format)) {
|
||||
if (@available(macOS 11.0, iOS 14.0, *)) {
|
||||
return mContext->device.supports32BitFloatFiltering;
|
||||
}
|
||||
return mContext->highestSupportedGpuFamily.apple >= 7 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 1;
|
||||
}
|
||||
if (isUnsignedIntFormat(format) || isSignedIntFormat(format) ||
|
||||
isDepthFormat(format) || isStencilFormat(format)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isRenderTargetFormatSupported(TextureFormat format) {
|
||||
MTLPixelFormat mtlFormat = getMetalFormat(mContext, format);
|
||||
// RGB9E5 isn't supported on Mac as a color render target.
|
||||
|
||||
@@ -194,6 +194,10 @@ bool NoopDriver::isTextureFormatMipmappable(TextureFormat format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isTextureFormatFilterable(TextureFormat format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isRenderTargetFormatSupported(TextureFormat format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -183,9 +183,16 @@ void GLDescriptorSet::update(OpenGLState& gl, HandleAllocatorGL& handleAllocator
|
||||
params.wrapT = SamplerWrapMode::CLAMP_TO_EDGE;
|
||||
params.wrapR = SamplerWrapMode::CLAMP_TO_EDGE;
|
||||
}
|
||||
|
||||
// GLES3.x specification forbids depth textures to be filtered.
|
||||
if (t && isDepthFormat(t->format)
|
||||
&& params.compareMode == SamplerCompareMode::NONE) {
|
||||
bool const depthTextureNoPcf = isDepthFormat(t->format) &&
|
||||
params.compareMode == SamplerCompareMode::NONE;
|
||||
|
||||
// GLES3.x may not support filtering of fp32 textures
|
||||
bool const fp32TextureNotFilterable = isFp32ColorFormat(t->format) &&
|
||||
!gl.context().ext.OES_texture_float_linear;
|
||||
|
||||
if (t && (depthTextureNoPcf || fp32TextureNotFilterable)) {
|
||||
params.filterMag = SamplerMagFilter::NEAREST;
|
||||
switch (params.filterMin) {
|
||||
case SamplerMinFilter::LINEAR:
|
||||
|
||||
@@ -675,6 +675,8 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->OES_EGL_image_external_essl3 = exts.has("GL_OES_EGL_image_external_essl3"sv);
|
||||
ext->OES_rgb8_rgba8 = exts.has("GL_OES_rgb8_rgba8"sv);
|
||||
ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv);
|
||||
ext->OES_texture_float_linear = exts.has("GL_OES_texture_float_linear"sv);
|
||||
ext->OES_texture_half_float_linear = exts.has("GL_OES_texture_half_float_linear"sv);
|
||||
ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv);
|
||||
ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv);
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
@@ -682,14 +684,15 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
|
||||
|
||||
// ES 3.2 implies EXT_color_buffer_float
|
||||
// ES 3.2 implies EXT_color_buffer_float (but not necessarily filterable)
|
||||
if (major > 3 || (major == 3 && minor >= 2)) {
|
||||
ext->EXT_color_buffer_float = true;
|
||||
}
|
||||
// ES 3.x implies EXT_discard_framebuffer and OES_vertex_array_object
|
||||
// ES 3.x implies EXT_discard_framebuffer, OES_vertex_array_object and OES_texture_half_float_linear
|
||||
if (major >= 3) {
|
||||
ext->EXT_discard_framebuffer = true;
|
||||
ext->OES_vertex_array_object = true;
|
||||
ext->OES_texture_half_float_linear = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,6 +743,8 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
ext->OES_EGL_image_external_essl3 = false;
|
||||
ext->OES_rgb8_rgba8 = true;
|
||||
ext->OES_standard_derivatives = true;
|
||||
ext->OES_texture_float_linear = true;
|
||||
ext->OES_texture_half_float_linear = true;
|
||||
ext->OES_texture_npot = true;
|
||||
ext->OES_vertex_array_object = true;
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
|
||||
@@ -208,6 +208,8 @@ public:
|
||||
bool OES_packed_depth_stencil;
|
||||
bool OES_rgb8_rgba8;
|
||||
bool OES_standard_derivatives;
|
||||
bool OES_texture_float_linear;
|
||||
bool OES_texture_half_float_linear;
|
||||
bool OES_texture_npot;
|
||||
bool OES_vertex_array_object;
|
||||
bool OVR_multiview2;
|
||||
|
||||
@@ -2775,6 +2775,39 @@ bool OpenGLDriver::isTextureFormatMipmappable(TextureFormat const format) {
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isTextureFormatFilterable(TextureFormat format) {
|
||||
auto const& gl = getBackendState();
|
||||
switch (format) {
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::RG8:
|
||||
case TextureFormat::RGB8:
|
||||
case TextureFormat::RGB565:
|
||||
case TextureFormat::RGBA4:
|
||||
case TextureFormat::RGB5_A1:
|
||||
case TextureFormat::RGBA8:
|
||||
case TextureFormat::RGBA8_SNORM:
|
||||
case TextureFormat::RGB10_A2:
|
||||
case TextureFormat::SRGB8:
|
||||
case TextureFormat::SRGB8_A8:
|
||||
case TextureFormat::R11F_G11F_B10F:
|
||||
case TextureFormat::RGB9_E5:
|
||||
return true;
|
||||
case TextureFormat::R16F:
|
||||
case TextureFormat::RG16F:
|
||||
case TextureFormat::RGB16F:
|
||||
case TextureFormat::RGBA16F:
|
||||
return gl.ext.OES_texture_half_float_linear;
|
||||
case TextureFormat::R32F:
|
||||
case TextureFormat::RG32F:
|
||||
case TextureFormat::RGB32F:
|
||||
case TextureFormat::RGBA32F:
|
||||
return gl.ext.OES_texture_float_linear;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isRenderTargetFormatSupported(TextureFormat const format) {
|
||||
// Supported formats per http://docs.gl/es3/glRenderbufferStorage, note that desktop OpenGL may
|
||||
// support more formats, but it requires querying GL_INTERNALFORMAT_SUPPORTED which is not
|
||||
|
||||
@@ -1548,6 +1548,16 @@ bool VulkanDriver::isTextureFormatMipmappable(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
bool VulkanDriver::isTextureFormatFilterable(TextureFormat format) {
|
||||
VkFormat vkformat = fvkutils::getVkFormat(format);
|
||||
if (vkformat == VK_FORMAT_UNDEFINED) {
|
||||
return false;
|
||||
}
|
||||
VkFormatProperties info;
|
||||
vkGetPhysicalDeviceFormatProperties(mPlatform->getPhysicalDevice(), vkformat, &info);
|
||||
return (info.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isRenderTargetFormatSupported(TextureFormat format) {
|
||||
VkFormat vkformat = fvkutils::getVkFormat(format);
|
||||
if (vkformat == VK_FORMAT_UNDEFINED) {
|
||||
|
||||
@@ -822,6 +822,17 @@ bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
|
||||
return WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(webGpuFormat);
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureFormatFilterable(TextureFormat format) {
|
||||
if (isFp32ColorFormat(format)) {
|
||||
return mDevice.HasFeature(wgpu::FeatureName::Float32Filterable);
|
||||
}
|
||||
if (isUnsignedIntFormat(format) || isSignedIntFormat(format) ||
|
||||
isDepthFormat(format) || isStencilFormat(format)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isRenderTargetFormatSupported(const TextureFormat format) {
|
||||
//todo
|
||||
return true;
|
||||
|
||||
@@ -728,9 +728,10 @@ struct VsmShadowOptions {
|
||||
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
* precision is rarely needed, but it does reduce light leaks as well as "fading"
|
||||
* of the shadows in some situations. Setting highPrecision to true for a single
|
||||
* shadow map will double the memory usage of all shadow maps.
|
||||
* This may not be supported on all mobile devices.
|
||||
*/
|
||||
bool highPrecision = false;
|
||||
|
||||
|
||||
@@ -106,6 +106,8 @@ FView::FView(FEngine& engine)
|
||||
{
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
mIsHighPrecisionEvsmSupported = driver.isTextureFormatFilterable(TextureFormat::RGBA32F);
|
||||
|
||||
mFeatureLevel = engine.getSupportedFeatureLevel();
|
||||
|
||||
auto const& layout = engine.getPerRenderableDescriptorSetLayout();
|
||||
@@ -1483,6 +1485,9 @@ void FView::setAmbientOcclusionOptions(AmbientOcclusionOptions options) noexcept
|
||||
}
|
||||
void FView::setVsmShadowOptions(VsmShadowOptions options) noexcept {
|
||||
options.msaaSamples = std::max(uint8_t(0), options.msaaSamples);
|
||||
if (!mIsHighPrecisionEvsmSupported) {
|
||||
options.highPrecision = false;
|
||||
}
|
||||
mVsmShadowOptions = options;
|
||||
}
|
||||
|
||||
|
||||
@@ -586,6 +586,7 @@ private:
|
||||
bool mCulling = true;
|
||||
bool mFrontFaceWindingInverted = false;
|
||||
bool mIsTransparentPickingEnabled = false;
|
||||
bool mIsHighPrecisionEvsmSupported = true;
|
||||
|
||||
FRenderTarget* mRenderTarget = nullptr;
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
MOCK_METHOD(bool, isTextureFormatSupported, (backend::TextureFormat format), (override));
|
||||
MOCK_METHOD(bool, isTextureSwizzleSupported, (), (override));
|
||||
MOCK_METHOD(bool, isTextureFormatMipmappable, (backend::TextureFormat format), (override));
|
||||
MOCK_METHOD(bool, isTextureFormatFilterable, (backend::TextureFormat format), (override));
|
||||
MOCK_METHOD(bool, isRenderTargetFormatSupported, (backend::TextureFormat format), (override));
|
||||
MOCK_METHOD(bool, isFrameBufferFetchSupported, (), (override));
|
||||
MOCK_METHOD(bool, isFrameBufferFetchMultiSampleSupported, (), (override));
|
||||
|
||||
3
web/filament-js/filament.d.ts
vendored
3
web/filament-js/filament.d.ts
vendored
@@ -1741,9 +1741,10 @@ export interface View$VsmShadowOptions {
|
||||
msaaSamples?: number;
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
* precision is rarely needed, but it does reduce light leaks as well as "fading"
|
||||
* of the shadows in some situations. Setting highPrecision to true for a single
|
||||
* shadow map will double the memory usage of all shadow maps.
|
||||
* This may not be supported on all mobile devices.
|
||||
*/
|
||||
highPrecision?: boolean;
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user