new feature level API for backends (#5784)

* new feature level API for backends

backend can now return a "feature level", each level corresponds to a
"bundle" of features.
Level1: ES3.0 capabilities
Level2: ES3.1 capabilities + 31 textures + cubemap arrays

Currently metal always returns level 1, GL and Vulkan return level 2
if 31 textures or more are supported.

* Add public APIs for feature levels

* Add infrastructure to check feature levels in materials

* validate material feature level on use

The validation is done when creating a renderable. If the engine doesn't
support the material's feature level, an exception is thrown (or assert
if exceptions are not enabled).

* material documentation

* activate ESSL 3.10 for feature level 2

also generate #defines to identify available feature levels

* support for cubemap arrays in the public API


if feature level 2 is supported, cubemap arrays can be used from the
public API.

* add release notes
This commit is contained in:
Mathias Agopian
2022-08-17 10:14:26 -07:00
committed by GitHub
parent 7c092a8714
commit b2ec57776d
40 changed files with 513 additions and 114 deletions
+2
View File
@@ -5,6 +5,8 @@ A new header is inserted each time a *tag* is created.
## main branch
- engine: new feature level APIs, see `Engine::getSupportedFeatureLevel()`
## v1.25.6
- Add CONFIG_MINSPEC_UBO_SIZE as a nicer way to allow exceeding the ES3.0 minspec.
@@ -328,3 +328,24 @@ Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, j
Engine* engine = (Engine*) nativeEngine;
return (jboolean)engine->isAutomaticInstancingEnabled();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Engine_nGetSupportedFeatureLevel(JNIEnv *, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jint)engine->getSupportedFeatureLevel();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Engine_nSetActiveFeatureLevel(JNIEnv *, jclass,
jlong nativeEngine, jint ordinal) {
Engine* engine = (Engine*) nativeEngine;
return (jint)engine->setActiveFeatureLevel((Engine::FeatureLevel)ordinal);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Engine_nGetActiveFeatureLevel(JNIEnv *, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jint)engine->getActiveFeatureLevel();
}
@@ -47,7 +47,7 @@ import com.google.android.filament.proguard.UsedByReflection;
* <pre>
* import com.google.android.filament.*
*
* Engin engine = Engine.create();
* Engine engine = Engine.create();
* SwapChain swapChain = engine.createSwapChain(nativeWindow);
* Renderer renderer = engine.createRenderer();
* Scene scene = engine.createScene();
@@ -107,6 +107,7 @@ import com.google.android.filament.proguard.UsedByReflection;
*/
public class Engine {
private static final Backend[] sBackendValues = Backend.values();
private static final FeatureLevel[] sFeatureLevelValues = FeatureLevel.values();
private long mNativeObject;
@@ -141,6 +142,18 @@ public class Engine {
NOOP,
}
/**
* Defines the backend's feature levels.
*/
public enum FeatureLevel {
/** Reserved, don't use */
FEATURE_LEVEL_0,
/** OpenGL ES 3.0 features (default) */
FEATURE_LEVEL_1,
/** OpenGL ES 3.1 features + 31 textures units + cubemap arrays */
FEATURE_LEVEL_2
};
private Engine(long nativeEngine) {
mNativeObject = nativeEngine;
mTransformManager = new TransformManager(nGetTransformManager(nativeEngine));
@@ -269,6 +282,50 @@ public class Engine {
getTransformManager().setAccurateTranslationsEnabled(true);
}
/**
* Query the feature level supported by the selected backend.
*
* A specific feature level needs to be set before the corresponding features can be used.
*
* @return FeatureLevel supported the selected backend.
* @see #setActiveFeatureLevel
*/
@NonNull
public FeatureLevel getSupportedFeatureLevel() {
return sFeatureLevelValues[(int) nGetSupportedFeatureLevel(getNativeObject())];
}
/**
* Activate all features of a given feature level. By default FeatureLevel::FEATURE_LEVEL_1 is
* active. The selected feature level must not be higher than the value returned by
* getActiveFeatureLevel() and it's not possible lower the active feature level.
*
* @param featureLevel the feature level to activate. If featureLevel is lower than
* getActiveFeatureLevel(), the current (higher) feature level is kept.
* If featureLevel is higher than getSupportedFeatureLevel(), an exception
* is thrown, or the program is terminated if exceptions are disabled.
*
* @return the active feature level.
*
* @see #getSupportedFeatureLevel
* @see #getActiveFeatureLevel
*/
@NonNull
public FeatureLevel setActiveFeatureLevel(@NonNull FeatureLevel featureLevel) {
return sFeatureLevelValues[(int) nSetActiveFeatureLevel(getNativeObject(), featureLevel.ordinal())];
}
/**
* Returns the currently active feature level.
* @return currently active feature level
* @see #getSupportedFeatureLevel
* @see #setActiveFeatureLevel
*/
@NonNull
public FeatureLevel getActiveFeatureLevel() {
return sFeatureLevelValues[(int) nGetActiveFeatureLevel(getNativeObject())];
}
/**
* Enables or disables automatic instancing of render primitives. Instancing of render primitive
* can greatly reduce CPU overhead but requires the instanced primitives to be identical
@@ -289,7 +346,7 @@ public class Engine {
/**
* @return true if automatic instancing is enabled, false otherwise.
* @see setAutomaticInstancingEnabled
* @see #setAutomaticInstancingEnabled
*/
public boolean isAutomaticInstancingEnabled() {
return nIsAutomaticInstancingEnabled(getNativeObject());
@@ -760,4 +817,7 @@ public class Engine {
private static native long nGetEntityManager(long nativeEngine);
private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable);
private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine);
private static native int nGetSupportedFeatureLevel(long nativeEngine);
private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal);
private static native int nGetActiveFeatureLevel(long nativeEngine);
}
+30
View File
@@ -932,6 +932,36 @@ material {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### General: featureLevel
Type
: `number`
Value
: An integer value, either 1 or 2. Defaults to 1.
Feature Level | Guaranteed features
:----------------------|:---------------------------------
1 | 9 textures per material
2 | 12 textures per material, cubemap arrays, ESSL 3.10
[Table [featureLevels]: Feature levels]
Description
: Sets the feature level of the material. Each feature level defines a set of features the
material can use. If the material uses a feature not supported by the selected level, `matc`
will generate an error during compilation. A given feature level is guaranteed to support
all features of lower feature levels.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
featureLevel : 2
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bugs
: `matc` doesn't verify that a material is not using features above its selected feature level.
### General: shadingModel
Type
@@ -59,6 +59,14 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
static constexpr size_t CONFIG_BINDING_COUNT = 12; // This is guaranteed by OpenGL ES.
/**
* Defines the backend's feature levels.
*/
enum class FeatureLevel : uint8_t {
FEATURE_LEVEL_1 = 1, //!< OpenGL ES 3.0 features (default)
FEATURE_LEVEL_2 //!< OpenGL ES 3.1 features + 31 textures units + cubemap arrays
};
/**
* Selects which driver a particular Engine should use.
*/
@@ -304,6 +304,7 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, cancelExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::SyncStatus, getSyncStatus, backend::SyncHandle, sh)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
DECL_DRIVER_API_SYNCHRONOUS_0(backend::FeatureLevel, getFeatureLevel)
/*
* Updating driver objects
@@ -688,6 +688,13 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
}
FeatureLevel MetalDriver::getFeatureLevel() {
// TODO: before we can return FEATURE_LEVEL_2 we need to decouple the samplers from
// the textures. Metal only supports 16 samplers, but at least 31 textures on all
// hardware.
return FeatureLevel::FEATURE_LEVEL_1;
}
math::float2 MetalDriver::getClipSpaceParams() {
// virtual and physical z-coordinate of clip-space is in [-w, 0]
// Note: this is actually never used (see: main.vs), but it's a backend API so we implement it
+4
View File
@@ -177,6 +177,10 @@ bool NoopDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
}
FeatureLevel NoopDriver::getFeatureLevel() {
return FeatureLevel::FEATURE_LEVEL_1;
}
math::float2 NoopDriver::getClipSpaceParams() {
return math::float2{ 1.0f, 0.0f };
}
+49 -30
View File
@@ -40,7 +40,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
OpenGLContext::OpenGLContext() noexcept {
state.vao.p = &mDefaultVAO;
// There query work with all GL/GLES versions!
// These queries work with all GL/GLES versions!
state.vendor = (char const*)glGetString(GL_VENDOR);
state.renderer = (char const*)glGetString(GL_RENDERER);
state.version = (char const*)glGetString(GL_VERSION);
@@ -55,26 +55,56 @@ OpenGLContext::OpenGLContext() noexcept {
queryOpenGLVersion(&state.major, &state.minor);
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gets.max_uniform_block_size);
glGetIntegerv(GL_MAX_SAMPLES, &gets.max_samples);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gets.max_draw_buffers);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &gets.uniform_buffer_offset_alignment);
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
// This works on all versions of GLES
if (state.major == 3 && state.minor >= 0) {
mShaderModel = ShaderModel::MOBILE;
}
if (state.major == 3 && state.minor >= 1) {
features.multisample_texture = true;
}
initExtensionsGLES();
if (state.major == 3) {
assert_invariant(gets.max_texture_image_units >= 16);
if (state.minor >= 0) {
mShaderModel = ShaderModel::MOBILE;
}
if (state.minor >= 1) {
features.multisample_texture = true;
// figure out our feature level
if (gets.max_texture_image_units >= 31 &&
ext.EXT_texture_cube_map_array) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
}
}
}
} else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// OpenGL version
if (state.major == 4 && state.minor >= 1) {
mShaderModel = ShaderModel::DESKTOP;
initExtensionsGL();
if (state.major == 4) {
if (state.minor >= 1) {
mShaderModel = ShaderModel::DESKTOP;
}
if (state.minor >= 3) {
// figure out our feature level
if (gets.max_texture_image_units >= 31) {
// cubemap arrays are available as of OpenGL 4.0
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
}
}
features.multisample_texture = true;
}
features.multisample_texture = true;
// feedback loops are allowed on GL desktop as long as writes are disabled
bugs.allow_read_only_ancillary_feedback_loop = true;
initExtensionsGL();
assert_invariant(gets.max_texture_image_units >= 16);
}
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy);
}
#endif
/*
* Figure out which driver bugs we need to workaround
*/
@@ -192,18 +222,6 @@ OpenGLContext::OpenGLContext() noexcept {
}
flush(slog.v);
// now we can query getter and features
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gets.max_uniform_block_size);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &gets.uniform_buffer_offset_alignment);
glGetIntegerv(GL_MAX_SAMPLES, &gets.max_samples);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gets.max_draw_buffers);
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy);
}
#endif
assert_invariant(gets.max_draw_buffers >= 4); // minspec
#ifndef NDEBUG
@@ -213,6 +231,7 @@ OpenGLContext::OpenGLContext() noexcept {
<< "GL_MAX_SAMPLES = " << gets.max_samples << '\n'
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = " << gets.max_anisotropy << '\n'
<< "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size << '\n'
<< "GL_MAX_TEXTURE_IMAGE_UNITS = " << gets.max_texture_image_units << '\n'
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = " << gets.uniform_buffer_offset_alignment << '\n'
;
flush(slog.v);
@@ -328,18 +347,18 @@ void OpenGLContext::initExtensionsGLES() noexcept {
#if !defined(__EMSCRIPTEN__)
ext.EXT_texture_compression_etc2 = true;
#endif
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
ext.EXT_texture_cube_map_array = exts.has("GL_EXT_texture_cube_map_array"sv) || exts.has("GL_OES_texture_cube_map_array"sv);
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
ext.KHR_debug = exts.has("GL_KHR_debug"sv);
ext.KHR_texture_compression_astc_hdr = exts.has("GL_KHR_texture_compression_astc_hdr"sv);
ext.KHR_texture_compression_astc_ldr = exts.has("GL_KHR_texture_compression_astc_ldr"sv);
ext.OES_EGL_image_external_essl3 = exts.has("GL_OES_EGL_image_external_essl3"sv);
ext.QCOM_tiled_rendering = exts.has("GL_QCOM_tiled_rendering"sv);
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
ext.WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
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);
ext.WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
// ES 3.2 implies EXT_color_buffer_float
if (state.major >= 3 && state.minor >= 2) {
ext.EXT_color_buffer_float = true;
@@ -371,6 +390,8 @@ void OpenGLContext::initExtensionsGL() noexcept {
ext.EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
ext.EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
ext.EXT_texture_compression_etc2 = exts.has("GL_ARB_ES3_compatibility"sv);
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
ext.EXT_texture_sRGB = exts.has("GL_EXT_texture_sRGB"sv);
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
@@ -378,8 +399,6 @@ void OpenGLContext::initExtensionsGL() noexcept {
ext.KHR_texture_compression_astc_hdr = exts.has("GL_KHR_texture_compression_astc_hdr"sv);
ext.KHR_texture_compression_astc_ldr = exts.has("GL_KHR_texture_compression_astc_ldr"sv);
ext.OES_EGL_image_external_essl3 = exts.has("GL_OES_EGL_image_external_essl3"sv);
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
}
void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
+7 -1
View File
@@ -125,6 +125,7 @@ public:
GLint max_renderbuffer_size;
GLint max_samples;
GLint max_uniform_block_size;
GLint max_texture_image_units;
GLint uniform_buffer_offset_alignment;
} gets = {};
@@ -150,6 +151,7 @@ public:
bool EXT_texture_compression_etc2 = false;
bool EXT_texture_compression_s3tc = false;
bool EXT_texture_compression_s3tc_srgb = false;
bool EXT_texture_cube_map_array = false;
bool EXT_texture_filter_anisotropic = false;
bool EXT_texture_sRGB = false;
bool GOOGLE_cpp_style_line_directive = false;
@@ -210,7 +212,8 @@ public:
// GL and Vulkan and probably Metal.
bool allow_read_only_ancillary_feedback_loop = false;
// Some Adreno drivers would crash gl draw when there's uninitialized uniform array exists, even when the code using that uniform array is not called. it's needed to do an initialization to avoid it.
// Some Adreno drivers crash in glDrawXXX() when there's an uninitialized uniform block,
// even when the shader doesn't access it.
bool enable_initialize_non_used_uniform_array = false;
} bugs;
@@ -224,6 +227,8 @@ public:
}
void resetProgram() noexcept { state.program.use = 0; }
FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
// Try to keep the State structure sorted by data-access patterns
struct State {
GLint major = 0;
@@ -343,6 +348,7 @@ public:
private:
ShaderModel mShaderModel = ShaderModel::MOBILE;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
{ bugs.disable_glFlush,
@@ -1633,6 +1633,10 @@ bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
}
FeatureLevel OpenGLDriver::getFeatureLevel() {
return mContext.getFeatureLevel();
}
math::float2 OpenGLDriver::getClipSpaceParams() {
return mContext.ext.EXT_clip_control ?
// z-coordinate of virtual and physical clip-space is in [-w, 0]
+13 -1
View File
@@ -884,9 +884,21 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
}
FeatureLevel VulkanDriver::getFeatureLevel() {
const auto supportedSamplerCount = std::min(
mContext.physicalDeviceProperties.limits.maxDescriptorSetSamplers,
mContext.physicalDeviceProperties.limits.maxDescriptorSetSampledImages);
const bool imageCubeArray = (bool)mContext.physicalDeviceFeatures.imageCubeArray;
return (supportedSamplerCount >= 31 && imageCubeArray) ?
FeatureLevel::FEATURE_LEVEL_2 :
FeatureLevel::FEATURE_LEVEL_1;
}
math::float2 VulkanDriver::getClipSpaceParams() {
// virtual and physical z-coordinate of clip-space is in [-w, 0]
// Note: this is actually never used (see: main.vs), but it's a backend API so we implement it
// Note: this is actually never used (see: main.vs), but it's a backend API, so we implement it
// properly.
return math::float2{ 1.0f, 0.0f };
}
@@ -695,7 +695,7 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& cmdbuffer)
++mCurrentTime;
// The Vulkan spec says: "When a command buffer begins recording, all state in that command
// buffer is undefined." Therefore we need to clear all bindings at this time.
// buffer is undefined." Therefore, we need to clear all bindings at this time.
mBoundPipeline = {};
mBoundLayout = {};
mBoundDescriptor = {};
+39
View File
@@ -403,6 +403,45 @@ public:
*/
static void destroy(Engine* engine);
using FeatureLevel = backend::FeatureLevel;
/**
* Query the feature level supported by the selected backend.
*
* A specific feature level needs to be set before the corresponding features can be used.
*
* @return FeatureLevel supported the selected backend.
* @see setActiveFeatureLevel
*/
FeatureLevel getSupportedFeatureLevel() const noexcept;
/**
* Activate all features of a given feature level. By default FeatureLevel::FEATURE_LEVEL_1 is
* active. The selected feature level must not be higher than the value returned by
* getActiveFeatureLevel() and it's not possible lower the active feature level.
*
* @param featureLevel the feature level to activate. If featureLevel is lower than
* getActiveFeatureLevel(), the current (higher) feature level is kept.
* If featureLevel is higher than getSupportedFeatureLevel(), an exception
* is thrown, or the program is terminated if exceptions are disabled.
*
* @return the active feature level.
*
* @see getSupportedFeatureLevel
* @see getActiveFeatureLevel
*/
FeatureLevel setActiveFeatureLevel(FeatureLevel featureLevel);
/**
* Returns the currently active feature level.
* @return currently active feature level
* @see getSupportedFeatureLevel
* @see setActiveFeatureLevel
*/
FeatureLevel getActiveFeatureLevel() const noexcept;
/**
* @return EntityManager used by filament
*/
+18 -5
View File
@@ -40,7 +40,6 @@ namespace filament {
class BufferObject;
class Engine;
class IndexBuffer;
class Material;
class MaterialInstance;
class Renderer;
class SkinningBuffer;
@@ -161,10 +160,17 @@ public:
/**
* Binds a material instance to the specified primitive.
*
* If no material is specified for a given primitive, Filament will fall back to a basic default material.
* If no material is specified for a given primitive, Filament will fall back to a basic
* default material.
*
* @param index zero-based index of the primitive, must be less than the count passed to Builder constructor
* The MaterialInstance's material must have a feature level equal or lower to the engine's
* selected feature level.
*
* @param index zero-based index of the primitive, must be less than the count passed to
* Builder constructor
* @param materialInstance the material to bind
*
* @see Engine::setActiveFeatureLevel
*/
Builder& material(size_t index, MaterialInstance const* materialInstance) noexcept;
@@ -590,10 +596,17 @@ public:
/**
* Changes the material instance binding for the given primitive.
*
* \see Builder::material()
* The MaterialInstance's material must have a feature level equal or lower to the engine's
* selected feature level.
*
* @exception utils::PreConditionPanic if the engine doesn't support the material's
* feature level.
*
* @see Builder::material()
* @see Engine::setActiveFeatureLevel
*/
void setMaterialInstanceAt(Instance instance,
size_t primitiveIndex, MaterialInstance const* materialInstance) noexcept;
size_t primitiveIndex, MaterialInstance const* materialInstance);
/**
* Retrieves the material instance that is bound to the given primitive.
+12
View File
@@ -261,4 +261,16 @@ bool Engine::isAutomaticInstancingEnabled() const noexcept {
return upcast(this)->isAutomaticInstancingEnabled();
}
FeatureLevel Engine::getSupportedFeatureLevel() const noexcept {
return upcast(this)->getSupportedFeatureLevel();
}
FeatureLevel Engine::setActiveFeatureLevel(FeatureLevel featureLevel) {
return upcast(this)->setActiveFeatureLevel(featureLevel);
}
FeatureLevel Engine::getActiveFeatureLevel() const noexcept {
return upcast(this)->getActiveFeatureLevel();
}
} // namespace filament
+4
View File
@@ -118,6 +118,10 @@ bool MaterialParser::getMaterialVersion(uint32_t* value) const noexcept {
return mImpl.getFromSimpleChunk(ChunkType::MaterialVersion, value);
}
bool MaterialParser::getFeatureLevel(uint8_t* value) const noexcept {
return mImpl.getFromSimpleChunk(ChunkType::MaterialFeatureLevel, value);
}
bool MaterialParser::getName(utils::CString* cstring) const noexcept {
ChunkType type = ChunkType::MaterialName;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
+1
View File
@@ -58,6 +58,7 @@ public:
// Accessors
bool getMaterialVersion(uint32_t* value) const noexcept;
bool getFeatureLevel(uint8_t* value) const noexcept;
bool getName(utils::CString*) const noexcept;
bool getUIB(UniformInterfaceBlock* uib) const noexcept;
bool getSIB(SamplerInterfaceBlock* sib) const noexcept;
+1 -1
View File
@@ -89,7 +89,7 @@ size_t RenderableManager::getPrimitiveCount(Instance instance) const noexcept {
}
void RenderableManager::setMaterialInstanceAt(Instance instance,
size_t primitiveIndex, MaterialInstance const* materialInstance) noexcept {
size_t primitiveIndex, MaterialInstance const* materialInstance) {
upcast(this)->setMaterialInstanceAt(instance, 0, primitiveIndex, upcast(materialInstance));
}
+16 -3
View File
@@ -251,6 +251,11 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
continue;
}
// we want a feature level violation to be a hard error (exception if enabled, or crash)
ASSERT_PRECONDITION(upcast(engine).hasFeatureLevel(material->getFeatureLevel()),
"Material \"%s\" has feature level %u which is not supported by this Engine",
material->getName().c_str_safe(), (uint8_t)material->getFeatureLevel());
// reject invalid geometry parameters
ASSERT_PRECONDITION(entry.offset + entry.count <= entry.indices->getIndexCount(),
"[entity=%u, primitive @ %u] offset (%u) + count (%u) > indexCount (%u)",
@@ -514,12 +519,20 @@ void FRenderableManager::destroyComponentMorphTargets(FEngine& engine,
}
void FRenderableManager::setMaterialInstanceAt(Instance instance, uint8_t level,
size_t primitiveIndex, FMaterialInstance const* mi) noexcept {
size_t primitiveIndex, FMaterialInstance const* mi) {
if (instance) {
Slice<FRenderPrimitive>& primitives = getRenderPrimitives(instance, level);
if (primitiveIndex < primitives.size()) {
primitives[primitiveIndex].setMaterialInstance(upcast(mi));
AttributeBitset required = mi->getMaterial()->getRequiredAttributes();
assert_invariant(mi);
FMaterial const* material = mi->getMaterial();
// we want a feature level violation to be a hard error (exception if enabled, or crash)
ASSERT_PRECONDITION(mEngine.hasFeatureLevel(material->getFeatureLevel()),
"Material \"%s\" has feature level %u which is not supported by this Engine",
material->getName().c_str_safe(), (uint8_t)material->getFeatureLevel());
primitives[primitiveIndex].setMaterialInstance(mi);
AttributeBitset required = material->getRequiredAttributes();
AttributeBitset declared = primitives[primitiveIndex].getEnabledAttributes();
if (UTILS_UNLIKELY((declared & required) != required)) {
slog.w << "[instance=" << instance.asValue() << ", primitive @ " << primitiveIndex
+1 -1
View File
@@ -161,7 +161,7 @@ public:
inline size_t getLevelCount(Instance) const noexcept { return 1; }
inline size_t getPrimitiveCount(Instance instance, uint8_t level) const noexcept;
void setMaterialInstanceAt(Instance instance, uint8_t level,
size_t primitiveIndex, FMaterialInstance const* materialInstance) noexcept;
size_t primitiveIndex, FMaterialInstance const* materialInstance);
MaterialInstance* getMaterialInstanceAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
void setGeometryAt(Instance instance, uint8_t level, size_t primitiveIndex,
PrimitiveType type, FVertexBuffer* vertices, FIndexBuffer* indices,
+14 -1
View File
@@ -264,6 +264,8 @@ void FEngine::init() {
DriverApi& driverApi = getDriverApi();
slog.i << "FEngine feature level: " << int(driverApi.getFeatureLevel()) << io::endl;
mResourceAllocator = new ResourceAllocator(driverApi);
mFullScreenTriangleVb = upcast(VertexBuffer::Builder()
@@ -588,7 +590,7 @@ int FEngine::loop() {
mPlatform = DefaultPlatform::create(&mBackend);
mOwnPlatform = true;
const char* const backend = backendToString(mBackend);
slog.d << "FEngine resolved backend: " << backend << io::endl;
slog.i << "FEngine resolved backend: " << backend << io::endl;
if (mPlatform == nullptr) {
slog.e << "Selected backend not supported in this build." << io::endl;
mDriverBarrier.latch();
@@ -1059,4 +1061,15 @@ void FEngine::destroy(FEngine* engine) {
}
}
Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
FEngine::DriverApi& driver = const_cast<FEngine*>(this)->getDriverApi();
return driver.getFeatureLevel();
}
Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
ASSERT_PRECONDITION(featureLevel <= getSupportedFeatureLevel(),
"Feature level %u not supported", (unsigned)featureLevel);
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
}
} // namespace filament
+13
View File
@@ -175,6 +175,14 @@ public:
return mUvFromClipMatrix;
}
FeatureLevel getSupportedFeatureLevel() const noexcept;
FeatureLevel setActiveFeatureLevel(FeatureLevel featureLevel);
FeatureLevel getActiveFeatureLevel() const noexcept {
return mActiveFeatureLevel;
}
PostProcessManager const& getPostProcessManager() const noexcept {
return mPostProcessManager;
}
@@ -362,6 +370,10 @@ public:
size_t getRequestedDriverHandleArenaSize() const noexcept { return mConfig.driverHandleArenaSizeMB * MiB; }
Config const& getConfig() const noexcept { return mConfig; }
bool hasFeatureLevel(backend::FeatureLevel featureLevel) const noexcept {
return featureLevel <= mActiveFeatureLevel;
}
private:
static Config validateConfig(const Config* pConfig) noexcept;
@@ -388,6 +400,7 @@ private:
backend::Handle<backend::HwRenderTarget> mDefaultRenderTarget;
Backend mBackend;
FeatureLevel mActiveFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
Platform* mPlatform = nullptr;
bool mOwnPlatform = false;
bool mAutomaticInstancingEnabled = false;
+25 -8
View File
@@ -45,12 +45,13 @@ namespace filament {
using namespace backend;
static MaterialParser* createParser(Backend backend, const void* data, size_t size) {
MaterialParser* materialParser = new MaterialParser(backend, data, size);
// unique_ptr so we don't leak MaterialParser on failures below
auto materialParser = std::make_unique<MaterialParser>(backend, data, size);
MaterialParser::ParseResult materialResult = materialParser->parse();
if (backend == Backend::NOOP) {
return materialParser;
return materialParser.release();
}
ASSERT_PRECONDITION(materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND,
@@ -61,12 +62,12 @@ static MaterialParser* createParser(Backend backend, const void* data, size_t si
uint32_t version = 0;
materialParser->getMaterialVersion(&version);
ASSERT_PRECONDITION(version == MATERIAL_VERSION, "Material version mismatch. Expected %d but "
"received %d.", MATERIAL_VERSION, version);
ASSERT_PRECONDITION(version == MATERIAL_VERSION,
"Material version mismatch. Expected %d but received %d.", MATERIAL_VERSION, version);
assert_invariant(backend != Backend::DEFAULT && "Default backend has not been resolved.");
return materialParser;
return materialParser.release();
}
struct Material::BuilderDetails {
@@ -95,8 +96,12 @@ Material::Builder& Material::Builder::package(const void* payload, size_t size)
}
Material* Material::Builder::build(Engine& engine) {
MaterialParser* materialParser = createParser(
upcast(engine).getBackend(), mImpl->mPayload, mImpl->mSize);
std::unique_ptr<MaterialParser> materialParser{ createParser(
upcast(engine).getBackend(), mImpl->mPayload, mImpl->mSize) };
if (materialParser == nullptr) {
return nullptr;
}
uint32_t v = 0;
materialParser->getShaderModels(&v);
@@ -117,7 +122,7 @@ Material* Material::Builder::build(Engine& engine) {
return nullptr;
}
mImpl->mMaterialParser = materialParser;
mImpl->mMaterialParser = materialParser.release();
return upcast(engine).createMaterial(*this);
}
@@ -150,6 +155,17 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
UTILS_UNUSED_IN_RELEASE bool nameOk = parser->getName(&mName);
assert_invariant(nameOk);
uint8_t featureLevel = 1;
parser->getFeatureLevel(&featureLevel);
assert_invariant(featureLevel <= 2);
mFeatureLevel = [featureLevel]() -> FeatureLevel {
switch (featureLevel) {
default:
case 1: return FeatureLevel::FEATURE_LEVEL_1;
case 2: return FeatureLevel::FEATURE_LEVEL_2;
}
}();
UTILS_UNUSED_IN_RELEASE bool sibOK = parser->getSIB(&mSamplerInterfaceBlock);
assert_invariant(sibOK);
@@ -342,6 +358,7 @@ UniformInterfaceBlock::UniformInfo const* FMaterial::reflect(
}
void FMaterial::prepareProgramSlow(Variant variant) const noexcept {
assert_invariant(mEngine.hasFeatureLevel(mFeatureLevel));
switch (getMaterialDomain()) {
case MaterialDomain::SURFACE:
getSurfaceProgramSlow(variant);
+2
View File
@@ -105,6 +105,7 @@ public:
bool isVariantLit() const noexcept { return mIsVariantLit; }
const utils::CString& getName() const noexcept { return mName; }
backend::FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
backend::RasterState getRasterState() const noexcept { return mRasterState; }
uint32_t getId() const noexcept { return mMaterialId; }
@@ -196,6 +197,7 @@ private:
BlendingMode mRenderBlendingMode = BlendingMode::OPAQUE;
TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT;
bool mIsVariantLit = false;
backend::FeatureLevel mFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1;
Shading mShading = Shading::UNLIT;
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
+24 -15
View File
@@ -208,7 +208,7 @@ void FTexture::setImage(FEngine& engine, size_t level,
uint32_t width, uint32_t height, uint32_t depth,
FTexture::PixelBufferDescriptor&& buffer) const {
auto validateTarget = [](SamplerType sampler) -> bool {
auto validateTarget = [&engine](SamplerType sampler) -> bool {
switch (sampler) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_3D:
@@ -218,7 +218,7 @@ void FTexture::setImage(FEngine& engine, size_t level,
case SamplerType::SAMPLER_EXTERNAL:
return false;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return false; // feature level 2
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_2);
}
};
@@ -254,19 +254,15 @@ void FTexture::setImage(FEngine& engine, size_t level,
// can't happen by construction, fallthrough...
case SamplerType::SAMPLER_2D:
assert_invariant(mDepth == 1);
textureDepthOrLayers = mDepth;
textureDepthOrLayers = 1;
break;
case SamplerType::SAMPLER_3D:
textureDepthOrLayers = valueForLevel(level, mDepth);
break;
case SamplerType::SAMPLER_2D_ARRAY:
textureDepthOrLayers = mDepth;
break;
case SamplerType::SAMPLER_CUBEMAP:
textureDepthOrLayers = 6;
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
textureDepthOrLayers = mDepth * 6;
textureDepthOrLayers = mDepth;
break;
}
@@ -315,14 +311,27 @@ void FTexture::setImage(FEngine& engine, size_t level,
assert_invariant(w == h);
const size_t faceSize = PixelBufferDescriptor::computeDataSize(buffer.format, buffer.type,
buffer.stride ? buffer.stride : w, h, buffer.alignment);
UTILS_NOUNROLL
for (size_t face = 0; face < 6; face++) {
engine.getDriverApi().update3DImage(mHandle, uint8_t(level), 0, 0, face, w, h, 1, {
(char*)buffer.buffer + faceOffsets[face],
faceSize, buffer.format, buffer.type, buffer.alignment,
buffer.left, buffer.top, buffer.stride });
if (faceOffsets[0] == 0 &&
faceOffsets[1] == 1 * faceSize &&
faceOffsets[2] == 2 * faceSize &&
faceOffsets[3] == 3 * faceSize &&
faceOffsets[4] == 4 * faceSize &&
faceOffsets[5] == 5 * faceSize) {
// in this special case, we can upload all 6 faces in one call
engine.getDriverApi().update3DImage(mHandle, uint8_t(level),
0, 0, 0, w, h, 6, std::move(buffer));
} else {
UTILS_NOUNROLL
for (size_t face = 0; face < 6; face++) {
engine.getDriverApi().update3DImage(mHandle, uint8_t(level), 0, 0, face, w, h, 1, {
(char*)buffer.buffer + faceOffsets[face],
faceSize, buffer.format, buffer.type, buffer.alignment,
buffer.left, buffer.top, buffer.stride });
}
engine.getDriverApi().queueCommand(
make_copyable_function([buffer = std::move(buffer)]() {}));
}
engine.getDriverApi().queueCommand(make_copyable_function([buffer = std::move(buffer)]() {}));
}
void FTexture::setExternalImage(FEngine& engine, void* image) noexcept {
@@ -50,6 +50,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialName = charTo64bitNum("MAT_NAME"),
MaterialVersion = charTo64bitNum("MAT_VERS"),
MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"),
MaterialShading = charTo64bitNum("MAT_SHAD"),
MaterialBlendingMode = charTo64bitNum("MAT_BLEN"),
MaterialTransparencyMode = charTo64bitNum("MAT_TRMD"),
@@ -206,6 +206,7 @@ public:
using SamplerFormat = filament::backend::SamplerFormat;
using ParameterPrecision = filament::backend::Precision;
using CullingMode = filament::backend::CullingMode;
using FeatureLevel = filament::backend::FeatureLevel;
enum class VariableQualifier : uint8_t {
OUT
@@ -360,6 +361,8 @@ public:
MaterialBuilder& quality(ShaderQuality quality) noexcept;
MaterialBuilder& featureLevel(FeatureLevel featureLevel) noexcept;
//! Set the blending mode for this material.
MaterialBuilder& blending(BlendingMode blending) noexcept;
@@ -654,7 +657,7 @@ private:
// and vertex shaders in mProperties.
bool findProperties(filament::backend::ShaderType type,
MaterialBuilder::PropertyList& p) noexcept;
bool runSemanticAnalysis() noexcept;
bool runSemanticAnalysis(MaterialInfo const& info) noexcept;
bool checkLiteRequirements() noexcept;
@@ -709,6 +712,7 @@ private:
OutputList mOutputs;
ShaderQuality mShaderQuality = ShaderQuality::DEFAULT;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT;
CullingMode mCullingMode = CullingMode::BACK;
+12 -5
View File
@@ -271,6 +271,11 @@ MaterialBuilder& MaterialBuilder::quality(ShaderQuality quality) noexcept {
return *this;
}
MaterialBuilder& MaterialBuilder::featureLevel(FeatureLevel featureLevel) noexcept {
mFeatureLevel = featureLevel;
return *this;
}
MaterialBuilder& MaterialBuilder::blending(BlendingMode blending) noexcept {
mBlendingMode = blending;
return *this;
@@ -499,6 +504,7 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
info.useLegacyMorphing = mUseLegacyMorphing;
info.instanced = mInstanced;
info.vertexDomainDeviceJittered = mVertexDomainDeviceJittered;
info.featureLevel = mFeatureLevel;
}
bool MaterialBuilder::findProperties(filament::backend::ShaderType type,
@@ -546,7 +552,7 @@ bool MaterialBuilder::findAllProperties() noexcept {
#endif
}
bool MaterialBuilder::runSemanticAnalysis() noexcept {
bool MaterialBuilder::runSemanticAnalysis(MaterialInfo const& info) noexcept {
#ifndef FILAMAT_LITE
using namespace filament::backend;
GLSLTools glslTools;
@@ -561,12 +567,12 @@ bool MaterialBuilder::runSemanticAnalysis() noexcept {
ShaderModel model = static_cast<ShaderModel>(mSemanticCodeGenParams.shaderModel);
std::string shaderCode = peek(ShaderType::VERTEX, mSemanticCodeGenParams, mProperties);
bool result = glslTools.analyzeVertexShader(shaderCode, model, mMaterialDomain, targetApi);
bool result = glslTools.analyzeVertexShader(shaderCode, model, mMaterialDomain, targetApi, info);
if (!result) return false;
shaderCode = peek(ShaderType::FRAGMENT, mSemanticCodeGenParams, mProperties);
result = glslTools.analyzeFragmentShader(shaderCode, model, mMaterialDomain, targetApi,
mCustomSurfaceShading);
mCustomSurfaceShading, info);
return result;
#else
return true;
@@ -953,12 +959,12 @@ Package MaterialBuilder::build(JobSystem& jobSystem) noexcept {
}
// prepareToBuild must be called first, to populate mCodeGenPermutations.
MaterialInfo info {};
MaterialInfo info{};
prepareToBuild(info);
// Run checks, in order.
// The call to findProperties populates mProperties and must come before runSemanticAnalysis.
if (!checkLiteRequirements() || !findAllProperties() || !runSemanticAnalysis()) {
if (!checkLiteRequirements() || !findAllProperties() || !runSemanticAnalysis(info)) {
// Return an empty package to signal a failure to build the material.
return Package::invalidPackage();
}
@@ -1039,6 +1045,7 @@ std::string MaterialBuilder::peek(filament::backend::ShaderType type,
void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept {
container.addSimpleChild<uint32_t>(ChunkType::MaterialVersion, filament::MATERIAL_VERSION);
container.addSimpleChild<uint8_t>(ChunkType::MaterialFeatureLevel, (uint8_t)mFeatureLevel);
container.addSimpleChild<const char*>(ChunkType::MaterialName, mMaterialName.c_str_safe());
container.addSimpleChild<uint32_t>(ChunkType::MaterialShaderModels, mShaderModels.getValue());
container.addSimpleChild<uint8_t>(ChunkType::MaterialDomain, static_cast<uint8_t>(mMaterialDomain));
+29 -2
View File
@@ -22,6 +22,7 @@
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <filamat/MaterialBuilder.h>
#include "../shaders/MaterialInfo.h"
#include <utils/Log.h>
@@ -53,7 +54,8 @@ GLSLangCleaner::~GLSLangCleaner() {
bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode, ShaderModel model,
MaterialBuilder::MaterialDomain materialDomain,
MaterialBuilder::TargetApi targetApi, bool hasCustomSurfaceShading) const noexcept {
MaterialBuilder::TargetApi targetApi, bool hasCustomSurfaceShading,
MaterialInfo const& info) const noexcept {
// Parse to check syntax and semantic.
const char* shaderCString = shaderCode.c_str();
@@ -90,6 +92,18 @@ bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode, ShaderModel
return false;
}
switch (info.featureLevel) {
case FeatureLevel::FEATURE_LEVEL_1:
// TODO: feature level checks
// - check no more than 9 samplers are used by the user and 16 total
// - check cubemap arrays are not used
break;
case FeatureLevel::FEATURE_LEVEL_2:
// TODO: feature level checks
// - check no more than 12 samplers are used by the user and 31 total
break;
}
// If this is a post-process material, at this point we've successfully met all the
// requirements.
if (materialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
@@ -129,7 +143,8 @@ bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode, ShaderModel
bool GLSLTools::analyzeVertexShader(const std::string& shaderCode, ShaderModel model,
MaterialBuilder::MaterialDomain materialDomain,
MaterialBuilder::TargetApi targetApi) const noexcept {
MaterialBuilder::TargetApi targetApi,
MaterialInfo const& info) const noexcept {
// TODO: After implementing post-process vertex shaders, properly analyze them here.
if (materialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
@@ -161,6 +176,18 @@ bool GLSLTools::analyzeVertexShader(const std::string& shaderCode, ShaderModel m
return false;
}
switch (info.featureLevel) {
case FeatureLevel::FEATURE_LEVEL_1:
// TODO: feature level checks
// - check no more than 9 samplers are used by the user and 16 total
// - check cubemap arrays are not used
break;
case FeatureLevel::FEATURE_LEVEL_2:
// TODO: feature level checks
// - check no more than 12 samplers are used by the user and 31 total
break;
}
return true;
}
+4 -2
View File
@@ -124,12 +124,14 @@ public:
bool analyzeFragmentShader(const std::string& shaderCode,
filament::backend::ShaderModel model,
MaterialBuilder::MaterialDomain materialDomain,
MaterialBuilder::TargetApi targetApi, bool hasCustomSurfaceShading) const noexcept;
MaterialBuilder::TargetApi targetApi, bool hasCustomSurfaceShading,
MaterialInfo const& info) const noexcept;
bool analyzeVertexShader(const std::string& shaderCode,
filament::backend::ShaderModel model,
MaterialBuilder::MaterialDomain materialDomain,
MaterialBuilder::TargetApi targetApi) const noexcept;
MaterialBuilder::TargetApi targetApi,
MaterialInfo const& info) const noexcept;
// Public for unit tests.
using Property = MaterialBuilder::Property;
+17 -6
View File
@@ -16,6 +16,8 @@
#include "CodeGenerator.h"
#include "MaterialInfo.h"
#include "generated/shaders.h"
#include <utils/sstream.h>
@@ -37,25 +39,27 @@ io::sstream& CodeGenerator::generateSeparator(io::sstream& out) {
return out;
}
io::sstream& CodeGenerator::generateProlog(io::sstream& out, ShaderType type,
bool hasExternalSamplers) const {
utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, ShaderType type,
MaterialInfo const& info) const {
switch (mShaderModel) {
case ShaderModel::MOBILE:
// Vulkan requires version 310 or higher
if (mTargetLanguage == TargetLanguage::SPIRV) {
if (mTargetLanguage == TargetLanguage::SPIRV ||
info.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
// Vulkan requires layout locations on ins and outs, which were not supported
// in the OpenGL 4.1 GLSL profile.
out << "#version 310 es\n\n";
} else {
out << "#version 300 es\n\n";
}
if (hasExternalSamplers) {
if (info.hasExternalSamplers) {
out << "#extension GL_OES_EGL_image_external_essl3 : require\n\n";
}
out << "#define TARGET_MOBILE\n";
break;
case ShaderModel::DESKTOP:
if (mTargetLanguage == TargetLanguage::SPIRV) {
if (mTargetLanguage == TargetLanguage::SPIRV ||
info.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
// Vulkan requires binding specifiers on uniforms and samplers, which were not
// supported in the OpenGL 4.1 GLSL profile.
out << "#version 450 core\n\n";
@@ -91,10 +95,17 @@ io::sstream& CodeGenerator::generateProlog(io::sstream& out, ShaderType type,
out << "#define FILAMENT_OPENGL_SEMANTICS\n";
}
out << '\n';
out << "#define FILAMENT_FEATURE_LEVEL_1 1\n";
out << "#define FILAMENT_FEATURE_LEVEL_2 2\n";
out << "#define FILAMENT_FEATURE_LEVEL FILAMENT_FEATURE_LEVEL_" << (int)info.featureLevel << "\n";
out << '\n';
if (mTargetApi == TargetApi::VULKAN ||
mTargetApi == TargetApi::METAL ||
(mTargetApi == TargetApi::OPENGL && mShaderModel == ShaderModel::DESKTOP)) {
(mTargetApi == TargetApi::OPENGL && mShaderModel == ShaderModel::DESKTOP) ||
info.featureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
out << "#define FILAMENT_HAS_FEATURE_TEXTURE_GATHER\n";
}
+25 -24
View File
@@ -56,31 +56,32 @@ public:
filament::backend::ShaderModel getShaderModel() const noexcept { return mShaderModel; }
// insert a separator (can be a new line)
static utils::io::sstream& generateSeparator(utils::io::sstream& out) ;
static utils::io::sstream& generateSeparator(utils::io::sstream& out);
// generate prolog for the given shader
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderType type, bool hasExternalSamplers) const;
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderType type,
MaterialInfo const& info) const;
static utils::io::sstream& generateEpilog(utils::io::sstream& out) ;
static utils::io::sstream& generateEpilog(utils::io::sstream& out);
// generate common functions for the given shader
static utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generatePostProcessCommon(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderType type);
static utils::io::sstream& generatePostProcessCommon(utils::io::sstream& out, ShaderType type);
static utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderType type);
static utils::io::sstream& generateFog(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateFog(utils::io::sstream& out, ShaderType type);
// generate the shader's main()
static utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generatePostProcessMain(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderType type);
static utils::io::sstream& generatePostProcessMain(utils::io::sstream& out, ShaderType type);
// generate the shader's code for the lit shading model
static utils::io::sstream& generateShaderLit(utils::io::sstream& out, ShaderType type,
filament::Variant variant, filament::Shading shading, bool customSurfaceShading) ;
filament::Variant variant, filament::Shading shading, bool customSurfaceShading);
// generate the shader's code for the unlit shading model
static utils::io::sstream& generateShaderUnlit(utils::io::sstream& out, ShaderType type,
filament::Variant variant, bool hasShadowMultiplier) ;
filament::Variant variant, bool hasShadowMultiplier);
// generate the shader's code for the screen-space reflections
static utils::io::sstream& generateShaderReflections(utils::io::sstream& out, ShaderType type,
@@ -88,12 +89,12 @@ public:
// generate declarations for custom interpolants
static utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderType type,
const utils::CString& name, size_t index) ;
const utils::CString& name, size_t index);
// generate declarations for non-custom "in" variables
static utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderType type,
const filament::AttributeBitset& attributes, filament::Interpolation interpolation) ;
static utils::io::sstream& generatePostProcessInputs(utils::io::sstream& out, ShaderType type) ;
const filament::AttributeBitset& attributes, filament::Interpolation interpolation);
static utils::io::sstream& generatePostProcessInputs(utils::io::sstream& out, ShaderType type);
// generate declarations for custom output variables
utils::io::sstream& generateOutput(utils::io::sstream& out, ShaderType type,
@@ -102,7 +103,7 @@ public:
MaterialBuilder::OutputType outputType) const;
// generate no-op shader for depth prepass
static utils::io::sstream& generateDepthShaderMain(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateDepthShaderMain(utils::io::sstream& out, ShaderType type);
// generate uniforms
utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderType type, uint8_t binding,
@@ -114,23 +115,23 @@ public:
// generate subpass
static utils::io::sstream& generateSubpass(utils::io::sstream& out,
filament::SubpassInfo subpass) ;
filament::SubpassInfo subpass);
// generate material properties getters
static utils::io::sstream& generateMaterialProperty(utils::io::sstream& out,
MaterialBuilder::Property property, bool isSet) ;
MaterialBuilder::Property property, bool isSet);
utils::io::sstream& generateQualityDefine(utils::io::sstream& out, ShaderQuality quality) const;
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, bool value) ;
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, uint32_t value) ;
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, const char* string) ;
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, bool value);
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, uint32_t value);
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, const char* string);
static utils::io::sstream& generateIndexedDefine(utils::io::sstream& out, const char* name,
uint32_t index, uint32_t value) ;
uint32_t index, uint32_t value);
static utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderType type);
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderType type);
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderType type);
static void fixupExternalSamplers(
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;
+1
View File
@@ -61,6 +61,7 @@ struct UTILS_PUBLIC MaterialInfo {
filament::SubpassInfo subpass;
filament::SamplerBindingMap samplerBindings;
filament::ShaderQuality quality;
filament::backend::FeatureLevel featureLevel;
};
}
+4 -4
View File
@@ -181,7 +181,7 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
const bool lit = material.isLit;
cg.generateProlog(vs, ShaderType::VERTEX, material.hasExternalSamplers);
cg.generateProlog(vs, ShaderType::VERTEX, material);
cg.generateQualityDefine(vs, material.quality);
@@ -315,7 +315,7 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
const bool lit = material.isLit;
io::sstream fs;
cg.generateProlog(fs, ShaderType::FRAGMENT, material.hasExternalSamplers);
cg.generateProlog(fs, ShaderType::FRAGMENT, material);
cg.generateQualityDefine(fs, material.quality);
@@ -536,7 +536,7 @@ std::string ShaderGenerator::createPostProcessVertexProgram(
const filament::Variant::type_t variantKey, SamplerBindingMap const& samplerBindingMap) const noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage);
io::sstream vs;
cg.generateProlog(vs, ShaderType::VERTEX, false);
cg.generateProlog(vs, ShaderType::VERTEX, material);
cg.generateQualityDefine(vs, material.quality);
@@ -577,7 +577,7 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(
uint8_t variant, const SamplerBindingMap& samplerBindingMap) const noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage);
io::sstream fs;
cg.generateProlog(fs, ShaderType::FRAGMENT, false);
cg.generateProlog(fs, ShaderType::FRAGMENT, material);
cg.generateQualityDefine(fs, material.quality);
+1
View File
@@ -86,6 +86,7 @@ static void printStringChunk(ostream& json, const ChunkContainer& container,
static bool printMaterial(ostream& json, const ChunkContainer& container) {
printStringChunk(json, container, MaterialName, "name");
printUint32Chunk(json, container, MaterialVersion, "version");
printUint32Chunk(json, container, MaterialFeatureLevel, "feature_level");
json << "\"shading\": {\n";
printChunk<Shading, uint8_t>(json, container, MaterialShading, "model");
printChunk<MaterialDomain, uint8_t>(json, container, ChunkType::MaterialDomain, "material_domain");
+6
View File
@@ -95,6 +95,12 @@ static bool printMaterial(ostream& text, const ChunkContainer& container) {
text << version << endl;
}
uint8_t featureLevel;
if (read(container, MaterialFeatureLevel, &featureLevel)) {
text << " " << setw(alignment) << left << "Feature level: ";
text << featureLevel << endl;
}
CString name;
if (read(container, MaterialName, &name)) {
text << " " << setw(alignment) << left << "Name: ";
+19 -1
View File
@@ -15,6 +15,7 @@
*/
#include "ParametersProcessor.h"
#include "backend/DriverEnums.h"
#include <algorithm>
#include <iostream>
@@ -35,7 +36,7 @@ static bool logEnumIssue(const std::string& key, const JsonishString& value,
std::cerr << "Error while processing key '" << key << "' value." << std::endl;
std::cerr << "Value '" << value.getString() << "' is invalid. Valid values are:"
<< std::endl;
for (auto entries : map) {
for (const auto& entries : map) {
std::cerr << " " << entries.first << std::endl;
}
return false;
@@ -398,6 +399,22 @@ static bool processQuality(MaterialBuilder& builder, const JsonishValue& value)
return true;
}
static bool processFeatureLevel(MaterialBuilder& builder, const JsonishValue& value) {
using filament::backend::FeatureLevel;
JsonishNumber const* const number = value.toJsonNumber();
FeatureLevel featureLevel;
if (number->getFloat() == 1.0f) {
featureLevel = FeatureLevel::FEATURE_LEVEL_1;
} else if (number->getFloat() == 2.0f) {
featureLevel = FeatureLevel::FEATURE_LEVEL_2;
} else {
std::cerr << "featureLevel: invalid value " << number->getFloat() << std::endl;
return false;
}
builder.featureLevel(featureLevel);
return true;
}
static bool processOutput(MaterialBuilder& builder, const JsonishObject& jsonObject) noexcept {
const JsonishValue* nameValue = jsonObject.getValue("name");
@@ -785,6 +802,7 @@ ParametersProcessor::ParametersProcessor() {
mParameters["outputs"] = { &processOutputs, Type::ARRAY };
mParameters["quality"] = { &processQuality, Type::STRING };
mParameters["customSurfaceShading"] = { &processCustomSurfaceShading, Type::BOOL };
mParameters["featureLevel"] = { &processFeatureLevel, Type::NUMBER };
}
bool ParametersProcessor::process(MaterialBuilder& builder, const JsonishObject& jsonObject) {
+6
View File
@@ -391,6 +391,12 @@ class_<Engine>("Engine")
.function("isAutomaticInstancingEnabled", &Engine::isAutomaticInstancingEnabled)
.function("getSupportedFeatureLevel", &Engine::getSupportedFeatureLevel)
.function("setActiveFeatureLevel", &Engine::setActiveFeatureLevel)
.function("getActiveFeatureLevel", &Engine::getActiveFeatureLevel)
.function("_execute", EMBIND_LAMBDA(void, (Engine* engine), {
EM_ASM_INT({
const handle = window.filament_contextHandle;
+4
View File
@@ -399,6 +399,10 @@ enum_<backend::CullingMode>("CullingMode")
.value("BACK", backend::CullingMode::BACK)
.value("FRONT_AND_BACK", backend::CullingMode::FRONT_AND_BACK);
enum_<backend::FeatureLevel>("FeatureLevel")
.value("FEATURE_LEVEL_1", backend::FeatureLevel::FEATURE_LEVEL_1)
.value("FEATURE_LEVEL_2", backend::FeatureLevel::FEATURE_LEVEL_2);
enum_<ktxreader::Ktx2Reader::TransferFunction>("Ktx2Reader$TransferFunction")
.value("LINEAR", ktxreader::Ktx2Reader::TransferFunction::LINEAR)
.value("sRGB", ktxreader::Ktx2Reader::TransferFunction::sRGB);