Compare commits

..
Author SHA1 Message Date
Benjamin Doherty f3da96d424 Throw an NSException on Apple platforms when panicking 2024-03-27 16:04:49 -07:00
Nimrod Gileadi a068143953 Add semicolons to code snippet in AssetLoader.h (#7717) 2024-03-27 17:24:10 +00:00
Powei Feng 90d90094dc Bump material version to 51 in MaterialEnums.h 2024-03-26 14:37:13 -07:00
Sungun Park 54df4524eb Improve multiview shader replacement (#7706)
Replace the num_views for OpenGL multiviwe only when
- The engine is initialized with multiview stereo
- The variant for the material contains STE flag
- The program is for surface
- It's vertex shader (this is already in)
2024-03-26 21:36:22 +00:00
Powei Feng d70f2e1b81 vk: delete instead of ref-count EmptyTexture (#7711) 2024-03-26 13:35:50 -07:00
Powei Feng 2f359b73a4 Add missing include to Platform.h (#7709) 2024-03-26 19:51:57 +00:00
Sungun Park abb0cbc98e Add FILAMENT_ENABLE_MULTIVIEW option (#7707)
This allows the engine to include multiview shader code for default
materials.
2024-03-25 21:43:04 +00:00
Jacob Su 2763931b47 fix install android samples apk error. (#7230) 2024-03-22 23:05:01 -07:00
Sungun Park 0ead96b606 Replace value of num_views with engine's eye count (#7696)
For OpenGL multiview, it honors the qualifier `layout(num_views = X)`
specified in shader files to determine the number of views for
multiview.

We cannot recompile materials everytime the value changes. So replace
the value of num_views with the engine's eye count when shaders compile.
2024-03-22 21:45:58 +00:00
Ben Doherty 317c1bb7ea Metal: track buffer allocations (#7556) 2024-03-22 12:47:56 -07:00
Ben Doherty 9aaaad9271 Improve Skybox eyeDirection precision (#7685) 2024-03-22 12:03:06 -07:00
Mathias Agopian d2ce714e73 fix use-after-free disable option 2024-03-22 11:27:30 -07:00
Mathias Agopian 8398175d9c Add an option to disable use-after-free checks in the backend
BUGS=330403836
2024-03-22 10:24:29 -07:00
Sungun Park 26258a4718 Facilitate four views for multiview (#7694)
This change allows the combine function for multiview to be able to
combine more than two views side-by-side.
2024-03-22 17:09:08 +00:00
Mathias Agopian 3644e7f808 Fixes IBL prefilter has floor and ceiling flipped
FIXES=[330603077]
2024-03-21 13:28:24 -07:00
Powei Feng 4d774820d9 gl: Don't reference swapchain obj on Web
Fixes #7693
2024-03-21 13:27:45 -07:00
Eliza Velasquez aad45d9119 Fix typo 2024-03-21 19:59:47 +00:00
Eliza Velasquez 5389b37002 Incorporate feedback 2024-03-21 19:59:47 +00:00
Eliza Velasquez d5fe9e236f Allow rendering thread to pause
This PR adds a new `pause()` option to the `Engine` `Builder` and a new function
`setPaused()` to the `Engine`. While paused, the rendering thread will pause
indefinitely for commands as if none are available. As soon as the rendering
thread is unpaused, the commands are immediately executed.
2024-03-21 19:59:47 +00:00
Mathias Agopian 59890ac85a PlatformEGL::createSwapChain never returns a nullptr anymore
in case the swapchain creation fails, it will now return a swapchain
with an EGL_NO_SURFACE handle. this will avoid having to nullptr check
the pointer in various places and will revert to the previous behavior
on failure.

FIXES=[329659681]
2024-03-20 16:07:02 -07:00
Mathias Agopian 435969f565 attempt to detect buffer overflows in Texture::setImage()
We verify that the buffer given to setImage() is at least as large
as needed for the given region to transfer; at least based on the
size given.

This might help catch b/330407429.

BUGS=330407429
2024-03-20 16:06:43 -07:00
Ben Doherty e3db39105f iOS: fix gltf-viewer crash while destruction ResourceLoader (#7684) 2024-03-19 16:14:45 -07:00
Powei Feng dfa821d351 vk: fix broken gltf renderings (#7680)
This is due to color attachments being set to store=discard when
they are multi-sampled. It's unclear why that condition exists. For
now, removing it will fix the rendering issues with transparent
object + MSAA. We'll keep it as such until an issue surfaces.

Fixes #7674
2024-03-19 22:38:52 +00:00
Powei Feng 1ab223c965 vk: minor fixes (#7682)
- Add description to a few debug options
- Set correct usage flag for blit-src/dest images
- Correctly initialize a debug-only field in VulkanProgram
2024-03-19 13:59:52 -07:00
Mathias Agopian 29bb60cd94 fix typo that caused a wrong assertion 2024-03-19 13:13:12 -07:00
Mathias Agopian 93b15dac87 The type of geometry of a renderable can now be specified
- dynamic (default) no restriction apply
- static bounds: bounds and world transform can't be changed
- static: additionaly morphing/skinning and vertex/index buffers are
  immutable.

This will allow some optimizations in the future. Currently, we just
store the type but don't do anything with it.
2024-03-19 11:12:25 -07:00
Romain Guy a8fda9b4d0 Make PBR Neutral invertible (#7677)
Based on model-viewer's change at
https://github.com/google/model-viewer/pull/4716
2024-03-19 11:11:44 -07:00
Benjamin Doherty ba9cb2fe43 Release Filament 1.51.0 2024-03-17 13:12:32 -07:00
Sungun Park 0f7cffc407 Connect multiview components together (#7671)
Plumb through multiview configurations to the pipeline so that the
engine draws scenes using multiview extension. Users need to prepare
shaders compiled with the `multiview` param and set the
`stereoscopicType` flag to MULTIVIEW in the Engine::Config to enable
multiview feature.

In this change, postprocessings for multiview are not yet supported. So
we all disable them until they're supported.

The debug option `combineMultiviewImages` combines layers as one image,
which allows us to check the final result.
2024-03-15 22:01:38 +00:00
Mathias Agopian dace5fd695 minor fixes and cleanup to Primitive and BufferInfo factories
- both files use consistent names
- enforce that the keys are not moved around
- don't pass the value (which is 4 bytes) by reference
2024-03-15 11:26:47 -07:00
Powei Feng b23ee1bce4 gltfio: add extended tangents job (#7666)
- TangentsJobExtended extracts data from cgltf accessor and
   runs geometry::TangentSpaceMesh on the attributes and computes
   the tangent space.
 - The /extended folder is meant for running this process. Note that
   this API might remesh the input and will require corresponding
   changes that might break previous assumptions.
 - The general flow of the code is modeled after src/TangentsJob.h
 - This is not hooked into current code and should have no
   practical effect on gltfio.
2024-03-15 17:46:08 +00:00
Mathias Agopian 25f017b883 fix missing headers 2024-03-14 16:41:21 -07:00
Sungun Park 0def77eaa8 Add layerCount to createRenderTarget (#7660)
This new parameter indicates whether the render target will be created
for multiview.

If the value is greater than 1, it tells the render target should be
created for multiview. Otherwise, 1 or 0, it creates a single layer
render target.
2024-03-14 00:38:34 +00:00
Powei Feng 3c25dab22f vk: clean-up initialization of classes (#7667)
Instead of holding pointers to class instances in VulkanDriver,
we standardize by making the relevant classes have proper
constructors and initialize in VulkanDriver's constructor
initializer list.
2024-03-13 23:51:22 +00:00
Mathias Agopian 93a80fd084 More Platform improvements for protected contexts
- `isSwapChainProtected()` is now virtual
- `createDefaultRenderTarget()` is renamed to `getDefaultFramebufferObject()`
- new `getCurrentContextType()` returns the current context type
- `makeCurrent` now takes an additional `ContextType`
- `PlatformEGL::getContextForType()` to retrieve the `EGLContext` for
   a given type.
- `PlatformEGL::makeCurrent` non-virtual utilities to set only the
  context or swapchains.
2024-03-13 16:13:34 -07:00
Mathias Agopian 963e097bdc handle non-0 default FBO with protected contextes 2024-03-13 16:13:34 -07:00
Mathias Agopian 86479781a5 assert when swapchain creation fails
This is better than risking a null-pointer dereference later.
2024-03-13 16:13:34 -07:00
Hanno J. Gödecke 4ea5872b26 fix: applyCrossFade use correct instance 2024-03-13 16:13:01 -07:00
Hanno J. Gödecke f27f0ef4fc fix: apply stashCrossFade to correct entity 2024-03-13 16:13:01 -07:00
Ben Doherty 348454781f Remove erroneous assertion (#7661) 2024-03-12 14:29:49 -07:00
Sungun Park bb6c8ef1c8 Add option FILAMENT_SAMPLES_STEREO_TYPE for samples/gltfio (#7658)
This option can be either "instanced" or "multiview", indicating what
stereoscopic rendering type shaders in samples/gltfio should be built
for.
2024-03-12 13:49:00 -07:00
Powei Feng fa6b4ebd04 gltfio: refactor for clarity (#7652)
- Pull certain utility functions in a separate header and cpp
- Refactor ResourceLoader::loadeResources into smaller methods
2024-03-12 20:31:19 +00:00
Ben Doherty 9aad4df441 Metal: respect disableParallelShaderCompile config (#7659) 2024-03-12 13:06:43 -07:00
Ben Doherty 2e581be8fd Move SwapChain flags into separate file (#7654) 2024-03-12 12:10:43 -07:00
Sungun Park 6d7eaf31d3 Add multiview filamat for default materials (#7644)
Add prebuilt materials for the engine default materials. They'll be
selected for multiview stereoscopic implementation.
2024-03-12 02:50:54 +00:00
Powei Feng dea345d28e geometry: fix mikktspace wrapper (#7651)
- Fix missing attributes in TangentSpaceMesh
- Fix missing reference in MikktspaceImpl.cpp
2024-03-11 22:27:46 +00:00
Sungun Park c1dfd8553d Release Filament 1.50.6 2024-03-11 21:38:10 +00:00
Powei Feng 0c48f40836 vk: add /usr/local/lib to rpath on macos (#7643)
For reasons unknown, after upgrading to XCode 15.3, dlopen can
no longer find libvulkan.1.dylib. We fix it by explicitly adding
/usr/local/lib to rpath for macos.
2024-03-08 23:18:43 +00:00
Mathias Agopian cac4d2aa94 Modernize draw API.
PipelineState now holds a handle to a HwVertexBufferInfo. 
DriverAPI::draw() is now technically deprecated and replaced by the
more efficient draw2(), which only takes an index offset, index count 
and instance count. The Pipeline to use is now specified with a new
API bindPipeline() and the primitive to use with bindRenderPrimitive().

This allows clients to reuse RenderPrimitives and ultimately Pipelines.

This change reduces CPU usage significantly on Metal and Vulkan, by 
reducing the need to lookup for a pipeline at every draw call.

The application, however, must be a "good citizen" by reusing 
MaterialInstance and RenderPrimitive as much as possible. We do have
RenderPrimitive cache however, so reusing the same VertexBuffer and
associated parameters also works.
2024-03-08 13:42:10 -08:00
Sungun Park f8973d53d6 Consolidate materials for feature level (#7640)
Some default materials such as defaultMaterial and skybox have discrete
material file for feature level 0.

Combine these materials as one utilizing the `-P` option of matc.
2024-03-08 12:26:53 -08:00
Ben Doherty d07168f49c Metal: change shader compilation pool size to 1 (#7639) 2024-03-08 10:32:38 -08:00
Mathias Agopian c3057e17bb add post-lighting mix factor support
This adds a new material property (float postLightingMixFactor) which
is used to mix the original color with the post-lighting blended color.
The default value is 1.0, which keeps the current behavior.

FIXES=[328498606]
2024-03-08 10:32:21 -08:00
Powei Feng 23a8efd3dc vk: clean up debug tools (#7635)
- Fix broken resource leak print out.
 - Add sampler name to debugUtils when enabled
2024-03-08 01:15:24 +00:00
Mathias Agopian e1973978ae Set the protected attribute on EGLImage
We set the attribute based on the usage bits of the underlaying 
AHardwareBuffer.
2024-03-07 14:23:57 -08:00
Powei Feng b9a33b7d3e Try fixing windows artifact output again (#7637)
The [previous] change assumed that the shell is powershell, but the shell is actually commands (cmd). 

The [previous] change assumed we're in the root directory.  This assumption is probably correct [ref]. So we keep that change.

[ref]:  https://github.com/google/filament/blob/main/build/windows/build-github.bat#L134
[previous]: 373c5710b1
2024-03-07 01:09:04 +00:00
Mathias Agopian 434c226e8a IBLs were mirrored when using IBLPrefilter
cmgen mirrors environment maps by default so that the reflection map
appears un-mirrored. IBLPrefilter didn't do that. 

EquirectangularToCubemap now takes a Config parameter that allows to
specify the mirroring, which is enabled by default.


FIXES=[320856413]
2024-03-06 15:04:30 -08:00
Sungun Park 0605e9fe82 Fix a warning (#7634)
This fixes a warning for 6601c7c2b5
2024-03-06 21:42:30 +00:00
179 changed files with 4125 additions and 1594 deletions
+17
View File
@@ -45,6 +45,8 @@ option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
option(FILAMENT_ENABLE_MULTIVIEW "Enable multiview for Filament" OFF)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
@@ -531,6 +533,21 @@ else()
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" ON)
endif()
# This only affects the prebuilt shader files in gltfio and samples, not filament library.
# The value can be either "instanced" or "multiview".
set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" CACHE STRING
"Stereoscopic type that shader files in gltfio and samples are built for."
)
string(TOLOWER "${FILAMENT_SAMPLES_STEREO_TYPE}" FILAMENT_SAMPLES_STEREO_TYPE)
if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ")
endif ()
# Compiling samples for multiview implies enabling multiview feature as well.
if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
set(FILAMENT_ENABLE_MULTIVIEW ON)
endif ()
# ==================================================================================================
# Material compilation flags
# ==================================================================================================
+2
View File
@@ -7,3 +7,5 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.50.6'
implementation 'com.google.android.filament:filament-android:1.51.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.50.6'
pod 'Filament', '~> 1.51.0'
```
### Snapshots
+7
View File
@@ -7,6 +7,13 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.51.1
## v1.51.0
- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**]
## v1.50.6
- Add new API `SwapChain::getFrameScheduledCallback`
@@ -391,6 +391,13 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
engine->flush();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
jlong nativeEngine, jboolean paused) {
Engine* engine = (Engine*) nativeEngine;
engine->setPaused(paused);
}
// Managers...
extern "C" JNIEXPORT jlong JNICALL
@@ -487,7 +494,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
jlong jobSystemThreadCount,
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
jint stereoscopicType, jlong stereoscopicEyeCount,
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
jboolean disableHandleUseAfterFreeCheck) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -502,6 +510,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
.disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck,
};
builder->config(&config);
}
@@ -518,6 +527,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
builder->sharedContext((void*) sharedContext);
}
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderPaused(
JNIEnv*, jclass, jlong nativeBuilder, jboolean paused) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
builder->paused((bool) paused);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
@@ -104,6 +104,14 @@ Java_com_google_android_filament_RenderableManager_nBuilderGeometry__JIIJJIIII(J
(size_t) count);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderGeometryType(JNIEnv*, jclass,
jlong nativeBuilder, int type) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
builder->geometryType((RenderableManager::Builder::GeometryType)type);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderMaterial(JNIEnv*, jclass,
@@ -224,7 +224,8 @@ public class Engine {
config.jobSystemThreadCount,
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
config.disableHandleUseAfterFreeCheck);
return this;
}
@@ -239,6 +240,20 @@ public class Engine {
return this;
}
/**
* Sets the initial paused state of the rendering thread.
*
* <p>Warning: This is an experimental API. See {@link Engine#setPaused(boolean)} for
* caveats.
*
* @param paused Whether to start the rendering thread paused.
* @return A reference to this Builder for chaining calls.
*/
public Builder paused(boolean paused) {
nSetBuilderPaused(mNativeBuilder, paused);
return this;
}
/**
* Creates an instance of Engine
*
@@ -408,6 +423,11 @@ public class Engine {
* This value determines for how many frames are texture entries kept in the cache.
*/
public long resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
*/
public boolean disableHandleUseAfterFreeCheck = false;
}
private Engine(long nativeEngine, Config config) {
@@ -654,7 +674,7 @@ public class Engine {
*/
@NonNull
public SwapChain createSwapChain(@NonNull Object surface) {
return createSwapChain(surface, SwapChain.CONFIG_DEFAULT);
return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT);
}
/**
@@ -662,15 +682,15 @@ public class Engine {
*
* @param surface on Android, <b>must be</b> an instance of {@link android.view.Surface}
*
* @param flags configuration flags, see {@link SwapChain}
* @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
* @see SwapChain#CONFIG_DEFAULT
* @see SwapChain#CONFIG_TRANSPARENT
* @see SwapChain#CONFIG_READABLE
* @see SwapChainFlags#CONFIG_DEFAULT
* @see SwapChainFlags#CONFIG_TRANSPARENT
* @see SwapChainFlags#CONFIG_READABLE
*
*/
@NonNull
@@ -688,21 +708,22 @@ public class Engine {
*
* @param width width of the rendering buffer
* @param height height of the rendering buffer
* @param flags configuration flags, see {@link SwapChain}
* @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
* @see SwapChain#CONFIG_DEFAULT
* @see SwapChain#CONFIG_TRANSPARENT
* @see SwapChain#CONFIG_READABLE
* @see SwapChainFlags#CONFIG_DEFAULT
* @see SwapChainFlags#CONFIG_TRANSPARENT
* @see SwapChainFlags#CONFIG_READABLE
*
*/
@NonNull
public SwapChain createSwapChain(int width, int height, long flags) {
if (width >= 0 && height >= 0) {
long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
long nativeSwapChain =
nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain");
return new SwapChain(nativeSwapChain, null);
}
@@ -714,11 +735,12 @@ public class Engine {
*
* @param surface a properly initialized {@link NativeSurface}
*
* @param flags configuration flags, see {@link SwapChain}
* @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created
* @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be
* created
*/
@NonNull
public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) {
@@ -1187,6 +1209,22 @@ public class Engine {
nFlush(getNativeObject());
}
/**
* Pause or resume the rendering thread.
*
* <p>Warning: This is an experimental API. In particular, note the following caveats.
*
* <ul><li>
* Buffer callbacks will never be called as long as the rendering thread is paused.
* Do not rely on a buffer callback to unpause the thread.
* </li><li>
* While the rendering thread is paused, rendering commands will continue to be queued until the
* buffer limit is reached. When the limit is reached, the program will abort.
* </li></ul>
*/
public void setPaused(boolean paused) {
nSetPaused(getNativeObject(), paused);
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
@@ -1261,6 +1299,7 @@ public class Engine {
private static native void nDestroyEntity(long nativeEngine, int entity);
private static native void nFlushAndWait(long nativeEngine);
private static native void nFlush(long nativeEngine);
private static native void nSetPaused(long nativeEngine, boolean paused);
private static native long nGetTransformManager(long nativeEngine);
private static native long nGetLightManager(long nativeEngine);
private static native long nGetRenderableManager(long nativeEngine);
@@ -1281,8 +1320,10 @@ public class Engine {
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
int stereoscopicType, long stereoscopicEyeCount,
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge);
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
boolean disableHandleUseAfterFreeCheck);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
private static native long nBuilderBuild(long nativeBuilder);
}
@@ -175,6 +175,32 @@ public class RenderableManager {
return this;
}
/**
* Type of geometry for a Renderable
*/
public enum GeometryType {
/** dynamic gemoetry has no restriction */
DYNAMIC,
/** bounds and world space transform are immutable */
STATIC_BOUNDS,
/** skinning/morphing not allowed and Vertex/IndexBuffer immutables */
STATIC
}
/**
* Specify whether this renderable has static bounds. In this context his means that
* the renderable's bounding box cannot change and that the renderable's transform is
* assumed immutable. Changing the renderable's transform via the TransformManager
* can lead to corrupted graphics. Note that skinning and morphing are not forbidden.
* Disabled by default.
* @param enable whether this renderable has static bounds. false by default.
*/
@NonNull
public Builder geometryType(GeometryType type) {
nBuilderGeometryType(mNativeBuilder, type.ordinal());
return this;
}
/**
* Binds a material instance to the specified primitive.
*
@@ -964,6 +990,7 @@ public class RenderableManager {
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer);
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int count);
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int minIndex, int maxIndex, int count);
private static native void nBuilderGeometryType(long nativeBuilder, int type);
private static native void nBuilderMaterial(long nativeBuilder, int index, long nativeMaterialInstance);
private static native void nBuilderBlendOrder(long nativeBuilder, int index, int blendOrder);
private static native void nBuilderGlobalBlendOrderEnabled(long nativeBuilder, int index, boolean enabled);
@@ -68,72 +68,6 @@ public class SwapChain {
private final Object mSurface;
private long mNativeObject;
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
/**
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
@@ -145,17 +79,19 @@ public class SwapChain {
*
* @param engine A reference to the filament Engine
* @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise.
* @see SwapChainFlags#CONFIG_PROTECTED_CONTENT
*/
public static boolean isProtectedContentSupported(@NonNull Engine engine) {
return nIsProtectedContentSupported(engine.getNativeObject());
}
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
* @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise.
* @see SwapChainFlags#CONFIG_SRGB_COLORSPACE
*/
public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.filament;
// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend
// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java
// classes.
/**
* Flags that a <code>SwapChain</code> can be created with to control behavior.
*
* @see Engine#createSwapChain
* @see Engine#createSwapChainFromNativeSurface
*/
public final class SwapChainFlags {
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
/**
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
}
@@ -27,7 +27,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import com.google.android.filament.SwapChain;
import com.google.android.filament.SwapChainFlags;
/**
* UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder
@@ -538,7 +538,7 @@ public class UiHelper {
* the options set on this UiHelper.
*/
public long getSwapChainFlags() {
return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT;
return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT;
}
/**
+8
View File
@@ -81,9 +81,17 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/TangentsJob.cpp
${GLTFIO_DIR}/src/TangentsJob.h
${GLTFIO_DIR}/src/UbershaderProvider.cpp
${GLTFIO_DIR}/src/Utility.cpp
${GLTFIO_DIR}/src/Utility.h
${GLTFIO_DIR}/src/Wireframe.cpp
${GLTFIO_DIR}/src/Wireframe.h
${GLTFIO_DIR}/src/downcast.h
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.h
${GLTFIO_DIR}/src/extended/TangentsJobExtended.cpp
${GLTFIO_DIR}/src/extended/TangentsJobExtended.h
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.cpp
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.h
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.50.6
VERSION_NAME=1.51.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -516,7 +516,7 @@ function build_android {
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
for sample in ${ANDROID_SAMPLES}; do
echo "Installing out/${sample}-debug.apk"
cp samples/${sample}/build/outputs/apk/debug/${sample}-debug-unsigned.apk \
cp samples/${sample}/build/outputs/apk/debug/${sample}-debug.apk \
../out/${sample}-debug.apk
done
fi
+36 -17
View File
@@ -252,8 +252,13 @@ set(MATERIAL_SRCS
)
set(MATERIAL_FL0_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
src/materials/defaultMaterial.mat
src/materials/skybox.mat
)
set(MATERIAL_MULTIVIEW_SRCS
src/materials/defaultMaterial.mat
src/materials/skybox.mat
)
# Embed the binary resource blob for materials.
@@ -285,6 +290,11 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
endif()
# Whether to include MULTIVIEW materials.
if (FILAMENT_ENABLE_MULTIVIEW)
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -315,33 +325,42 @@ foreach (mat_src ${MATERIAL_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
COMMENT "Compiling material ${fullname} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
foreach (mat_src ${MATERIAL_FL0_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
list(FIND MATERIAL_FL0_SRCS ${mat_src} index)
if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
string(REGEX REPLACE "[.]filamat$" "_fl0.filamat" output_path_fl0 ${output_path})
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
OUTPUT ${output_path_fl0}
COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path_fl0} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
COMMENT "Compiling material ${fullname} to ${output_path_fl0}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach ()
endif ()
list(APPEND MATERIAL_BINS ${output_path_fl0})
endif ()
list(FIND MATERIAL_MULTIVIEW_SRCS ${mat_src} index)
if (${index} GREATER -1 AND FILAMENT_ENABLE_MULTIVIEW)
string(REGEX REPLACE "[.]filamat$" "_multiview.filamat" output_path_multiview ${output_path})
add_custom_command(
OUTPUT ${output_path_multiview}
COMMAND matc ${MATC_BASE_FLAGS} -PstereoscopicType=multiview -o ${output_path_multiview} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${fullname} to ${output_path_multiview}"
)
list(APPEND MATERIAL_BINS ${output_path_multiview})
endif ()
endforeach()
# Additional dependencies on included files for materials
+18 -2
View File
@@ -1186,11 +1186,27 @@ struct StencilState {
//! Stencil operations for front-facing polygons
StencilOperations front = {
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
.stencilFunc = StencilFunction::A,
.stencilOpStencilFail = StencilOperation::KEEP,
.padding0 = 0,
.stencilOpDepthFail = StencilOperation::KEEP,
.stencilOpDepthStencilPass = StencilOperation::KEEP,
.padding1 = 0,
.ref = 0,
.readMask = 0xff,
.writeMask = 0xff };
//! Stencil operations for back-facing polygons
StencilOperations back = {
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
.stencilFunc = StencilFunction::A,
.stencilOpStencilFail = StencilOperation::KEEP,
.padding0 = 0,
.stencilOpDepthFail = StencilOperation::KEEP,
.stencilOpDepthStencilPass = StencilOperation::KEEP,
.padding1 = 0,
.ref = 0,
.readMask = 0xff,
.writeMask = 0xff };
//! Whether stencil-buffer writes are enabled
bool stencilWrite = false;
@@ -29,10 +29,13 @@ namespace filament::backend {
//! \privatesection
struct PipelineState {
Handle<HwProgram> program;
RasterState rasterState;
StencilState stencilState;
PolygonOffset polygonOffset;
Handle<HwProgram> program; // 4
Handle<HwVertexBufferInfo> vertexBufferInfo; // 4
RasterState rasterState; // 4
StencilState stencilState; // 12
PolygonOffset polygonOffset; // 8
PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1
uint8_t padding[3] = {}; // 3
};
} // namespace filament::backend
+38 -1
View File
@@ -23,6 +23,7 @@
#include <utils/Invocable.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -56,9 +57,14 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
* Currently only honored by the GL and Metal backends.
*/
bool disableParallelShaderCompile = false;
/**
* Disable backend handles use-after-free checks.
*/
bool disableHandleUseAfterFreeCheck = false;
};
Platform() noexcept;
@@ -188,9 +194,40 @@ public:
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize);
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
/**
* Sets the callback function that the backend can use to update backend-specific statistics
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
* thread.
*
* @param debugUpdateStat an Invocable that updates debug statistics
*/
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
/**
* @return true if debugUpdateStat is valid.
*/
bool hasDebugUpdateStatFunc() const noexcept;
/**
* To track backend-specific statistics, the backend implementation can call the
* application-provided callback function debugUpdateStatFunc to associate or update a value
* with a given key. It is possible for this function to be called multiple times with the
* same key, in which case newer values should overwrite older values.
*
* This function is guaranteed to be called only on a single thread, the Filament driver
* thread.
*
* @param key a null-terminated C-string with the key of the debug statistic
* @param value the updated value of key
*/
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
private:
InsertBlobFunc mInsertBlob;
RetrieveBlobFunc mRetrieveBlob;
DebugUpdateStatFunc mDebugUpdateStat;
};
} // namespace filament
@@ -116,6 +116,8 @@ public:
Program& cacheId(uint64_t cacheId) noexcept;
Program& multiview(bool multiview) noexcept;
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
@@ -143,6 +145,8 @@ public:
uint64_t getCacheId() const noexcept { return mCacheId; }
bool isMultiview() const noexcept { return mMultiview; }
CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; }
private:
@@ -158,6 +162,11 @@ private:
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
// Indicates the current engine was initialized with multiview stereo, and the variant for this
// program contains STE flag. This will be referred later for the OpenGL shader compiler to
// determine whether shader code replacement for the num_views should be performed.
// This variable could be promoted as a more generic variable later if other similar needs occur.
bool mMultiview = false;
};
} // namespace filament::backend
@@ -32,6 +32,10 @@ struct TargetBufferInfo {
// texture to be used as render target
Handle<HwTexture> handle;
// starting layer index for multiview. This value is only used when the `layerCount` for the
// render target is greater than 1.
uint8_t baseViewIndex = 0;
// level to be used
uint8_t level = 0;
@@ -80,7 +84,7 @@ public:
// this is here for backward compatibility
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: mInfos{{ handle, level, layer }} {
: mInfos{{ handle, 0, level, layer }} {
}
};
@@ -89,7 +89,7 @@ public:
* @return The driver's SwapChain object.
*
*/
virtual SwapChain* UTILS_NONNULL createSwapChain(
virtual SwapChain* UTILS_NULLABLE createSwapChain(
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
/**
@@ -126,29 +126,59 @@ public:
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
/**
* Called by the driver to establish the default FBO. The default implementation returns 0.
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
* Returns true if the swapchain is protected
*/
virtual uint32_t createDefaultRenderTarget() noexcept;
virtual bool isSwapChainProtected(Platform::SwapChain* UTILS_NONNULL swapChain) noexcept;
/**
* Called by the driver to make the OpenGL context active on the calling thread and bind
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
* Called by the driver to establish the default FBO. The default implementation returns 0.
*
* This method can be called either on the regular or protected OpenGL contexts and can return
* a different or identical name, since these names exist in different namespaces.
*
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
*/
virtual uint32_t getDefaultFramebufferObject() noexcept;
/**
* Type of contexts available
*/
enum class ContextType {
NONE, //!< No current context
UNPROTECTED, //!< current context is unprotected
PROTECTED //!< current context supports protected content
};
/**
* Returns the type of the context currently in use. This value is updated by makeCurrent()
* and therefore can be cached between calls. ContextType::PROTECTED can only be returned
* if isProtectedContextSupported() is true.
* @return ContextType
*/
virtual ContextType getCurrentContextType() const noexcept;
/**
* Binds the requested context to the current thread and drawSwapChain to the default FBO
* returned by getDefaultFramebufferObject().
*
* @param type type of context to bind to the current thread.
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
* @return true on success, false on error.
*/
virtual void makeCurrent(
virtual bool makeCurrent(ContextType type,
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
/**
* Called by the driver to make the OpenGL context active on the calling thread and bind
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
* the drawSwapChain to the default FBO returned by getDefaultFramebufferObject().
* The context used is either the default context or the protected context. When a context
* change is necessary, the preContextChange and postContextChange callbacks are called,
* before and after the context change respectively. postContextChange is given the index
* of the new context (0 for default and 1 for protected).
* The default implementation just calls makeCurrent(SwapChain*, SwapChain*).
* The default implementation just calls makeCurrent(getCurrentContextType(), SwapChain*, SwapChain*).
*
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
@@ -57,7 +57,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImage(ExternalTexture* texture) noexcept override;
@@ -45,7 +45,7 @@ public:
void terminate() noexcept override;
uint32_t createDefaultRenderTarget() noexcept override;
uint32_t getDefaultFramebufferObject() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
@@ -53,7 +53,7 @@ public:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
@@ -99,11 +99,18 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
ContextType getCurrentContextType() const noexcept override;
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
bool canCreateFence() noexcept override;
@@ -132,21 +139,25 @@ protected:
/**
* Always use this instead of eglMakeCurrent(), as it tracks some state.
*/
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept;
/**
* Returns true if the swapchain is protected
*/
static bool isSwapChainProtected(Platform::SwapChain const* swapChain) noexcept;
EGLContext getContextForType(ContextType type) const noexcept;
// makes the draw and read surface current without changing the current context
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
return egl.makeCurrent(drawSurface, readSurface);
}
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
EGLBoolean makeCurrent(EGLContext context) noexcept {
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
}
// TODO: this should probably use getters instead.
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLContext mCurrentContext = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
@@ -181,8 +192,20 @@ protected:
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
private:
EGLBoolean makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
class EGL {
EGLDisplay& mEGLDisplay;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLContext mCurrentContext = EGL_NO_CONTEXT;
public:
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
EGLBoolean makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
} egl{ mEGLDisplay };
};
} // namespace filament::backend
@@ -51,7 +51,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
private:
@@ -53,7 +53,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
protected:
@@ -46,7 +46,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
};
@@ -50,12 +50,13 @@ class CommandBufferQueue {
size_t mFreeSpace = 0;
size_t mHighWatermark = 0;
uint32_t mExitRequested = 0;
bool mPaused = false;
static constexpr uint32_t EXIT_REQUESTED = 0x31415926;
public:
// requiredSize: guaranteed available space after flush()
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused);
~CommandBufferQueue();
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
@@ -80,6 +81,9 @@ public:
// returns from waitForCommands() immediately.
void requestExit();
// suspend or unsuspend the queue.
void setPaused(bool paused);
bool isExitRequested() const;
};
@@ -239,6 +239,7 @@ DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
uint32_t, width,
uint32_t, height,
uint8_t, samples,
uint8_t, layerCount,
backend::MRT, color,
backend::TargetBufferInfo, depth,
backend::TargetBufferInfo, stencil)
@@ -493,6 +494,17 @@ DECL_DRIVER_API_N(blit,
math::uint2, srcOrigin,
math::uint2, size)
DECL_DRIVER_API_N(bindPipeline,
backend::PipelineState, state)
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(draw2,
uint32_t, indexOffset,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(draw,
backend::PipelineState, state,
backend::RenderPrimitiveHandle, rph,
@@ -500,6 +512,7 @@ DECL_DRIVER_API_N(draw,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
@@ -37,9 +37,9 @@
#include <stddef.h>
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
namespace filament::backend {
@@ -49,7 +49,7 @@ namespace filament::backend {
template<size_t P0, size_t P1, size_t P2>
class HandleAllocator {
public:
HandleAllocator(const char* name, size_t size) noexcept;
HandleAllocator(const char* name, size_t size, bool disableUseAfterFreeCheck) noexcept;
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
~HandleAllocator();
@@ -169,11 +169,13 @@ public:
if (isPoolHandle(handle.getId())) {
// check for use after free
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t const expectedAge = pNode[-1].age;
ASSERT_POSTCONDITION(expectedAge == age,
"use-after-free of Handle with id=%d", handle.getId());
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t const expectedAge = pNode[-1].age;
ASSERT_POSTCONDITION(expectedAge == age,
"use-after-free of Handle with id=%d", handle.getId());
}
}
return static_cast<Dp>(p);
@@ -187,7 +189,6 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
private:
template<typename D>
@@ -210,8 +211,9 @@ private:
Pool<P1> mPool1;
Pool<P2> mPool2;
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
bool mUseAfterFreeCheckDisabled;
public:
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
explicit Allocator(const utils::AreaPolicy::HeapArea& area, bool disableUseAfterFreeCheck);
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
@@ -237,8 +239,10 @@ private:
// check for double-free
Node* const pNode = static_cast<Node*>(p);
uint8_t& expectedAge = pNode[-1].age;
ASSERT_POSTCONDITION(expectedAge == age,
"double-free of Handle of size %d at %p", size, p);
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
ASSERT_POSTCONDITION(expectedAge == age,
"double-free of Handle of size %d at %p", size, p);
}
expectedAge = (expectedAge + 1) & 0xF; // fixme
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
@@ -348,6 +352,7 @@ private:
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
};
} // namespace filament::backend
+17 -3
View File
@@ -39,10 +39,11 @@ using namespace utils;
namespace filament::backend {
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused)
: mRequiredSize((requiredSize + (CircularBuffer::getBlockSize() - 1u)) & ~(CircularBuffer::getBlockSize() -1u)),
mCircularBuffer(bufferSize),
mFreeSpace(mCircularBuffer.size()) {
mFreeSpace(mCircularBuffer.size()),
mPaused(paused) {
assert_invariant(mCircularBuffer.size() > requiredSize);
}
@@ -56,6 +57,16 @@ void CommandBufferQueue::requestExit() {
mCondition.notify_one();
}
void CommandBufferQueue::setPaused(bool paused) {
std::lock_guard<utils::Mutex> const lock(mLock);
if (paused) {
mPaused = true;
} else {
mPaused = false;
mCondition.notify_one();
}
}
bool CommandBufferQueue::isExitRequested() const {
std::lock_guard<utils::Mutex> const lock(mLock);
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
@@ -115,6 +126,9 @@ void CommandBufferQueue::flush() noexcept {
#endif
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
ASSERT_POSTCONDITION(!mPaused,
"CommandStream is full, but since the rendering thread is paused, "
"the buffer cannot flush and we will deadlock. Instead, abort.");
mCondition.wait(lock, [this, requiredSize]() -> bool {
// TODO: on macOS, we need to call pumpEvents from time to time
return mFreeSpace >= requiredSize;
@@ -127,7 +141,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
return std::move(mCommandBuffersToExecute);
}
std::unique_lock<utils::Mutex> lock(mLock);
while (mCommandBuffersToExecute.empty() && !mExitRequested) {
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
mCondition.wait(lock);
}
+8 -4
View File
@@ -39,8 +39,10 @@ using namespace utils;
template <size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
: mArea(area) {
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area,
bool disableUseAfterFreeCheck)
: mArea(area),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
// The largest handle this allocator can generate currently depends on the architecture's
// min alignment, typically 8 or 16 bytes.
@@ -74,8 +76,10 @@ HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& ar
// ------------------------------------------------------------------------------------------------
template <size_t P0, size_t P1, size_t P2>
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size) noexcept
: mHandleArena(name, size) {
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck) noexcept
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
}
template <size_t P0, size_t P1, size_t P2>
+14
View File
@@ -53,4 +53,18 @@ size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size
return 0;
}
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
mDebugUpdateStat = std::move(debugUpdateStat);
}
bool Platform::hasDebugUpdateStatFunc() const noexcept {
return bool(mDebugUpdateStat);
}
void Platform::debugUpdateStat(const char* key, uint64_t value) {
if (mDebugUpdateStat) {
mDebugUpdateStat(key, value);
}
}
} // namespace filament::backend
+5
View File
@@ -91,6 +91,11 @@ Program& Program::cacheId(uint64_t cacheId) noexcept {
return *this;
}
Program& Program::multiview(bool multiview) noexcept {
mMultiview = multiview;
return *this;
}
io::ostream& operator<<(io::ostream& out, const Program& builder) {
out << "Program{";
builder.mLogger(out);
+47 -7
View File
@@ -18,7 +18,6 @@
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
#include "MetalContext.h"
#include "MetalBufferPool.h"
#include <backend/DriverEnums.h>
@@ -28,9 +27,50 @@
#include <utility>
#include <memory>
#include <atomic>
namespace filament::backend {
class TrackedMetalBuffer {
public:
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
TrackedMetalBuffer(id<MTLBuffer> buffer) noexcept : mBuffer(buffer) {
if (buffer) {
aliveBuffers++;
}
}
~TrackedMetalBuffer() {
if (mBuffer) {
aliveBuffers--;
}
}
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
swap(rhs);
return *this;
}
id<MTLBuffer> get() const noexcept { return mBuffer; }
operator bool() const noexcept { return bool(mBuffer); }
static uint64_t getAliveBuffers() { return aliveBuffers; }
private:
void swap(TrackedMetalBuffer& other) noexcept {
id<MTLBuffer> temp = mBuffer;
mBuffer = other.mBuffer;
other.mBuffer = temp;
}
id<MTLBuffer> mBuffer;
static std::atomic<uint64_t> aliveBuffers;
};
class MetalBuffer {
public:
@@ -82,7 +122,7 @@ public:
private:
id<MTLBuffer> mBuffer = nil;
TrackedMetalBuffer mBuffer;
size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
MetalContext& mContext;
@@ -151,7 +191,7 @@ public:
// finishes executing.
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
assert_invariant(mAuxBuffer);
return {mAuxBuffer, 0};
return {mAuxBuffer.get(), 0};
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
@@ -180,9 +220,9 @@ public:
*/
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
if (UTILS_UNLIKELY(mAuxBuffer)) {
return { mAuxBuffer, 0 };
return { mAuxBuffer.get(), 0 };
}
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
}
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
@@ -191,8 +231,8 @@ public:
private:
id<MTLDevice> mDevice;
id<MTLBuffer> mBuffer;
id<MTLBuffer> mAuxBuffer;
TrackedMetalBuffer mBuffer;
TrackedMetalBuffer mAuxBuffer;
MTLResourceOptions mBufferOptions;
+7 -4
View File
@@ -15,12 +15,15 @@
*/
#include "MetalBuffer.h"
#include "MetalBufferPool.h"
#include "MetalContext.h"
namespace filament {
namespace backend {
std::atomic<uint64_t> TrackedMetalBuffer::aliveBuffers = 0;
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
@@ -61,7 +64,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
// Acquire a staging buffer to hold the contents of this update.
MetalBufferPool* bufferPool = mContext.bufferPool;
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
memcpy(staging->buffer.contents, src, size);
memcpy(staging->buffer.get().contents, src, size);
// The blit below requires that byteOffset be a multiple of 4.
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
@@ -70,9 +73,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
blitEncoder.label = @"Buffer upload blit";
[blitEncoder copyFromBuffer:staging->buffer
[blitEncoder copyFromBuffer:staging->buffer.get()
sourceOffset:0
toBuffer:mBuffer
toBuffer:mBuffer.get()
destinationOffset:byteOffset
size:size];
[blitEncoder endEncoding];
@@ -93,7 +96,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return nil;
}
assert_invariant(mBuffer);
return mBuffer;
return mBuffer.get();
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
+3 -1
View File
@@ -19,6 +19,8 @@
#include <Metal/Metal.h>
#include "MetalBuffer.h"
#include <map>
#include <mutex>
#include <unordered_set>
@@ -30,7 +32,7 @@ struct MetalContext;
// Immutable POD representing a shared CPU-GPU buffer.
struct MetalBufferPoolEntry {
id<MTLBuffer> buffer;
TrackedMetalBuffer buffer;
size_t capacity;
mutable uint64_t lastAccessed;
mutable uint32_t referenceCount;
@@ -45,12 +45,12 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
options:MTLResourceStorageModeShared];
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry({
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
.buffer = buffer,
.capacity = numBytes,
.lastAccessed = mCurrentFrame,
.referenceCount = 1
});
};
mUsedStages.insert(stage);
return stage;
@@ -99,6 +99,7 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
DepthStencilStateCache depthStencilStateCache;
+73 -32
View File
@@ -20,6 +20,7 @@
#include "metal/MetalDriver.h"
#include "MetalBlitter.h"
#include "MetalBufferPool.h"
#include "MetalContext.h"
#include "MetalDriverFactory.h"
#include "MetalEnums.h"
@@ -36,6 +37,7 @@
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/sstream.h>
#include <algorithm>
@@ -48,24 +50,26 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
// For reference on a 64-bits machine in Release mode:
// MetalTimerQuery : 16 few
// HwStream : 24 few
// MetalRenderPrimitive : 24 many
// MetalVertexBuffer : 32 moderate
// -- less than or equal 32 bytes
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 144 moderate
// MetalProgram : 152 moderate
// MetalTexture : 152 moderate
// MetalVertexBuffer : 152 moderate
// -- less than or equal 160 bytes
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalRenderPrimitive : 584 many
// -- less than or equal to 592 bytes
// MetalVertexBufferInfo : 552 moderate
// -- less than or equal to 552 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
@@ -96,7 +100,9 @@ Dispatcher MetalDriver::getDispatcher() const noexcept {
MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(*platform),
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
mHandleAllocator("Handles", driverConfig.handleArenaSize) {
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck) {
mContext->driver = this;
mContext->device = mPlatform.createDevice();
@@ -178,7 +184,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue];
}
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this);
const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile
? MetalShaderCompiler::Mode::SYNCHRONOUS
: MetalShaderCompiler::Mode::ASYNCHRONOUS;
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode);
mContext->shaderCompiler->init();
#if defined(FILAMENT_METAL_PROFILING)
@@ -207,6 +216,9 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
#if defined(FILAMENT_METAL_PROFILING)
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
#endif
if (mPlatform.hasDebugUpdateStatFunc()) {
mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers());
}
}
void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
@@ -371,7 +383,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dum
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, MRT color,
uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"createRenderTarget must be called outside of a render pass.");
@@ -786,7 +798,7 @@ bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType)
}
bool MetalDriver::isParallelShaderCompileSupported() {
return true;
return mContext->shaderCompiler->isParallelShaderCompileSupported();
}
bool MetalDriver::isDepthStencilResolveSupported() {
@@ -1602,11 +1614,13 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void MetalDriver::bindPipeline(PipelineState ps) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"Attempted to draw without a valid command encoder.");
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
"bindPipeline() without a valid command encoder.");
MetalVertexBufferInfo const* const vbi =
handle_cast<MetalVertexBufferInfo>(ps.vertexBufferInfo);
auto program = handle_cast<MetalProgram>(ps.program);
const auto& rs = ps.rasterState;
@@ -1620,7 +1634,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
return;
}
functions.validate();
ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
@@ -1647,7 +1661,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
.vertexDescription = primitive->vertexDescription,
.vertexDescription = vbi->vertexDescription,
.colorAttachmentPixelFormat = {
colorPixelFormat[0],
colorPixelFormat[1],
@@ -1741,20 +1755,6 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
// Bind sampler groups (argument buffers).
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
@@ -1785,19 +1785,29 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
}
}
}
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"bindRenderPrimitive() without a valid command encoder.");
// Bind the user vertex buffers.
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
size_t maxBufferIndex = 0;
MetalRenderPrimitive const* const primitive = handle_cast<MetalRenderPrimitive>(rph);
MetalVertexBufferInfo const* const vbi =
handle_cast<MetalVertexBufferInfo>(primitive->vertexBuffer->vbih);
mContext->currentRenderPrimitive = rph;
auto vb = primitive->vertexBuffer;
for (auto m : primitive->bufferMapping) {
for (auto m : vbi->bufferMapping) {
assert_invariant(
m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
}
@@ -1812,6 +1822,27 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
[mContext->currentRenderPassEncoder setVertexBytes:bytes
length:16
atIndex:ZERO_VERTEX_BUFFER_BINDING];
}
void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"draw() without a valid command encoder.");
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
@@ -1825,6 +1856,16 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
instanceCount:instanceCount];
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
MetalRenderPrimitive const* const rp = handle_cast<MetalRenderPrimitive>(rph);
ps.primitiveType = rp->type;
ps.vertexBufferInfo = rp->vertexBuffer->vbih;
bindPipeline(ps);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute must be called outside of a render pass.");
+15 -17
View File
@@ -149,7 +149,21 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo {
MetalVertexBufferInfo(MetalContext& context,
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
AttributeArray attributes;
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
struct Entry {
uint8_t sourceBufferIndex = 0;
uint8_t stride = 0;
// maps to ->
uint8_t bufferArgumentIndex = 0;
Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
: sourceBufferIndex(sourceBufferIndex),
stride(stride),
bufferArgumentIndex(bufferArgumentIndex) {}
};
utils::FixedCapacityVector<Entry> bufferMapping;
};
struct MetalVertexBuffer : public HwVertexBuffer {
@@ -176,22 +190,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
struct Entry {
uint8_t sourceBufferIndex = 0;
uint8_t stride = 0;
// maps to ->
uint8_t bufferArgumentIndex = 0;
Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
: sourceBufferIndex(sourceBufferIndex),
stride(stride),
bufferArgumentIndex(bufferArgumentIndex) {}
};
utils::FixedCapacityVector<Entry> bufferMapping;
};
class MetalProgram : public HwProgram {
+25 -24
View File
@@ -19,6 +19,7 @@
#include "MetalBlitter.h"
#include "MetalEnums.h"
#include "MetalUtils.h"
#include "MetalBufferPool.h"
#include <filament/SwapChain.h>
@@ -343,27 +344,9 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {
}
bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive()
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
const size_t attributeCount = vbi->attributes.size();
const size_t maxAttributeCount = attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
@@ -403,8 +386,8 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
}
};
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vbi->attributes[attributeIndex];
for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) {
const auto& attribute = attributes[attributeIndex];
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
// to read from missing vertex attributes.
@@ -431,6 +414,24 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
}
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive() {
}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
}
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
@@ -770,13 +771,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
const size_t stagingBufferSize = shape.totalBytes;
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
memcpy(entry->buffer.contents,
memcpy(entry->buffer.get().contents,
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
stagingBufferSize);
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
blitCommandEncoder.label = @"Texture upload buffer blit";
[blitCommandEncoder copyFromBuffer:entry->buffer
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
sourceOffset:0
sourceBytesPerRow:shape.bytesPerRow
sourceBytesPerImage:shape.bytesPerSlice
@@ -41,6 +41,11 @@ class MetalShaderCompiler {
struct MetalProgramToken;
public:
enum class Mode {
SYNCHRONOUS, // synchronous shader compilation
ASYNCHRONOUS // asynchronous shader compilation
};
class MetalFunctionBundle {
public:
using Raster = std::tuple<id<MTLFunction>, id<MTLFunction>>;
@@ -110,7 +115,7 @@ public:
using program_token_t = std::shared_ptr<MetalProgramToken>;
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver);
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode);
MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete;
MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete;
@@ -120,15 +125,15 @@ public:
void init() noexcept;
void terminate() noexcept;
// Creates a program asynchronously
bool isParallelShaderCompileSupported() const noexcept;
// Creates a program, either synchronously or asynchronously, depending on the Mode
// MetalShaderCompiler was constructed with.
program_token_t createProgram(utils::CString const& name, Program&& program);
// Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid.
MetalFunctionBundle getProgram(program_token_t& token);
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
static void terminate(program_token_t& token);
void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
@@ -138,6 +143,7 @@ private:
CompilerThreadPool mCompilerThreadPool;
id<MTLDevice> mDevice;
CallbackManager mCallbackManager;
Mode mMode;
};
} // namespace filament::backend
@@ -70,22 +70,31 @@ struct MetalShaderCompiler::MetalProgramToken : ProgramToken {
MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default;
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver)
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode)
: mDevice(device),
mCallbackManager(driver) {
mCallbackManager(driver),
mMode(mode) {
}
void MetalShaderCompiler::init() noexcept {
const uint32_t poolSize = 1;
mCompilerThreadPool.init(poolSize, []() {}, []() {});
if (mMode == Mode::ASYNCHRONOUS) {
mCompilerThreadPool.init(poolSize, []() {}, []() {});
}
}
void MetalShaderCompiler::terminate() noexcept {
mCompilerThreadPool.terminate();
if (mMode == Mode::ASYNCHRONOUS) {
mCompilerThreadPool.terminate();
}
mCallbackManager.terminate();
}
bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
return mMode == Mode::ASYNCHRONOUS;
}
/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
const Program& program, id<MTLDevice> device) {
std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
@@ -180,14 +189,26 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
token->handle = mCallbackManager.get();
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
switch (mMode) {
case Mode::ASYNCHRONOUS: {
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
break;
}
case Mode::SYNCHRONOUS: {
MetalFunctionBundle compiledProgram = compileProgram(program, mDevice);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
break;
}
}
return token;
}
@@ -195,35 +216,23 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) {
assert_invariant(token);
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
if (mMode == Mode::ASYNCHRONOUS) {
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
}
}
}
// The job isn't guaranteed to have finished yet. We may have failed to dequeue it above,
// which means it's currently running. In that case get() will block until it finishes.
MetalShaderCompiler::MetalFunctionBundle program = token->get();
token = nullptr;
return program;
}
/* static */ void MetalShaderCompiler::terminate(program_token_t& token) {
assert_invariant(token);
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// The job is being executed right now (or has already executed).
token->wait();
} else {
// The job has not executed yet.
token->compiler.mCallbackManager.put(token->handle);
}
token.reset();
}
void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
mCallbackManager.setCallback(handler, callback, user);
+9
View File
@@ -350,6 +350,15 @@ void NoopDriver::blit(
math::uint2 size) {
}
void NoopDriver::bindPipeline(PipelineState pipelineState) {
}
void NoopDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
+64 -1
View File
@@ -16,11 +16,25 @@
#include "OpenGLContext.h"
#include "GLUtils.h"
#include "OpenGLTimerQuery.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <functional>
#include <string_view>
#include <utility>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
// change to true to display all GL extensions in the console on start-up
#define DEBUG_PRINT_EXTENSIONS false
@@ -49,7 +63,8 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
#endif
}
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept {
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
: mPlatform(platform) {
state.vao.p = &mDefaultVAO;
@@ -270,6 +285,9 @@ void OpenGLContext::synchronizeStateAndCache(size_t index) noexcept {
}
}
// the default FBO could be invalid
mDefaultFbo[index].reset();
contextIndex = index;
resetState();
}
@@ -751,6 +769,51 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
#endif // BACKEND_OPENGL_VERSION_GL
GLuint OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
if (UTILS_UNLIKELY(buffer == 0)) {
// we're binding the default frame buffer, resolve its actual name
auto& defaultFboForThisContext = mDefaultFbo[contextIndex];
if (UTILS_UNLIKELY(!defaultFboForThisContext.has_value())) {
defaultFboForThisContext = GLuint(mPlatform.getDefaultFramebufferObject());
}
buffer = defaultFboForThisContext.value();
}
bindFramebufferResolved(target, buffer);
return buffer;
}
void OpenGLContext::unbindFramebuffer(GLenum target) noexcept {
bindFramebufferResolved(target, 0);
}
void OpenGLContext::bindFramebufferResolved(GLenum target, GLuint buffer) noexcept {
switch (target) {
case GL_FRAMEBUFFER:
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
state.draw_fbo = state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
case GL_DRAW_FRAMEBUFFER:
if (state.draw_fbo != buffer) {
state.draw_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
case GL_READ_FRAMEBUFFER:
if (state.read_fbo != buffer) {
state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#endif
default:
break;
}
}
void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
if (target == GL_ELEMENT_ARRAY_BUFFER) {
constexpr size_t targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
+22 -37
View File
@@ -17,24 +17,32 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
#define TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
#include <math/vec4.h>
#include "OpenGLTimerQuery.h"
#include <utils/CString.h>
#include <utils/debug.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "GLUtils.h"
#include "gl_headers.h"
#include <utils/compiler.h>
#include <utils/bitset.h>
#include <utils/debug.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <array>
#include <functional>
#include <set>
#include <optional>
#include <tuple>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
class OpenGLPlatform;
@@ -82,7 +90,7 @@ public:
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
explicit OpenGLContext(OpenGLPlatform& platform) noexcept;
~OpenGLContext() noexcept;
~OpenGLContext() noexcept final;
// TimerQueryInterface ------------------------------------------------------------------------
@@ -148,7 +156,8 @@ public:
inline void bindBufferRange(GLenum target, GLuint index, GLuint buffer,
GLintptr offset, GLsizeiptr size) noexcept;
inline void bindFramebuffer(GLenum target, GLuint buffer) noexcept;
GLuint bindFramebuffer(GLenum target, GLuint buffer) noexcept;
void unbindFramebuffer(GLenum target) noexcept;
inline void enableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
inline void disableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
@@ -460,10 +469,15 @@ public:
void synchronizeStateAndCache(size_t index) noexcept;
private:
OpenGLPlatform& mPlatform;
ShaderModel mShaderModel = ShaderModel::MOBILE;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
TimerQueryFactoryInterface* mTimerQueryFactory = nullptr;
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
RenderPrimitive mDefaultVAO;
std::optional<GLuint> mDefaultFbo[2];
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
{ bugs.disable_glFlush,
@@ -516,8 +530,6 @@ private:
""},
}};
RenderPrimitive mDefaultVAO;
// this is chosen to minimize code size
#if defined(BACKEND_OPENGL_VERSION_GLES)
static void initExtensionsGLES(Extensions* ext, GLint major, GLint minor) noexcept;
@@ -732,33 +744,6 @@ void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
#endif
}
void OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
switch (target) {
case GL_FRAMEBUFFER:
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
state.draw_fbo = state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
case GL_DRAW_FRAMEBUFFER:
if (state.draw_fbo != buffer) {
state.draw_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
case GL_READ_FRAMEBUFFER:
if (state.read_fbo != buffer) {
state.read_fbo = buffer;
glBindFramebuffer(target, buffer);
}
break;
#endif
default:
break;
}
}
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t targetIndex) noexcept {
assert_invariant(targetIndex == getIndexForTextureTarget(target));
assert_invariant(targetIndex < TEXTURE_TARGET_COUNT);
+148 -73
View File
@@ -16,21 +16,54 @@
#include "OpenGLDriver.h"
#include "private/backend/DriverApi.h"
#include "CommandStreamDispatcher.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include "OpenGLDriverFactory.h"
#include "OpenGLProgram.h"
#include "OpenGLTimerQuery.h"
#include "gl_headers.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/SamplerDescriptor.h>
#include <utils/compiler.h>
#include <backend/BufferDescriptor.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <backend/Platform.h>
#include <backend/Program.h>
#include <backend/SamplerDescriptor.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/Dispatcher.h"
#include "private/backend/DriverApi.h"
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <algorithm>
#include <chrono>
#include <functional>
#include <limits>
#include <memory>
#include <mutex>
#include <new>
#include <utility>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#if defined(__EMSCRIPTEN__)
#include <emscripten.h>
@@ -94,10 +127,10 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
// GLSamplerGroup : 16 few
// GLSwapChain : 16 few
// GLTimerQuery : 16 few
// GLRenderPrimitive : 20 many
// GLFence : 24 few
// -- less than or equal 24 bytes
// GLRenderPrimitive : 32 many
// GLBufferObject : 32 many
// -- less than or equal 32 bytes
// OpenGLProgram : 56 moderate
// GLTexture : 64 moderate
// -- less than or equal 64 bytes
@@ -172,7 +205,9 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
: mPlatform(*platform),
mContext(mPlatform),
mShaderCompilerService(*this),
mHandleAllocator("Handles", driverConfig.handleArenaSize),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck),
mSamplerMap(32),
mDriverConfig(driverConfig) {
@@ -518,10 +553,12 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
GLIndexBuffer const* const ib = handle_cast<const GLIndexBuffer*>(ibh);
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive*>(rph);
GLVertexBuffer* const vb = handle_cast<GLVertexBuffer*>(vbh);
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
rp->gl.vertexBufferWithObjects = vbh;
rp->type = pt;
rp->vbih = vb->vbih;
// create a name for this VAO in the current context
gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]);
@@ -728,23 +765,23 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
// we can't be here -- doesn't matter what we do
case SamplerType::SAMPLER_2D:
t->gl.target = GL_TEXTURE_2D;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
break;
case SamplerType::SAMPLER_3D:
t->gl.target = GL_TEXTURE_3D;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
break;
case SamplerType::SAMPLER_2D_ARRAY:
t->gl.target = GL_TEXTURE_2D_ARRAY;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
break;
case SamplerType::SAMPLER_CUBEMAP:
t->gl.target = GL_TEXTURE_CUBE_MAP;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
t->gl.targetIndex = OpenGLContext::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);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
break;
}
@@ -756,7 +793,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
t->gl.targetIndex = (uint8_t)
gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
@@ -827,27 +864,27 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
switch (target) {
case SamplerType::SAMPLER_EXTERNAL:
t->gl.target = GL_TEXTURE_EXTERNAL_OES;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
break;
case SamplerType::SAMPLER_2D:
t->gl.target = GL_TEXTURE_2D;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
break;
case SamplerType::SAMPLER_3D:
t->gl.target = GL_TEXTURE_3D;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
break;
case SamplerType::SAMPLER_2D_ARRAY:
t->gl.target = GL_TEXTURE_2D_ARRAY;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
break;
case SamplerType::SAMPLER_CUBEMAP:
t->gl.target = GL_TEXTURE_CUBE_MAP;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
t->gl.targetIndex = OpenGLContext::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);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
break;
}
@@ -858,7 +895,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
if (gl.features.multisample_texture) {
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
@@ -899,7 +936,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]);
GLuint const index = i;
GLint const size = getComponentCount(attribute.type);
GLint const size = (GLint)getComponentCount(attribute.type);
GLenum const type = getComponentType(attribute.type);
GLboolean const normalized = getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED);
GLsizei const stride = attribute.stride;
@@ -954,7 +991,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
}
void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
GLRenderTarget const* rt, GLenum attachment) noexcept {
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept {
#if !defined(NDEBUG)
// Only used by assert_invariant() checks below
@@ -1092,9 +1129,20 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
// TODO: support multiview for iOS and WebGL
#if !defined(__EMSCRIPTEN__) && !defined(IOS)
if (layerCount > 1) {
// if layerCount > 1, it means we use the multiview extension.
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
t->gl.id, 0, binfo.baseViewIndex, layerCount);
} else
#endif // !defined(__EMSCRIPTEN__) && !defined(IOS)
{
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
}
#endif
break;
default:
@@ -1255,11 +1303,9 @@ void OpenGLDriver::createDefaultRenderTargetR(
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
uint32_t const framebuffer = mPlatform.createDefaultRenderTarget();
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
rt->gl.isDefault = true;
rt->gl.fbo = framebuffer;
rt->gl.fbo = 0; // the actual id is resolved at binding time
rt->gl.samples = 1;
// FIXME: these flags should reflect the actual attachments present
rt->targets = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH;
@@ -1270,6 +1316,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
uint32_t width,
uint32_t height,
uint8_t samples,
uint8_t layerCount,
MRT color,
TargetBufferInfo depth,
TargetBufferInfo stencil) {
@@ -1336,7 +1383,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
if (any(targets & getTargetBufferFlagsAt(i))) {
assert_invariant(color[i].handle);
rt->gl.color[i] = handle_cast<GLTexture*>(color[i].handle);
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i);
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i, layerCount);
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
checkDimensions(rt->gl.color[i], color[i].level);
}
@@ -1358,7 +1405,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
// either we supplied only the depth handle or both depth/stencil are identical and not null
if (depth.handle && (stencil.handle == depth.handle || !stencil.handle)) {
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT);
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT, layerCount);
specialCased = true;
checkDimensions(rt->gl.depth, depth.level);
}
@@ -1369,13 +1416,13 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
if (any(targets & TargetBufferFlags::DEPTH)) {
assert_invariant(depth.handle);
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT);
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT, layerCount);
checkDimensions(rt->gl.depth, depth.level);
}
if (any(targets & TargetBufferFlags::STENCIL)) {
assert_invariant(stencil.handle);
rt->gl.stencil = handle_cast<GLTexture*>(stencil.handle);
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT);
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT, layerCount);
checkDimensions(rt->gl.stencil, stencil.level);
}
}
@@ -1417,6 +1464,13 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags);
#if !defined(__EMSCRIPTEN__)
// note: in practice this should never happen on Android
ASSERT_POSTCONDITION(sc->swapChain,
"createSwapChain(%p, 0x%lx) failed. See logs for details.",
nativeWindow, flags);
#endif
// See if we need the emulated rec709 output conversion
if (UTILS_UNLIKELY(mContext.isES2())) {
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
@@ -1431,6 +1485,13 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
sc->swapChain = mPlatform.createSwapChain(width, height, flags);
#if !defined(__EMSCRIPTEN__)
// note: in practice this should never happen on Android
ASSERT_POSTCONDITION(sc->swapChain,
"createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.",
width, height, flags);
#endif
// See if we need the emulated rec709 output conversion
if (UTILS_UNLIKELY(mContext.isES2())) {
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
@@ -1573,11 +1634,11 @@ void OpenGLDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
if (rt->gl.fbo) {
// first unbind this framebuffer if needed
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
gl.unbindFramebuffer(GL_FRAMEBUFFER);
}
if (rt->gl.fbo_read) {
// first unbind this framebuffer if needed
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
gl.unbindFramebuffer(GL_FRAMEBUFFER);
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -2411,7 +2472,9 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
size_t const stride = p.stride ? p.stride : width;
size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1);
size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
void const* const buffer = static_cast<char const*>(p.buffer) + p.left * bpp + bpr * p.top;
size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride"
void const* const buffer = static_cast<char const*>(p.buffer)
+ bpp* p.left + bpr * p.top + bpl * 0; // TODO: PBD should have a p.depth
switch (t->target) {
case SamplerType::SAMPLER_EXTERNAL:
@@ -2476,7 +2539,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (!gl.isES2()) {
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
// specify levels as they become available.
if (int8_t(level) < t->gl.baseLevel) {
t->gl.baseLevel = int8_t(level);
@@ -2577,7 +2640,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (!gl.isES2()) {
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
// specify levels as they become available.
if (int8_t(level) < t->gl.baseLevel) {
t->gl.baseLevel = int8_t(level);
@@ -2730,7 +2793,7 @@ TimerQueryResult OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
return TimerQueryFactoryInterface::getTimerQueryValue(tq, elapsedTime);
}
void OpenGLDriver::compilePrograms(CompilerPriorityQueue priority,
void OpenGLDriver::compilePrograms(CompilerPriorityQueue,
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
if (callback) {
getShaderCompilerService().notifyWhenAllProgramsAreReady(handler, callback, user);
@@ -2758,13 +2821,13 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
const TargetBufferFlags clearFlags = params.flags.clear & rt->targets;
TargetBufferFlags discardFlags = params.flags.discardStart & rt->targets;
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
if (gl.ext.EXT_discard_framebuffer
&& !gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
GLsizei const attachmentCount = getAttachments(attachments, discardFlags, !fbo);
if (attachmentCount) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
@@ -2852,9 +2915,9 @@ void OpenGLDriver::endRenderPass(int) {
}
if (!gl.bugs.disable_invalidate_framebuffer) {
// we wouldn't have to bind the framebuffer if we had glInvalidateNamedFramebuffer()
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
GLsizei const attachmentCount = getAttachments(attachments, effectiveDiscardFlags, !fbo);
if (attachmentCount) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
@@ -2916,50 +2979,48 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
}
GLsizei OpenGLDriver::getAttachments(AttachmentArray& attachments,
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept {
assert_invariant(buffers <= rt->targets);
TargetBufferFlags buffers, bool isDefaultFramebuffer) noexcept {
GLsizei attachmentCount = 0;
// the default framebuffer uses different constants!!!
const bool defaultFramebuffer = (rt->gl.fbo == 0);
if (any(buffers & TargetBufferFlags::COLOR0)) {
attachments[attachmentCount++] = defaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (any(buffers & TargetBufferFlags::COLOR1)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT1;
}
if (any(buffers & TargetBufferFlags::COLOR2)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT2;
}
if (any(buffers & TargetBufferFlags::COLOR3)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT3;
}
if (any(buffers & TargetBufferFlags::COLOR4)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT4;
}
if (any(buffers & TargetBufferFlags::COLOR5)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT5;
}
if (any(buffers & TargetBufferFlags::COLOR6)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT6;
}
if (any(buffers & TargetBufferFlags::COLOR7)) {
assert_invariant(!defaultFramebuffer);
assert_invariant(!isDefaultFramebuffer);
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT7;
}
#endif
if (any(buffers & TargetBufferFlags::DEPTH)) {
attachments[attachmentCount++] = defaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
}
if (any(buffers & TargetBufferFlags::STENCIL)) {
attachments[attachmentCount++] = defaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
}
return attachmentCount;
}
@@ -3679,8 +3740,8 @@ void OpenGLDriver::blit(
mask, GL_NEAREST);
CHECK_GL_ERROR(utils::slog.e)
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0);
gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER);
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
glDeleteFramebuffers(2, fbo);
if (any(d->usage & TextureUsage::SAMPLEABLE)) {
@@ -3784,39 +3845,42 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void OpenGLDriver::bindPipeline(PipelineState state) {
DEBUG_MARKER()
auto& gl = mContext;
setRasterState(state.rasterState);
setStencilState(state.stencilState);
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
mValidProgram = useProgram(p);
}
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors that can occur during the draw call when the program
// is invalid. The shader compile error has already been dumped to the console at this
// point, so it's fine to simply return early.
return;
}
void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
DEBUG_MARKER()
auto& gl = mContext;
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
if (UTILS_UNLIKELY(!vb)) {
mBoundRenderPrimitive = nullptr;
return;
}
gl.bindVertexArray(&rp->gl);
// If necessary, mutate the bindings in the VAO.
gl.bindVertexArray(&rp->gl);
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
updateVertexArrayObject(rp, glvb);
setRasterState(state.rasterState);
setStencilState(state.stencilState);
mBoundRenderPrimitive = rp;
}
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
}
if (UTILS_LIKELY(instanceCount <= 1)) {
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
@@ -3842,6 +3906,17 @@ void OpenGLDriver::scissor(Viewport scissor) {
setScissor(scissor);
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
state.primitiveType = rp->type;
state.vertexBufferInfo = rp->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
+29 -12
View File
@@ -18,31 +18,41 @@
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVER_H
#include "DriverBase.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include "OpenGLTimerQuery.h"
#include "ShaderCompilerService.h"
#include "private/backend/Driver.h"
#include "private/backend/HandleAllocator.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/Platform.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/Driver.h"
#include "private/backend/HandleAllocator.h"
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <utils/Allocator.h>
#include <utils/debug.h>
#include <math/vec4.h>
#include <tsl/robin_map.h>
#include <atomic>
#include <array>
#include <condition_variable>
#include <functional>
#include <memory>
#include <set>
#include <mutex>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#ifndef FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB
@@ -59,12 +69,14 @@ class OpenGLProgram;
class TimerQueryFactoryInterface;
class OpenGLDriver final : public DriverBase {
inline explicit OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept;
inline explicit OpenGLDriver(OpenGLPlatform* platform,
const Platform::DriverConfig& driverConfig) noexcept;
~OpenGLDriver() noexcept final;
Dispatcher getDispatcher() const noexcept final;
public:
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept;
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept;
class DebugMarker {
OpenGLDriver& driver;
@@ -142,6 +154,7 @@ public:
struct GLRenderPrimitive : public HwRenderPrimitive {
using HwRenderPrimitive::HwRenderPrimitive;
OpenGLContext::RenderPrimitive gl;
Handle<HwVertexBufferInfo> vbih;
};
struct GLTexture : public HwTexture {
@@ -301,7 +314,7 @@ private:
void updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb);
void framebufferTexture(TargetBufferInfo const& binfo,
GLRenderTarget const* rt, GLenum attachment) noexcept;
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept;
void setRasterState(RasterState rs) noexcept;
@@ -356,8 +369,8 @@ private:
}
using AttachmentArray = std::array<GLenum, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 2>;
static GLsizei getAttachments(AttachmentArray& attachments,
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept;
static GLsizei getAttachments(AttachmentArray& attachments, TargetBufferFlags buffers,
bool isDefaultFramebuffer) noexcept;
// state required to represent the current render pass
Handle<HwRenderTarget> mRenderPassTarget;
@@ -366,6 +379,10 @@ private:
GLboolean mRenderPassDepthWrite{};
GLboolean mRenderPassStencilWrite{};
GLRenderPrimitive const* mBoundRenderPrimitive = nullptr;
bool mValidProgram = false;
void clearWithRasterPipe(TargetBufferFlags clearFlags,
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
+11 -3
View File
@@ -40,7 +40,7 @@ OpenGLPlatform::~OpenGLPlatform() noexcept = default;
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
makeCurrent(drawSwapChain, readSwapChain);
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
}
bool OpenGLPlatform::isProtectedContextSupported() const noexcept {
@@ -51,10 +51,14 @@ bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
return false;
}
uint32_t OpenGLPlatform::createDefaultRenderTarget() noexcept {
uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
return 0;
}
OpenGLPlatform::ContextType OpenGLPlatform::getCurrentContextType() const noexcept {
return ContextType::UNPROTECTED;
}
void OpenGLPlatform::setPresentationTime(
UTILS_UNUSED int64_t presentationTimeInNanosecond) noexcept {
}
@@ -125,10 +129,14 @@ AcquiredImage OpenGLPlatform::transformAcquiredImage(AcquiredImage source) noexc
return source;
}
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain* swapChain) noexcept {
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain*) noexcept {
return TargetBufferFlags::NONE;
}
bool OpenGLPlatform::isSwapChainProtected(UTILS_UNUSED SwapChain*) noexcept {
return false;
}
bool OpenGLPlatform::isExtraContextSupported() const noexcept {
return false;
}
+11 -2
View File
@@ -16,16 +16,25 @@
#include "OpenGLProgram.h"
#include "BlobCacheKey.h"
#include "GLUtils.h"
#include "OpenGLDriver.h"
#include "ShaderCompilerService.h"
#include <backend/Program.h>
#include <private/backend/BackendUtils.h>
#include <utils/debug.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <private/backend/BackendUtils.h>
#include <array>
#include <string_view>
#include <utility>
#include <new>
#include <stddef.h>
namespace filament::backend {
@@ -28,6 +28,9 @@
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <array>
#include <limits>
#include <stddef.h>
#include <stdint.h>
@@ -30,6 +30,7 @@
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <cctype>
#include <chrono>
#include <string>
#include <string_view>
@@ -264,6 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
program.isMultiview(),
shaders,
token->shaderSourceCode);
@@ -299,6 +301,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
program.isMultiview(),
token->gl.shaders,
token->shaderSourceCode);
@@ -501,6 +504,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
void ShaderCompilerService::compileShaders(OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview,
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
@@ -514,8 +518,16 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
};
std::string specializationConstantString;
int32_t numViews = 2;
for (auto const& sc : specializationConstants) {
appendSpecConstantString(specializationConstantString, sc);
if (sc.id == 8) {
// This constant must match
// ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT
// which we can't use here because it's defined in EngineEnums.h.
// (we're breaking layering here, but it's for the good cause).
numViews = std::get<int32_t>(sc.value);
}
}
if (!specializationConstantString.empty()) {
specializationConstantString += '\n';
@@ -544,17 +556,23 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
if (UTILS_LIKELY(!shadersSource[i].empty())) {
Program::ShaderBlob& shader = shadersSource[i];
char* shader_src = reinterpret_cast<char*>(shader.data());
size_t shader_len = shader.size();
// remove GOOGLE_cpp_style_line_directive
std::string_view const source = process_GOOGLE_cpp_style_line_directive(context,
reinterpret_cast<char*>(shader.data()), shader.size());
process_GOOGLE_cpp_style_line_directive(context, shader_src, shader_len);
// replace the value of layout(num_views = X) for multiview extension
if (multiview && stage == ShaderStage::VERTEX) {
process_OVR_multiview2(context, numViews, shader_src, shader_len);
}
// add support for ARB_shading_language_packing if needed
auto const packingFunctions = process_ARB_shading_language_packing(context);
// split shader source, so we can insert the specialization constants and the packing
// functions
auto const [prolog, body] = splitShaderSource(source);
auto const [prolog, body] = splitShaderSource({ shader_src, shader_len });
const std::array<const char*, 4> sources = {
prolog.data(),
@@ -577,7 +595,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
#ifndef NDEBUG
// for debugging we return the original shader source (without the modifications we
// made here), otherwise the line numbers wouldn't match.
outShaderSourceCode[i] = { source.data(), source.length() };
outShaderSourceCode[i] = { shader_src, shader_len };
#endif
outShaders[i] = shaderId;
@@ -586,15 +604,59 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
}
// If usages of the Google-style line directive are present, remove them, as some
// drivers don't allow the quotation marks. This happens in-place.
std::string_view ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
// drivers don't allow the quotation marks. This source modification happens in-place.
void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept {
if (!context.ext.GOOGLE_cpp_style_line_directive) {
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
removeGoogleLineDirectives(source, len); // length is unaffected
}
}
return { source, len };
}
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
// the number of views, which is assumed as a single digit, for multiview.
// This source modification happens in-place.
void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
int32_t eyeCount, char* source, size_t len) noexcept {
// We don't use regular expression in favor of performance.
if (context.ext.OVR_multiview2) {
const std::string_view shader{ source, len };
const std::string_view layout = "layout";
const std::string_view num_views = "num_views";
size_t found = 0;
while (true) {
found = shader.find(layout, found);
if (found == std::string_view::npos) {
break;
}
found = shader.find_first_not_of(' ', found + layout.size());
if (found == std::string_view::npos || shader[found] != '(') {
continue;
}
found = shader.find_first_not_of(' ', found + 1);
if (found == std::string_view::npos) {
continue;
}
if (shader.compare(found, num_views.size(), num_views) != 0) {
continue;
}
found = shader.find_first_not_of(' ', found + num_views.size());
if (found == std::string_view::npos || shader[found] != '=') {
continue;
}
found = shader.find_first_not_of(' ', found + 1);
if (found == std::string_view::npos) {
continue;
}
// We assume the value should be one-digit number.
assert_invariant(eyeCount < 10);
assert_invariant(!::isdigit(source[found + 1]));
source[found] = '0' + eyeCount;
break;
}
}
}
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
@@ -134,10 +134,14 @@ private:
OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview,
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept;
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
char* source, size_t len) noexcept;
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
+6 -2
View File
@@ -67,12 +67,14 @@ PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
#ifdef GL_KHR_parallel_shader_compile
PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#endif
#ifdef GL_OVR_multiview
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
// use getProcAddress for ES3.1 and above entry points.
PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
static std::once_flag sGlExtInitialized;
#endif // __EMSCRIPTEN__
@@ -118,9 +120,11 @@ void importGLESExtensionsEntryPoints() {
#ifdef GL_KHR_parallel_shader_compile
getProcAddress(glMaxShaderCompilerThreadsKHR, "glMaxShaderCompilerThreadsKHR");
#endif
#ifdef GL_OVR_multiview
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
getProcAddress(glDispatchCompute, "glDispatchCompute");
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
#endif
});
#endif // __EMSCRIPTEN__
+3 -1
View File
@@ -151,9 +151,11 @@ extern PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
#ifdef GL_KHR_parallel_shader_compile
extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#endif
#ifdef GL_OVR_multiview
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#endif // __EMSCRIPTEN__
} // namespace glext
@@ -247,8 +247,8 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
delete cocoaSwapChain;
}
void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
@@ -287,6 +287,7 @@ void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
swapChain->previousBounds = currentBounds;
swapChain->previousWindowFrame = currentWindowFrame;
return true;
}
void PlatformCocoaGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -143,11 +143,12 @@ void PlatformCocoaTouchGL::destroySwapChain(Platform::SwapChain* swapChain) noex
}
}
uint32_t PlatformCocoaTouchGL::createDefaultRenderTarget() noexcept {
uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
return pImpl->mDefaultFramebuffer;
}
void PlatformCocoaTouchGL::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
@@ -182,6 +183,7 @@ void PlatformCocoaTouchGL::makeCurrent(SwapChain* drawSwapChain, SwapChain* read
ASSERT_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE, "Incomplete framebuffer.");
glBindFramebuffer(GL_FRAMEBUFFER, oldFramebuffer);
}
return true;
}
void PlatformCocoaTouchGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -25,8 +25,9 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#if defined(__ANDROID__)
#include <EGL/eglplatform.h>
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
#include <utils/compiler.h>
@@ -282,12 +283,14 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
}
}
if (UTILS_UNLIKELY(makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
if (UTILS_UNLIKELY(
egl.makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
// eglMakeCurrent failed
logEglError("eglMakeCurrent");
goto error;
}
mCurrentContextType = ContextType::UNPROTECTED;
mContextAttribs = std::move(contextAttribs);
initializeGlExtensions();
@@ -362,26 +365,6 @@ void PlatformEGL::releaseContext() noexcept {
eglReleaseThread();
}
EGLBoolean PlatformEGL::makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
EGLBoolean PlatformEGL::makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface) noexcept {
if (UTILS_UNLIKELY((mCurrentContext != context ||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
EGLBoolean const success = eglMakeCurrent(
mEGLDisplay, drawSurface, readSurface, context);
if (success) {
mCurrentDrawSurface = drawSurface;
mCurrentReadSurface = readSurface;
mCurrentContext = context;
}
return success;
}
return EGL_TRUE;
}
void PlatformEGL::terminate() noexcept {
// it's always allowed to use EGL_NO_SURFACE, EGL_NO_CONTEXT
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -468,30 +451,10 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
return ext.egl.KHR_gl_colorspace;
}
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain const* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
}
return false;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t flags) noexcept {
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
} else {
config = mEGLConfig;
}
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
return nullptr;
}
Config attribs;
if (ext.egl.KHR_gl_colorspace) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
@@ -508,16 +471,27 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
}
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config,
(EGLNativeWindowType)nativeWindow, attribs.data());
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreateWindowSurface");
return nullptr;
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
} else {
config = mEGLConfig;
}
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
EGLSurface sur = EGL_NO_SURFACE;
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
sur = eglCreateWindowSurface(mEGLDisplay, config,
(EGLNativeWindowType)nativeWindow, attribs.data());
if (UTILS_LIKELY(sur != EGL_NO_SURFACE)) {
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
} else {
logEglError("PlatformEGL::createSwapChain: eglCreateWindowSurface");
}
} else {
// error already logged
}
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
@@ -532,17 +506,6 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
Platform::SwapChain* PlatformEGL::createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) noexcept {
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, false, true);
} else {
config = mEGLConfig;
}
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
return nullptr;
}
Config attribs = {
{ EGL_WIDTH, EGLint(width) },
{ EGL_HEIGHT, EGLint(height) },
@@ -564,17 +527,28 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
}
EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
} else {
config = mEGLConfig;
}
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreatePbufferSurface");
return nullptr;
EGLSurface sur = EGL_NO_SURFACE;
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("PlatformEGL::createSwapChain: eglCreatePbufferSurface");
}
} else {
// error already logged
}
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.config = config
.config = config,
.flags = flags
});
return sc;
}
@@ -583,29 +557,45 @@ void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
makeCurrent(mCurrentContext, mEGLDummySurface, mEGLDummySurface);
egl.makeCurrent(mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sc->sur);
delete sc;
}
}
}
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
}
return false;
}
OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept {
return mCurrentContextType;
}
bool PlatformEGL::makeCurrent(ContextType type,
SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
makeCurrent(mEGLContext, dsc->sur, rsc->sur);
EGLContext context = getContextForType(type);
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
return success == EGL_TRUE ? true : false;
}
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
EGLContext context = mEGLContext;
assert_invariant(drawSwapChain);
assert_invariant(readSwapChain);
ContextType type = ContextType::UNPROTECTED;
if (ext.egl.EXT_protected_content) {
bool const swapChainProtected = PlatformEGL::isSwapChainProtected(dsc);
bool const swapChainProtected = isSwapChainProtected(drawSwapChain);
if (UTILS_UNLIKELY(swapChainProtected)) {
// we need a protected context
if (UTILS_UNLIKELY(mEGLContextProtected == EGL_NO_CONTEXT)) {
@@ -622,34 +612,36 @@ void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
goto error;
}
}
context = mEGLContextProtected;
type = ContextType::PROTECTED;
error: ;
}
bool const contextChange = context != mCurrentContext;
bool const contextChange = type != mCurrentContextType;
mCurrentContextType = type;
if (UTILS_UNLIKELY(contextChange)) {
preContextChange();
EGLBoolean const success = makeCurrent(context, dsc->sur, rsc->sur);
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
if (UTILS_UNLIKELY(!success)) {
logEglError("PlatformEGL::makeCurrent");
if (mEGLContextProtected != EGL_NO_CONTEXT) {
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
mEGLContextProtected = EGL_NO_CONTEXT;
}
context = mEGLContext;
mCurrentContextType = ContextType::UNPROTECTED;
}
if (UTILS_LIKELY(!swapChainProtected && mEGLContextProtected != EGL_NO_CONTEXT)) {
// We don't need the protected context anymore, unbind and destroy right away.
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
mEGLContextProtected = EGL_NO_CONTEXT;
}
size_t const contextIndex = (context == mEGLContext) ? 0 : 1;
size_t const contextIndex = (mCurrentContextType == ContextType::PROTECTED) ? 1 : 0;
postContextChange(contextIndex);
return;
}
}
EGLBoolean const success = makeCurrent(context, dsc->sur, rsc->sur);
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
if (UTILS_UNLIKELY(!success)) {
logEglError("PlatformEGL::makeCurrent");
}
@@ -746,6 +738,17 @@ void PlatformEGL::initializeGlExtensions() noexcept {
ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
}
EGLContext PlatformEGL::getContextForType(OpenGLPlatform::ContextType type) const noexcept {
switch (type) {
case ContextType::NONE:
return EGL_NO_CONTEXT;
case ContextType::UNPROTECTED:
return mEGLContext;
case ContextType::PROTECTED:
return mEGLContextProtected;
}
}
// ---------------------------------------------------------------------------------------------
PlatformEGL::Config::Config() = default;
@@ -782,4 +785,23 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
}
}
// ------------------------------------------------------------------------------------------------
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
EGLSurface readSurface) noexcept {
if (UTILS_UNLIKELY((
mCurrentContext != context ||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
EGLBoolean const success = eglMakeCurrent(
mEGLDisplay, drawSurface, readSurface, context);
if (success) {
mCurrentDrawSurface = drawSurface;
mCurrentReadSurface = readSurface;
mCurrentContext = context;
}
return success;
}
return EGL_TRUE;
}
} // namespace filament::backend
@@ -14,21 +14,34 @@
* limitations under the License.
*/
#include <backend/AcquiredImage.h>
#include <backend/Platform.h>
#include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include "opengl/GLUtils.h"
#include "ExternalStreamManagerAndroid.h"
#include <android/api-level.h>
#include <android/hardware_buffer.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/Log.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <sys/system_properties.h>
#include <jni.h>
#include <new>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
// We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
// the minimum for Jetpack at the time of this comment.
@@ -129,11 +142,12 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
}
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
if (mCurrentDrawSurface != EGL_NO_SURFACE) {
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
if (currentDrawSurface != EGL_NO_SURFACE) {
if (eglPresentationTimeANDROID) {
eglPresentationTimeANDROID(
mEGLDisplay,
mCurrentDrawSurface,
currentDrawSurface,
presentationTimeInNanosecond);
}
}
@@ -165,14 +179,28 @@ int PlatformEGLAndroid::getOSVersion() const noexcept {
AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept {
// Convert the AHardwareBuffer to EGLImage.
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image);
AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image;
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
if (!clientBuffer) {
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
return {};
}
// Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT.
EGLint attrs[] = { EGL_NONE, EGL_NONE };
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
PlatformEGL::Config attributes;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(pHardwareBuffer, &desc);
bool const isProtectedContent =
desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
if (isProtectedContent) {
attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
}
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
if (eglImage == EGL_NO_IMAGE_KHR) {
slog.e << "eglCreateImageKHR returned no image." << io::endl;
return {};
@@ -265,10 +265,11 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
}
void PlatformGLX::makeCurrent(
Platform::SwapChain* drawSwapChain, Platform::SwapChain* readSwapChain) noexcept {
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
g_glx.setCurrentContext(mGLXDisplay,
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
return true;
}
void PlatformGLX::commit(Platform::SwapChain* swapChain) noexcept {
@@ -255,8 +255,8 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
wglMakeCurrent(mWhdc, mContext);
}
void PlatformWGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"PlatformWGL does not support distinct draw/read swap chains.");
@@ -269,6 +269,7 @@ void PlatformWGL::makeCurrent(Platform::SwapChain* drawSwapChain,
wglMakeCurrent(0, NULL);
}
}
return true;
}
void PlatformWGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -46,8 +46,9 @@ Platform::SwapChain* PlatformWebGL::createSwapChain(
void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
void PlatformWebGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
return true;
}
void PlatformWebGL::commit(Platform::SwapChain* swapChain) noexcept {
@@ -154,15 +154,9 @@ struct BlitterUniforms {
}// anonymous namespace
VulkanBlitter::VulkanBlitter() noexcept = default;
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, VulkanCommands* commands) noexcept {
mPhysicalDevice = physicalDevice;
mDevice = device;
mAllocator = allocator;
mCommands = commands;
}
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
: mPhysicalDevice(physicalDevice),
mCommands(commands) {}
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
+1 -6
View File
@@ -32,10 +32,7 @@ struct VulkanProgram;
class VulkanBlitter {
public:
VulkanBlitter() noexcept;
void initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, VulkanCommands* commands) noexcept;
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
void blit(VkFilter filter,
VulkanAttachment dst, const VkOffset3D* dstRectPair,
@@ -47,8 +44,6 @@ public:
private:
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
VkDevice mDevice;
VmaAllocator mAllocator;
VulkanCommands* mCommands;
};
@@ -155,7 +155,7 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam
#endif
}
VulkanCommands::~VulkanCommands() {
void VulkanCommands::terminate() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
@@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() {
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
<< utils::io::flush;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
#else
mGroupMarkers->pop();
#endif
+48 -47
View File
@@ -139,71 +139,72 @@ public:
// - We do this because vkGetFenceStatus must be called from the rendering thread.
//
class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator);
~VulkanCommands();
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator);
// Creates a "current" command buffer if none exists, otherwise returns the current one.
VulkanCommandBuffer& get();
void terminate();
// Submits the current command buffer if it exists, then sets "current" to null.
// If there are no outstanding commands then nothing happens and this returns false.
bool flush();
// Creates a "current" command buffer if none exists, otherwise returns the current one.
VulkanCommandBuffer& get();
// Returns the "rendering finished" semaphore for the most recent flush and removes
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal();
// Submits the current command buffer if it exists, then sets "current" to null.
// If there are no outstanding commands then nothing happens and this returns false.
bool flush();
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
void injectDependency(VkSemaphore next);
// Returns the "rendering finished" semaphore for the most recent flush and removes
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal();
// Destroys all command buffers that are no longer in use.
void gc();
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
void injectDependency(VkSemaphore next);
// Waits for all outstanding command buffers to finish.
void wait();
// Destroys all command buffers that are no longer in use.
void gc();
// Updates the atomic "status" variable in every extant fence.
void updateFences();
// Waits for all outstanding command buffers to finish.
void wait();
// Sets an observer who is notified every time a new command buffer has been made "current".
// The observer's event handler can only be called during get().
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
// Updates the atomic "status" variable in every extant fence.
void updateFences();
// Sets an observer who is notified every time a new command buffer has been made "current".
// The observer's event handler can only be called during get().
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker();
void popGroupMarker();
void insertEventMarker(char const* string, uint32_t len);
void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const;
std::string getTopGroupMarker() const;
#endif
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice;
VkQueue const mQueue;
VkCommandPool const mPool;
VulkanContext const* mContext;
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice;
VkQueue const mQueue;
VkCommandPool const mPool;
VulkanContext const* mContext;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
int8_t mCurrentCommandBufferIndex = -1;
VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
int8_t mCurrentCommandBufferIndex = -1;
VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
#endif
};
+23 -10
View File
@@ -43,9 +43,12 @@
// Enables Android systrace
#define FVK_DEBUG_SYSTRACE 0x00000001
// Group markers are used to denote collection of GPU commands. It is typically at the granualarity
// of a renderpass.
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
// Group markers are used to denote collections of GPU commands. It is typically at the
// granualarity of a renderpass. You can enable this along with FVK_DEBUG_DEBUG_UTILS to take
// advantage of vkCmdBegin/EndDebugUtilsLabelEXT. You can also just enable this with
// FVK_DEBUG_PRINT_GROUP_MARKERS to print the current marker to stdout.
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
#define FVK_DEBUG_TEXTURE 0x00000004
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
#define FVK_DEBUG_COMMAND_BUFFER 0x00000010
@@ -63,11 +66,14 @@
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
// Usefaul default combinations
// Use this to debug potential Handle/Resource leakage. It will print out reference counts for all
// the currently active resources.
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
// Useful default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
FVK_DEBUG_SYSTRACE | \
FVK_DEBUG_GROUP_MARKERS
FVK_DEBUG_SYSTRACE
#define FVK_DEBUG_CORRECTNESS \
FVK_DEBUG_VALIDATION | \
@@ -80,14 +86,12 @@
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION)
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE)
#else
#define FVK_DEBUG_FLAGS 0
#endif
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
// Group marker only works only if validation or debug utils is enabled since it uses
// vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT
@@ -107,6 +111,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
// end dependcy checks
// Shorthand for combination of enabled debug flags
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1
#else
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0
#endif
// end shorthands
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
+140 -102
View File
@@ -209,10 +209,16 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
mContext(context),
mResourceAllocator(driverConfig.handleArenaSize),
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
mResourceManager(&mResourceAllocator),
mThreadSafeResourceManager(&mResourceAllocator),
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
mPipelineCache(&mResourceAllocator),
mStagePool(mAllocator, &mCommands),
mFramebufferCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
@@ -237,23 +243,13 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
#endif
mTimestamps = std::make_unique<VulkanTimestamps>(mPlatform->getDevice());
mCommands = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext,
&mResourceAllocator);
mCommands->setObserver(&mPipelineCache);
mCommands.setObserver(&mPipelineCache);
mPipelineCache.setDevice(mPlatform->getDevice(), mAllocator);
// TOOD: move them all to be initialized by constructor
mStagePool.initialize(mAllocator, mCommands.get());
mFramebufferCache.initialize(mPlatform->getDevice());
mSamplerCache.initialize(mPlatform->getDevice());
mEmptyTexture.reset(createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
mContext, mAllocator, mCommands.get(), mStagePool));
mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
mContext, mAllocator, &mCommands, mStagePool);
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
mCommands.get());
}
VulkanDriver::~VulkanDriver() noexcept = default;
@@ -264,26 +260,28 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// VulkanSamplerGroup : 24 few
// VulkanSamplerGroup : 16 few
// HwStream : 24 few
// VulkanFence : 40 few
// VulkanProgram : 40 moderate
// VulkanIndexBuffer : 72 moderate
// VulkanBufferObject : 72 many
// -- less than or equal 80 bytes
// VulkanRenderPrimitive : 104 many
// VulkanSwapChain : 112 few
// VulkanTimerQuery : 168 few
// -- less than or equal 176 bytes
// VulkanTexture : 232 moderate
// VulkanVertexBuffer : 312 moderate
// VulkanRenderTarget : 320 few
// -- less than or equal to 320 bytes
// VulkanFence : 32 few
// VulkanProgram : 32 moderate
// VulkanIndexBuffer : 64 moderate
// VulkanBufferObject : 64 many
// -- less than or equal 64 bytes
// VulkanRenderPrimitive : 72 many
// VulkanVertexBufferInfo : 96 moderate
// VulkanSwapChain : 104 few
// VulkanTimerQuery : 160 few
// -- less than or equal 160 bytes
// VulkanVertexBuffer : 192 moderate
// VulkanTexture : 224 moderate
// VulkanRenderTarget : 312 few
// -- less than or equal to 312 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
@@ -314,8 +312,11 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
void VulkanDriver::terminate() {
// Command buffers should come first since it might have commands depending on resources that
// are about to be destroyed.
mCommands.reset();
mEmptyTexture.reset();
mCommands.terminate();
delete mEmptyTexture;
mResourceManager.clear();
mTimestamps.reset();
mBlitter.terminate();
@@ -344,7 +345,7 @@ void VulkanDriver::terminate() {
}
void VulkanDriver::tick(int) {
mCommands->updateFences();
mCommands.updateFences();
}
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
@@ -356,9 +357,14 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_START("gc");
// Command buffers need to be submitted and completed before other resources can be gc'd. And
// its gc() function carrys out the *wait*.
mCommands->gc();
mCommands.gc();
mStagePool.gc();
mFramebufferCache.gc();
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
mResourceAllocator.print();
#endif
FVK_SYSTRACE_END();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -379,7 +385,7 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
void VulkanDriver::endFrame(uint32_t frameId) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endframe");
mCommands->flush();
mCommands.flush();
collectGarbage();
FVK_SYSTRACE_END();
}
@@ -387,7 +393,7 @@ void VulkanDriver::endFrame(uint32_t frameId) {
void VulkanDriver::flush(int) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("flush");
mCommands->flush();
mCommands.flush();
FVK_SYSTRACE_END();
}
@@ -395,8 +401,8 @@ void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("finish");
mCommands->flush();
mCommands->wait();
mCommands.flush();
mCommands.wait();
mReadPixels.runUntilComplete();
FVK_SYSTRACE_END();
@@ -411,9 +417,9 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t coun
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph, &mResourceAllocator);
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph,
&mResourceAllocator, pt, vbh, ibh);
mResourceManager.acquire(rp);
VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -493,7 +499,7 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
format, samples, w, h, depth, usage, mStagePool);
mResourceManager.acquire(vktexture);
}
@@ -505,7 +511,7 @@ void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targ
TextureSwizzle swizzleArray[] = {r, g, b, a};
const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
format, samples, w, h, depth, usage, mStagePool, false /*heap allocated */, swizzleMap);
mResourceManager.acquire(vktexture);
}
@@ -548,7 +554,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = {std::numeric_limits<uint32_t>::max()};
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = {0};
UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0;
@@ -600,7 +606,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
assert_invariant(tmin.x >= width && tmin.y >= height);
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), width, height,
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
samples, colorTargets, depthStencil, mStagePool);
mResourceManager.acquire(renderTarget);
}
@@ -618,7 +624,7 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
VulkanCommandBuffer const& commandBuffer = mCommands->get();
VulkanCommandBuffer const& commandBuffer = mCommands.get();
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
}
@@ -629,7 +635,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
mAllocator, mCommands.get(), mStagePool, nativeWindow, flags);
mAllocator, &mCommands, mStagePool, nativeWindow, flags);
mResourceManager.acquire(swapChain);
}
@@ -642,7 +648,7 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
}
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
mAllocator, &mCommands, mStagePool, nullptr, flags, VkExtent2D{width, height});
mResourceManager.acquire(swapChain);
}
@@ -982,13 +988,12 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
auto vb = mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh);
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
vb->setBuffer(mResourceAllocator, bo, index);
}
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mCommands.get();
auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh);
commands.acquire(ib);
ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size);
@@ -998,7 +1003,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mCommands.get();
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
commands.acquire(bo);
@@ -1009,7 +1014,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
BufferDescriptor&& bd, uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mCommands.get();
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
commands.acquire(bo);
// TODO: implement unsynchronized version
@@ -1174,7 +1179,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
// the non-sampling case.
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer const cmdbuffer = commands.buffer();
UTILS_NOUNROLL
@@ -1301,7 +1306,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
auto const topMarker = mCommands->getTopGroupMarker();
auto const topMarker = mCommands.getTopGroupMarker();
if (!topMarker.empty()) {
DebugUtils::setName(VK_OBJECT_TYPE_FRAMEBUFFER, reinterpret_cast<uint64_t>(vkfb),
topMarker.c_str());
@@ -1390,7 +1395,7 @@ void VulkanDriver::endRenderPass(int) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endRenderPass");
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
vkCmdEndRenderPass(cmdbuffer);
@@ -1443,7 +1448,7 @@ void VulkanDriver::nextSubpass(int) {
assert_invariant(renderTarget);
assert_invariant(mCurrentRenderPass.params.subpassMask);
vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
++mCurrentRenderPass.currentSubpass);
@@ -1460,14 +1465,6 @@ void VulkanDriver::nextSubpass(int) {
}
}
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
auto primitive = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
primitive->setBuffers(mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh),
mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh));
primitive->setPrimitiveType(pt);
}
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
@@ -1532,14 +1529,14 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands->insertEventMarker(string, len);
mCommands.insertEventMarker(string, len);
#endif
}
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
// Turns out all the markers are 0-terminated, so we can just pass it without len.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands->pushGroupMarker(string);
mCommands.pushGroupMarker(string);
#endif
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START(string);
@@ -1547,7 +1544,7 @@ void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
void VulkanDriver::popGroupMarker(int) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands->popGroupMarker();
mCommands.popGroupMarker();
#endif
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_END();
@@ -1560,7 +1557,7 @@ void VulkanDriver::stopCapture(int) {}
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& pbd) {
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
mCommands->flush();
mCommands.flush();
mReadPixels.run(
srcTarget, x, y, width, height, mPlatform->getGraphicsQueueFamilyIndex(),
std::move(pbd),
@@ -1713,14 +1710,13 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void VulkanDriver::bindPipeline(PipelineState pipelineState) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
VulkanCommandBuffer* commands = &mCommands.get();
const VulkanVertexBufferInfo& vbi =
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
Handle<HwProgram> programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
@@ -1728,15 +1724,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
commands->acquire(program);
commands->acquire(prim.indexBuffer);
commands->acquire(prim.vertexBuffer);
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
@@ -1762,20 +1749,19 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
.depthBiasSlopeFactor = depthOffset.slope
};
// unfortunately in Vulkan the topology is per pipeline
VkPrimitiveTopology const topology =
VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType);
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->attributes.size();
VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets();
VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions();
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
mPipelineCache.bindPrimitiveTopology(topology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount());
// Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding),
// where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract
@@ -1787,6 +1773,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
VulkanPipelineCache::UsageFlags usage = program->getUsage();
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
auto const& bindingToName = program->getBindingToName();
#endif
UTILS_NOUNROLL
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
uint16_t const indexPair = bindingToSamplerIndex[binding];
@@ -1819,15 +1809,21 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'";
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
utils::slog.w << " at binding point " << +binding << utils::io::endl;
#endif
texture = mEmptyTexture.get();
texture = mEmptyTexture;
}
SamplerParams const& samplerParams = boundSampler->s;
VkSampler const vksampler = mSamplerCache.getSampler(samplerParams);
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
reinterpret_cast<uint64_t>(vksampler), bindingToName[binding].c_str());
#endif
VkImageView imageView = VK_NULL_HANDLE;
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -1851,19 +1847,33 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage);
// Bind new descriptor sets if they need to change.
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
// message since the validation layers already do so.
if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
return;
}
// Bind a new pipeline if the pipeline state changed.
// If allocation failed, skip the draw call and bail. We do not emit an error since the
// validation layer will already do so.
if (!mPipelineCache.bindPipeline(commands)) {
return;
}
FVK_SYSTRACE_END();
}
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("bindRenderPrimitive");
VulkanCommandBuffer* commands = &mCommands.get();
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
commands->acquire(prim.indexBuffer);
commands->acquire(prim.vertexBuffer);
// This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->getAttributeCount();
VkDeviceSize const* offsets = vbi->getOffsets();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
@@ -1872,22 +1882,50 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw2");
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
// Bind new descriptor sets if they need to change.
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
// message since the validation layers already do so.
if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
return;
}
// Finally, make the actual draw call. TODO: support subranges
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
state.primitiveType = rp->type;
state.vertexBufferInfo = rp->vertexBuffer->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::scissor(Viewport scissorBox) {
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
// Set scissoring.
// clamp left-bottom to 0,0 and avoid overflows
@@ -1914,12 +1952,12 @@ void VulkanDriver::scissor(Viewport scissorBox) {
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
mTimestamps->beginQuery(&(mCommands->get()), vtq);
mTimestamps->beginQuery(&(mCommands.get()), vtq);
}
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
mTimestamps->endQuery(&(mCommands->get()), vtq);
mTimestamps->endQuery(&(mCommands.get()), vtq);
}
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
+3 -6
View File
@@ -101,16 +101,12 @@ private:
VulkanDriver& operator=(VulkanDriver const&) = delete;
private:
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh);
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
std::unique_ptr<VulkanCommands> mCommands;
std::unique_ptr<VulkanTimestamps> mTimestamps;
std::unique_ptr<VulkanTexture> mEmptyTexture;
VulkanTexture* mEmptyTexture;
VulkanSwapChain* mCurrentSwapChain = nullptr;
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
@@ -126,6 +122,7 @@ private:
// thread.
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
VulkanCommands mCommands;
VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool;
VulkanFboCache mFramebufferCache;
@@ -64,7 +64,8 @@ bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k
return true;
}
void VulkanFboCache::initialize(VkDevice device) noexcept { mDevice = device; }
VulkanFboCache::VulkanFboCache(VkDevice device)
: mDevice(device) {}
VulkanFboCache::~VulkanFboCache() {
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
@@ -238,7 +239,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
.format = config.colorFormat[i],
.samples = (VkSampleCountFlagBits) config.samples,
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
.storeOp = config.samples == 1 ? kEnableStore : kDisableStore,
.storeOp = kEnableStore,
.stencilLoadOp = kDontCare,
.stencilStoreOp = kDisableStore,
.initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear)
+1 -2
View File
@@ -104,10 +104,9 @@ public:
bool operator()(const FboKey& k1, const FboKey& k2) const;
};
explicit VulkanFboCache(VkDevice device);
~VulkanFboCache();
void initialize(VkDevice device) noexcept;
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey config) noexcept;
+46 -62
View File
@@ -99,6 +99,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
#endif
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
auto& bindingToName = mInfo->bindingToName;
#endif
auto& groupInfo = builder.getSamplerGroupInfo();
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
auto& usage = mInfo->usage;
@@ -109,12 +113,16 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
uint32_t const binding = samplers[i].binding;
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
bindingToName[binding] = samplers[i].name.c_str();
#endif
}
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl;
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
<< ", " << modules[1] << ")" << utils::io::endl;
#endif
}
@@ -281,32 +289,21 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co
return count;
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
attributes(attributes) {
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
mInfo(attributes.size()) {
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mResources(allocator) {
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
auto attribToBufferIndex = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
std::fill(mInfo.mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo.mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
VulkanVertexBufferInfo const* const vbi = allocator->handle_cast<VulkanVertexBufferInfo*>(vbih);
mInfo = new PipelineInfo(vbi->attributes.size());
auto attribDesc = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo->mSoa.data<PipelineInfo::OFFSETS>();
auto attribToBufferIndex = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
std::fill(mInfo->mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo->mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) {
Attribute attrib = vbi->attributes[attribIndex];
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute attrib = attributes[attribIndex];
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger);
@@ -318,7 +315,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
// expects to receive floats or ints.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
attrib = vbi->attributes[0];
attrib = attributes[0];
}
offsets[attribIndex] = attrib.offset;
attribDesc[attribIndex] = {
@@ -334,18 +331,23 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
}
}
VulkanVertexBuffer::~VulkanVertexBuffer() {
delete mInfo;
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
mResources(allocator) {
}
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
VulkanVertexBufferInfo const* const vbi =
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
size_t count = vbi->attributes.size();
auto vkbuffers = mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
auto attribToBuffer = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
size_t const count = vbi->getAttributeCount();
VkBuffer* const vkbuffers = getVkBuffers();
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
if (attribToBuffer[attribIndex] == static_cast<int8_t>(index)) {
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
@@ -361,35 +363,6 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
this->type = pt;
switch (pt) {
case PrimitiveType::POINTS:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
case PrimitiveType::LINES:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
break;
case PrimitiveType::LINE_STRIP:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
break;
case PrimitiveType::TRIANGLES:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
break;
case PrimitiveType::TRIANGLE_STRIP:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
break;
}
}
void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer,
VulkanIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
@@ -421,4 +394,15 @@ bool VulkanTimerQuery::isCompleted() noexcept {
VulkanTimerQuery::~VulkanTimerQuery() = default;
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(resourceAllocator) {
type = pt;
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
} // namespace filament::backend
+65 -45
View File
@@ -64,6 +64,12 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->bindingToSamplerIndex;
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
return mInfo->bindingToName;
}
#endif
private:
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
@@ -71,7 +77,11 @@ private:
struct PipelineInfo {
PipelineInfo()
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {}
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff)
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
, bindingToName(MAX_SAMPLER_COUNT, "")
#endif
{}
// This bitset maps to each of the sampler in the sampler groups associated with this
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
@@ -80,6 +90,12 @@ private:
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
// We store the sampler name mapped from binding index (only for debug purposes).
utils::FixedCapacityVector<std::string> bindingToName;
#endif
};
PipelineInfo* mInfo;
@@ -132,7 +148,48 @@ struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
AttributeArray attributes;
inline VkVertexInputAttributeDescription const* getAttribDescriptions() const {
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
}
inline VkVertexInputBindingDescription const* getBufferDescriptions() const {
return mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
}
inline int8_t const* getAttributeToBuffer() const {
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
}
inline VkDeviceSize const* getOffsets() const {
return mInfo.mSoa.data<PipelineInfo::OFFSETS>();
}
size_t getAttributeCount() const noexcept {
return mInfo.mSoa.size();
}
private:
struct PipelineInfo {
PipelineInfo(size_t size) : mSoa(size /* capacity */) {
mSoa.resize(size);
}
// These correspond to the index of the element in the SoA
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
static constexpr uint8_t OFFSETS = 2;
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3;
utils::StructureOfArrays<
VkVertexInputAttributeDescription,
VkVertexInputBindingDescription,
VkDeviceSize,
int8_t
> mSoa;
};
PipelineInfo mInfo;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
@@ -140,53 +197,20 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
~VulkanVertexBuffer();
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
}
inline VkVertexInputBindingDescription const* getBufferDescriptions() {
return mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
}
inline VkBuffer const* getVkBuffers() const {
return mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
return mBuffers.data();
}
inline VkDeviceSize const* getOffsets() const {
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
inline VkBuffer* getVkBuffers() {
return mBuffers.data();
}
Handle<HwVertexBufferInfo> vbih;
private:
struct PipelineInfo {
PipelineInfo(size_t size)
: mSoa(size /* capacity */) {
mSoa.resize(size);
}
// These correspond to the index of the element in the SoA
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
static constexpr uint8_t VK_BUFFER = 2;
static constexpr uint8_t OFFSETS = 3;
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4;
utils::StructureOfArrays<
VkVertexInputAttributeDescription,
VkVertexInputBindingDescription,
VkBuffer,
VkDeviceSize,
int8_t
> mSoa;
};
PipelineInfo* mInfo;
utils::FixedCapacityVector<VkBuffer> mBuffers;
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
};
@@ -219,19 +243,15 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
VulkanRenderPrimitive(VulkanResourceAllocator* allocator)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(allocator) {}
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
~VulkanRenderPrimitive() {
mResources.clear();
}
void setPrimitiveType(PrimitiveType pt);
void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer);
VulkanVertexBuffer* vertexBuffer = nullptr;
VulkanIndexBuffer* indexBuffer = nullptr;
VkPrimitiveTopology primitiveTopology;
private:
// Keep references to the vertex buffer and the index buffer.
@@ -563,6 +563,14 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
@@ -191,10 +191,19 @@ public:
mDummyTargetInfo.imageView = imageView;
}
// Acquires a resource to be bound to the current pipeline. The ownership of the resource
// will be transferred to the corresponding pipeline when pipeline is bound.
void acquireResource(VulkanResource* resource) {
mPipelineBoundResources.acquire(resource);
static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept {
switch (pt) {
case PrimitiveType::POINTS:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case PrimitiveType::LINES:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case PrimitiveType::LINE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case PrimitiveType::TRIANGLES:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case PrimitiveType::TRIANGLE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
}
}
private:
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include <private/backend/HandleAllocator.h>
@@ -29,18 +30,17 @@
namespace filament::backend {
// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType.
#define RESOURCE_TYPE_COUNT 12
#define DEBUG_RESOURCE_LEAKS 0
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
#if DEBUG_RESOURCE_LEAKS
#define TRACK_INCREMENT() \
if (!IS_MANAGED_TYPE(obj->mType)) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]++; \
#define TRACK_INCREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
}
#define TRACK_DECREMENT() \
if (!IS_MANAGED_TYPE(obj->mType)) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]--; \
#define TRACK_DECREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
}
#else
// No-op
@@ -51,8 +51,8 @@ namespace filament::backend {
class VulkanResourceAllocator {
public:
VulkanResourceAllocator(size_t arenaSize)
: mHandleAllocatorImpl("Handles", arenaSize)
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
#if DEBUG_RESOURCE_LEAKS
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
@@ -70,6 +70,7 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
// destruction.
case VulkanResourceType::FENCE:
case VulkanResourceType::HEAP_ALLOCATED:
case VulkanResourceType::END_TYPE:
break;
}
}
@@ -51,6 +51,8 @@ enum class VulkanResourceType : uint8_t {
// Below are resources that are managed manually (i.e. not ref counted).
FENCE,
HEAP_ALLOCATED,
END_TYPE, // A placeholder
};
#define IS_HEAP_ALLOC_TYPE(f) \
@@ -95,7 +95,8 @@ constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
}
void VulkanSamplerCache::initialize(VkDevice device) { mDevice = device; }
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
auto iter = mCache.find(params);
@@ -27,7 +27,7 @@ namespace filament::backend {
// Simple manager for VkSampler objects.
class VulkanSamplerCache {
public:
void initialize(VkDevice device);
explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(SamplerParams params) noexcept;
void terminate() noexcept;
private:
@@ -26,10 +26,9 @@ static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS;
namespace filament::backend {
void VulkanStagePool::initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept {
mAllocator = allocator;
mCommands = commands;
}
VulkanStagePool::VulkanStagePool(VmaAllocator allocator, VulkanCommands* commands)
: mAllocator(allocator),
mCommands(commands) {}
VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
// First check if a stage exists whose capacity is greater than or equal to the requested size.
@@ -45,7 +45,7 @@ struct VulkanStageImage {
// This class manages two types of host-mappable staging areas: buffer stages and image stages.
class VulkanStagePool {
public:
void initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept;
VulkanStagePool(VmaAllocator allocator, VulkanCommands* commands);
// Finds or creates a stage whose capacity is at least the given number of bytes.
// The stage is automatically released back to the pool after TIME_BEFORE_EVICTION frames.
@@ -18,10 +18,10 @@
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include <DataReshaper.h>
#include <backend/DriverEnums.h>
#include <private/backend/BackendUtils.h>
#include "DataReshaper.h"
#include <utils/Panic.h>
using namespace bluevk;
@@ -100,9 +100,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
// Filament expects blit() to work with any texture, so we almost always set these usage flags.
// TODO: investigate performance implications of setting these flags.
const VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) {
imageInfo.usage |= blittable;
}
if (any(usage & TextureUsage::SAMPLEABLE)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
@@ -159,7 +163,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.samples = (VkSampleCountFlagBits) samples;
VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage);
if (error || FVK_ENABLED_BOOL(FVK_DEBUG_TEXTURE)) {
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
utils::slog.d << "vkCreateImage: "
<< "image = " << mTextureImage << ", "
<< "result = " << error << ", "
+1 -1
View File
@@ -51,7 +51,7 @@ void BackendTest::init(Backend backend, bool isMobilePlatform) {
}
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
CONFIG_COMMAND_BUFFERS_SIZE) {
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
initializeDriver();
}
+2 -1
View File
@@ -46,7 +46,8 @@ void ComputeTest::init(Backend backend) {
}
ComputeTest::ComputeTest()
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) {
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE,
/*paused=*/false) {
}
ComputeTest::~ComputeTest() = default;
+11 -11
View File
@@ -226,9 +226,9 @@ TEST_F(BackendTest, ColorMagnify) {
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, 0 }, {}, {});
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, { dstTexture, level, 0 }, {}, {});
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
}
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
@@ -301,9 +301,9 @@ TEST_F(BackendTest, ColorMinify) {
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, 0 }, {}, {});
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, { dstTexture, level, 0 }, {}, {});
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
}
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
@@ -369,12 +369,12 @@ TEST_F(BackendTest, ColorResolve) {
// Create a 4-sample render target with the 4-sample texture.
Handle<HwRenderTarget> const srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount,
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
{{ srcColorTexture }}, {}, {});
// Create a 1-sample render target with the 1-sample texture.
Handle<HwRenderTarget> const dstRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1,
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
{{ dstColorTexture }}, {}, {});
// Prep for rendering.
@@ -473,9 +473,9 @@ TEST_F(BackendTest, Blit2DTextureArray) {
// Create two RenderTargets.
const int level = 0;
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, kSrcTexLayer }, {}, {});
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {});
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, { dstTexture, level, kDstTexLayer }, {}, {});
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {});
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
const int srcLevel = 0;
@@ -565,10 +565,10 @@ TEST_F(BackendTest, BlitRegion) {
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget =
api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, {srcTexture, kSrcLevel, 0}, {}, {});
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {});
Handle<HwRenderTarget> dstRenderTarget =
api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, {dstTexture, kDstLevel, 0}, {}, {});
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {});
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
SamplerMagFilter::LINEAR);
@@ -637,7 +637,7 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, { srcTexture, level, 0 }, {}, {});
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
}
// Blit one-quarter of src level 1 to dst level 0.
+1 -1
View File
@@ -216,7 +216,7 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
auto colorTexture = getDriverApi().createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0, 512, 512, 1, {{colorTexture}}, {}, {});
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {});
// Upload uniforms for the first triangle.
{
+1 -1
View File
@@ -160,7 +160,7 @@ TEST_F(BackendTest, FeedbackLoops) {
slog.i << "Level " << int(level) << ": " <<
(kTexWidth >> level) << "x" << (kTexHeight >> level) << io::endl;
renderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, { texture, level, 0 }, {}, {});
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {});
}
// Fill the base level of the texture with interesting colors.
+2 -1
View File
@@ -105,7 +105,8 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // samples
{{textureA },{textureB }}, // color
0, // layerCount
{{textureA },{textureB }}, // color
{}, // depth
{}); // stencil
+1 -1
View File
@@ -155,7 +155,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
// Render a white triangle into level 2.
// We specify mip level 2, because minMaxLevels has no effect when rendering into a texture.
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 32, 32, 1,
TargetBufferFlags::COLOR, 32, 32, 1, 0,
{texture, 2 /* level */, 0 /* layer */}, {}, {});
{
RenderPassParams params = {};
+4 -4
View File
@@ -279,7 +279,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// level (at least for OpenGL).
renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
t.getRenderTargetSize(), t.samples, {{ texture, uint8_t(t.mipLevel) }}, {},
t.getRenderTargetSize(), t.samples, 0, {{ texture, uint8_t(t.mipLevel) }}, {},
{});
}
@@ -318,9 +318,8 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// correct mip.
RenderPassParams p = params;
Handle<HwRenderTarget> mipLevelOneRT = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1,
{{ texture }}, {},
{});
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
{{ texture }}, {}, {});
p.clearColor = {1.f, 0.f, 0.f, 1.f};
getDriverApi().beginRenderPass(mipLevelOneRT, p);
getDriverApi().endRenderPass();
@@ -402,6 +401,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
renderTargetSize, // width
renderTargetSize, // height
1, // samples
0, // layerCount
{{ texture }}, // color
{}, // depth
{}); // stencil
+3 -3
View File
@@ -113,11 +113,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
// We purposely set the render target width and height to smaller than the texture, to check
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1,
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1, 0,
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {});
Handle<HwRenderTarget> fullRenderTarget = api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1,
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1, 0,
{srcTexture, kSrcLevel, 0}, {}, {});
TrianglePrimitive triangle(api);
@@ -208,7 +208,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
(uint32_t)std::numeric_limits<int32_t>::max()};
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 512, 512, 1,
TargetBufferFlags::COLOR, 512, 512, 1, 0,
{srcTexture, 0, 0}, {}, {});
TrianglePrimitive triangle(api);
+5 -5
View File
@@ -150,7 +150,7 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
auto stencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1,
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {}, {{stencilTexture}});
RunTest(renderTarget);
@@ -174,7 +174,7 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
auto depthStencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1,
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}});
RunTest(renderTarget);
@@ -202,10 +202,10 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
auto depthStencilTextureMSAA = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
auto renderTarget0 = getDriverApi().createRenderTarget(
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4,
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
auto renderTarget1 = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4,
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
api.startCapture(0);
@@ -273,4 +273,4 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.destroyRenderTarget(renderTarget1);
}
} // namespace test
} // namespace test
+29 -1
View File
@@ -300,7 +300,7 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
* Currently only honored by the GL and Metal backends.
*/
bool disableParallelShaderCompile = false;
@@ -335,6 +335,11 @@ public:
* This value determines for how many frames are texture entries kept in the cache.
*/
uint32_t resourceAllocatorCacheMaxAge = 2;
/*
* Disable backend handles use-after-free checks.
*/
bool disableHandleUseAfterFreeCheck = false;
};
@@ -402,6 +407,14 @@ public:
*/
Builder& featureLevel(FeatureLevel featureLevel) noexcept;
/**
* Warning: This is an experimental API. See Engine::setPaused(bool) for caveats.
*
* @param paused Whether to start the rendering thread paused.
* @return A reference to this Builder for chaining calls.
*/
Builder& paused(bool paused) noexcept;
#if UTILS_HAS_THREADING
/**
* Creates the filament Engine asynchronously.
@@ -827,6 +840,21 @@ public:
*/
void flush();
/**
* Pause or resume rendering thread.
*
* <p>Warning: This is an experimental API. In particular, note the following caveats.
*
* <ul><li>
* Buffer callbacks will never be called as long as the rendering thread is paused.
* Do not rely on a buffer callback to unpause the thread.
* </li><li>
* While the rendering thread is paused, rendering commands will continue to be queued until the
* buffer limit is reached. When the limit is reached, the program will abort.
* </li></ul>
*/
void setPaused(bool paused);
/**
* Drains the user callback message queue and immediately execute all pending callbacks.
*
+22 -1
View File
@@ -156,6 +156,15 @@ public:
*/
static constexpr uint8_t DEFAULT_CHANNEL = 2u;
/**
* Type of geometry for a Renderable
*/
enum class GeometryType : uint8_t {
DYNAMIC, //!< dynamic gemoetry has no restriction
STATIC_BOUNDS, //!< bounds and world space transform are immutable
STATIC //!< skinning/morphing not allowed and Vertex/IndexBuffer immutables
};
/**
* Creates a builder for renderable components.
*
@@ -204,6 +213,17 @@ public:
VertexBuffer* UTILS_NONNULL vertices,
IndexBuffer* UTILS_NONNULL indices) noexcept; //!< \overload
/**
* Specify the type of geometry for this renderable. DYNAMIC geometry has no restriction,
* STATIC_BOUNDS geometry means that both the bounds and the world-space transform of the
* the renderable are immutable.
* STATIC geometry has the same restrictions as STATIC_BOUNDS, but in addition disallows
* skinning, morphing and changing the VertexBuffer or IndexBuffer in any way.
* @param enable whether this renderable has static bounds. false by default.
*/
Builder& geometryType(GeometryType type) noexcept;
/**
* Binds a material instance to the specified primitive.
*
@@ -603,11 +623,12 @@ public:
/**
* Changes the bounding box used for frustum culling.
* The renderable must not have staticGeometry enabled.
*
* \see Builder::boundingBox()
* \see RenderableManager::getAxisAlignedBoundingBox()
*/
void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept;
void setAxisAlignedBoundingBox(Instance instance, const Box& aabb);
/**
* Changes the visibility bits.
+5
View File
@@ -295,6 +295,11 @@ utils::JobSystem& Engine::getJobSystem() noexcept {
return downcast(this)->getJobSystem();
}
void Engine::setPaused(bool paused) {
ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms.");
downcast(this)->setPaused(paused);
}
DebugRegistry& Engine::getDebugRegistry() noexcept {
return downcast(this)->getDebugRegistry();
}
+11 -12
View File
@@ -33,18 +33,17 @@ namespace filament {
using namespace utils;
using namespace backend;
size_t HwRenderPrimitiveFactory::KeyHash::operator()(
HwRenderPrimitiveFactory::Key const& p) const noexcept {
return utils::hash::combine(
p.params.vbh.getId(),
utils::hash::combine(p.params.ibh.getId(), (size_t)p.params.type));
size_t HwRenderPrimitiveFactory::Parameters::hash() const noexcept {
return utils::hash::combine(vbh.getId(),
utils::hash::combine(ibh.getId(),
(size_t)type));
}
bool operator==(HwRenderPrimitiveFactory::Key const& lhs,
HwRenderPrimitiveFactory::Key const& rhs) noexcept {
return lhs.params.vbh == rhs.params.vbh &&
lhs.params.ibh == rhs.params.ibh &&
lhs.params.type == rhs.params.type;
bool operator==(HwRenderPrimitiveFactory::Parameters const& lhs,
HwRenderPrimitiveFactory::Parameters const& rhs) noexcept {
return lhs.vbh == rhs.vbh &&
lhs.ibh == rhs.ibh &&
lhs.type == rhs.type;
}
// ------------------------------------------------------------------------------------------------
@@ -67,7 +66,7 @@ auto HwRenderPrimitiveFactory::create(DriverApi& driver,
backend::PrimitiveType type) noexcept -> Handle {
// see if we already have seen this RenderPrimitive
Key const key{{ vbh, ibh, type }, 1 };
Key const key({ vbh, ibh, type });
auto pos = mBimap.find(key);
// the common case is that we've never seen it (i.e.: no reuse)
@@ -77,7 +76,7 @@ auto HwRenderPrimitiveFactory::create(DriverApi& driver,
return handle;
}
pos->first.pKey->refs++;
++(pos->first.pKey->refs);
return pos->second.handle;
}
+21 -11
View File
@@ -52,8 +52,11 @@ public:
backend::VertexBufferHandle vbh; // 4
backend::IndexBufferHandle ibh; // 4
backend::PrimitiveType type; // 4
size_t hash() const noexcept;
};
friend bool operator==(Parameters const& lhs, Parameters const& rhs) noexcept;
Handle create(backend::DriverApi& driver,
backend::VertexBufferHandle vbh,
backend::IndexBufferHandle ibh,
@@ -63,29 +66,36 @@ public:
private:
struct Key {
// The key should not be copyable, unfortunately due to how the Bimap works we have
// to copy-construct it once.
Key(Key const&) = default;
Key& operator=(Key const&) = delete;
Key& operator=(Key&&) noexcept = delete;
explicit Key(Parameters const& params) : params(params), refs(1) { }
Parameters params;
mutable uint32_t refs; // 4 bytes
bool operator==(Key const& rhs) const noexcept {
return params == rhs.params;
}
};
struct KeyHash {
size_t operator()(Key const& p) const noexcept;
struct KeyHasher {
size_t operator()(Key const& p) const noexcept {
return p.params.hash();
}
};
friend bool operator==(Key const& lhs, Key const& rhs) noexcept;
struct Value { // 4 bytes
Handle handle;
};
struct ValueHash {
size_t operator()(Value const& p) const noexcept {
std::hash<Handle::HandleId> const h;
return h(p.handle.getId());
struct ValueHasher {
size_t operator()(Value const v) const noexcept {
return std::hash<Handle::HandleId>()(v.handle.getId());
}
};
friend bool operator==(Value const& lhs, Value const& rhs) noexcept {
friend bool operator==(Value const lhs, Value const rhs) noexcept {
return lhs.handle == rhs.handle;
}
@@ -104,7 +114,7 @@ private:
PoolAllocatorArena mArena;
// The special Bimap
Bimap<Key, Value, KeyHash, ValueHash,
Bimap<Key, Value, KeyHasher, ValueHasher,
utils::STLAllocator<Key, PoolAllocatorArena>> mBimap;
};
+1 -2
View File
@@ -22,7 +22,6 @@
#include <private/backend/DriverApi.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Hash.h>
@@ -66,7 +65,7 @@ auto HwVertexBufferInfoFactory::create(DriverApi& driver,
uint8_t attributeCount,
backend::AttributeArray attributes) noexcept -> Handle {
Key const key{{ bufferCount, attributeCount, {}, attributes }, 1 };
Key const key({ bufferCount, attributeCount, {}, attributes });
auto pos = mBimap.find(key);
// the common case is that we've never seen it (i.e.: no reuse)

Some files were not shown because too many files have changed in this diff Show More