Compare commits

..

27 Commits

Author SHA1 Message Date
Powei Feng
11fbacea20 Update MaterialEnums.h to v39 2023-06-20 11:57:42 -07:00
Benjamin Doherty
dba49f00df Merge branch 'rc/1.39.0' into release 2023-06-20 23:45:04 +08:00
Ben Doherty
89c0b44da9 Re-enable Metal half conversion, only register SimplificationPasses for Metal (#6883) 2023-06-20 14:27:44 +08:00
Ben Doherty
4f450fd5c4 Temporarily disable Metal relaxed to half pass due to spirv-cross bug (#6880) 2023-06-20 14:27:37 +08:00
Mathias Agopian
f0943cfca2 align uniform buffer to 16 bytes
this is needed on armv7 because we use alignas to get strcture-alignment,
but that also implies (to the compiler) that the structure itself
is aligned properly.
2023-06-20 14:23:47 +08:00
Mathias Agopian
9aa52b79d4 helpers to fix jank when resizing a TextureView (#6889)
There was two related issues:
- we need to "latch" the new TextureView size when its resized. That
  can only be done by recreating the EGLSurface (i.e. recreating the
  SwapChain). UiHelper now calls onNativeWindowChanged in the case of
  the TextureView resize, so clients can recreate their SwapChain.
- we also needed to make sure that all current filament frames have
  finished to render (i.e. the last eglSwapBuffers has been called) so
  that they don't pick-up a new size (this happens after
  eglSwapBuffers) that doesn't match the viewport.

Fixes b/282220665
2023-06-12 23:13:48 -07:00
Powei Feng
a82125dbbd vulkan: minor fixes (#6874)
- flush() and wait() before destroying a swapchain
 - Make sure the debug marker extension is enabled under correct
   circumstances.
 - Change shared_ptrs to unique_ptrs and raw pointers.
 - Rename most teardown methods to terminate()
2023-06-07 11:45:09 -07:00
Benjamin Doherty
24fcb299b5 Bump version to 1.39.0 2023-06-07 11:32:33 -05:00
Benjamin Doherty
b0238c1560 Release Filament 1.38.0 2023-06-07 11:31:01 -05:00
Mathias Agopian
5ffb52a17d Workaround invalid glsl generation
spirv-opt's CreateSimplificationPass() cuases spirv-cross to fail
generating working glsl.
2023-06-06 19:53:57 -07:00
Mathias Agopian
6b34e72418 disable glsl/msl minification when generate debug info is active 2023-06-06 19:53:09 -07:00
Powei Feng
d43c632e55 vulkan: add option to choose gpu in platform (#6864) 2023-06-06 17:18:57 -07:00
Romain Guy
d55dfdebde Fix erroneous comment 2023-06-06 15:30:06 -07:00
Powei Feng
4b12876ebc vulkan: fix context sharing (#6869)
- Make sure terminate() doesn't free shared context
- Assume no extensions have been enabled.
2023-06-06 14:04:28 -07:00
Daniel Kutenin
3e12449b8f Fix strict weak ordering in VulkanPlatform.cpp (#6867)
Merged-by: Powei Feng <powei@google.com>
2023-06-06 13:07:28 -07:00
Mathias Agopian
fdc3f302d9 fix a crash on adreno when vsm is enabled
fixes b/285928132
2023-06-06 09:55:35 -07:00
Mathias Agopian
07642fec83 workaround a bug in spirv-tools causing vsm to fail
it's unclear if the problem was in spirv-cross or spirv-opt, but
eitherway, the generated code at the end was invalid.
2023-06-06 09:54:45 -07:00
Mathias Agopian
4ce93232c3 MaterialParser micro optimizations
- improves performance
- reduces code size
2023-06-05 12:45:30 -07:00
Powei Feng
ccea370cd9 vulkan: wait on command buffers before resource gc (#6855) 2023-06-02 10:50:06 -07:00
Powei Feng
8f6885689b release: adjust version-bump script for linux (#6859) 2023-06-02 09:58:47 -07:00
Romain Guy
64b15fafe2 Save ImGui settings in a user-specific directory (#6865)
* Save ImGui settings in a user-specific directory

Fixes #6862

* Fix Windows build

* Fix Web build

* Add missing file
2023-06-02 08:42:27 -07:00
Powei Feng
d891acb8c0 Fix missing include (#6858) 2023-06-01 15:15:34 -07:00
Powei Feng
de0bc718be vulkan: fix freebsd platform breakage (#6863) 2023-06-01 13:42:39 -07:00
Romain Guy
283a06e200 Update AGP and Kotlin coroutines dependency (#6857) 2023-06-01 08:22:51 -07:00
Powei Feng
3cfb2f8339 vulkan: fix accidentally enabled API dump (#6856) 2023-05-31 16:05:27 -07:00
Powei Feng
21164885e8 Update MATERIAL_VERSION to 38 (#6852) 2023-05-31 11:26:03 -07:00
Mathias Agopian
0d745c4fea JobQueue is not used anymore, remove it. 2023-05-30 17:44:06 -07:00
69 changed files with 638 additions and 618 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.38.0'
implementation 'com.google.android.filament:filament-android:1.39.0'
}
```
@@ -50,7 +50,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.38.0'
pod 'Filament', '~> 1.39.0'
```
### Snapshots

View File

@@ -7,6 +7,11 @@ 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.39.0
- matc: workaround a bug in spirv-tools causing vsm to fail [⚠️ **Recompile materials**]
- UiHelper: fix jank when a `TextureView` is resized (fixes b\282220665)
## v1.38.0
- engine: a new feature to set a transform on the global-scale fog [⚠️ **Recompile materials**]

View File

@@ -84,7 +84,7 @@ buildscript {
'targetSdk': 33,
'compileSdk': 33,
'kotlin': '1.8.20',
'kotlin_coroutines': '1.6.4',
'kotlin_coroutines': '1.7.1',
'buildTools': '33.0.2',
'ndk': '25.1.8937393',
'androidx_core': '1.10.0',
@@ -104,7 +104,7 @@ buildscript {
]
dependencies {
classpath 'com.android.tools.build:gradle:8.0.0'
classpath 'com.android.tools.build:gradle:8.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}

View File

@@ -22,17 +22,35 @@ import com.google.android.filament.Fence;
public class FilamentHelper {
/**
* Wait for all pending frames to be processed before returning. This is to
* avoid a race between the surface being resized before pending frames are
* rendered into it. This is typically called from {@link UiHelper.RendererCallback#onResized},
* {@link android.view.SurfaceHolder.Callback#surfaceChanged} or
* {@link android.view.TextureView.SurfaceTextureListener#onSurfaceTextureSizeChanged}.
* Wait for all pending frames to be processed before returning. This is to avoid a race
* between the surface being resized before pending frames are rendered into it.
* <p>
* For {@link android.view.TextureView} this must be called before the texture's size is
* reconfigured, which unfortunately is done by the Android framework before
* {@link UiHelper} listeners are invoked. Therefore <code>synchronizePendingFrames</code>
* cannot be called from
* {@link android.view.TextureView.SurfaceTextureListener#onSurfaceTextureSizeChanged}; instead
* a subclass of {@link android.view.TextureView} must be used in order to call it from
* {@link android.view.TextureView#onSizeChanged}:
* </p>
* <pre>
* public class MyTextureView extends TextureView {
* private Engine engine;
* protected void onSizeChanged(int w, int h, int oldw, int oldh) {
* FilamentHelper.synchronizePendingFrames(engine);
* super.onSizeChanged(w, h, oldw, oldh);
* }
* }
* </pre>
*
* Otherwise, this is typically called from {@link UiHelper.RendererCallback#onResized},
* {@link android.view.SurfaceHolder.Callback#surfaceChanged}.
*
* @param engine Filament engine to synchronize
*
* @see UiHelper.RendererCallback#onResized
* @see android.view.SurfaceHolder.Callback#surfaceChanged
* @see android.view.TextureView.SurfaceTextureListener#onSurfaceTextureSizeChanged
* @see android.view.TextureView#onSizeChanged
*/
static public void synchronizePendingFrames(Engine engine) {
Fence fence = engine.createFence();

View File

@@ -244,6 +244,10 @@ public class UiHelper {
}
mSurface = surface;
}
public Surface getSurface() {
return mSurface;
}
}
/**
@@ -489,6 +493,14 @@ public class UiHelper {
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
mRenderCallback.onNativeWindowChanged(textureViewHandler.getSurface());
}
@Override

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.38.0
VERSION_NAME=1.39.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -2,6 +2,11 @@
set -e
case "$(uname -s)" in
Darwin*) IS_DARWIN=1;;
*) ;;
esac
function print_help {
local SELF_NAME
SELF_NAME=$(basename "$0")
@@ -45,7 +50,11 @@ function replace {
FIND_STR="${1//\{\{VERSION\}\}/${VERSION_REGEX}}"
REPLACE_STR="${1//\{\{VERSION\}\}/${NEW_VERSION}}"
local FILE_NAME="$2"
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
if [ IS_DARWIN ]; then
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
else
sed -i -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
fi
}
# The following are the canonical locations where the Filament version number is referenced.

View File

@@ -1130,7 +1130,9 @@ enum class Workaround : uint16_t {
ADRENO_UNIFORM_ARRAY_CRASH,
// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
A8X_STATIC_TEXTURE_TARGET_ERROR
A8X_STATIC_TEXTURE_TARGET_ERROR,
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
};
} // namespace filament::backend

View File

@@ -87,6 +87,29 @@ public:
return 0;
}
// ----------------------------------------------------
// ---------- Platform Customization options ----------
/**
* The client preference can be stored within the struct. We allow for two specification of
* preference:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`.
* 2) Index of the device in the list as returned by vkEnumeratePhysicalDevices.
*/
struct GPUPreference {
std::string deviceName;
int8_t index = -1;
};
/**
* Client can provide a preference over the GPU to use in the vulkan instance
* @return `GPUPreference` struct that indicates the client's preference
*/
virtual GPUPreference getPreferredGPU() noexcept {
return {};
}
// -------- End platform customization options --------
// ----------------------------------------------------
/**
* Get the images handles and format of the memory backing the swapchain. This should be called
* after createSwapChain() or after recreateIfResized().

View File

@@ -724,6 +724,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
return mContext->bugs.a8xStaticTextureTargetError;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false;
}
return false;
}

View File

@@ -221,13 +221,13 @@ OpenGLContext::OpenGLContext() noexcept {
// Blits to texture arrays are failing
// This bug continues to reproduce, though at times we've seen it appear to "go away".
// The standalone sample app that was written to show this problem still reproduces.
// The working hypthesis is that some other state affects this behavior.
bugs.disable_sidecar_blit_into_texture_array = true;
// The working hypothesis is that some other state affects this behavior.
bugs.disable_blit_into_texture_array = true;
// early exit condition is flattened in EASU code
bugs.split_easu = true;
// initialize the non used uniform array for adreno drivers.
// initialize the non-used uniform array for Adreno drivers.
bugs.enable_initialize_non_used_uniform_array = true;
int maj, min, driverMajor, driverMinor;

View File

@@ -262,7 +262,7 @@ public:
// Some drivers can't blit from a sidecar renderbuffer into a layer of a texture array.
// This technique is used for VSM with MSAA turned on.
bool disable_sidecar_blit_into_texture_array;
bool disable_blit_into_texture_array;
// Some drivers incorrectly flatten the early exit condition in the EASU code, in which
// case we need an alternative algorithm
@@ -446,8 +446,8 @@ private:
{ bugs.dont_use_timer_query,
"dont_use_timer_query",
""},
{ bugs.disable_sidecar_blit_into_texture_array,
"disable_sidecar_blit_into_texture_array",
{ bugs.disable_blit_into_texture_array,
"disable_blit_into_texture_array",
""},
{ bugs.split_easu,
"split_easu",

View File

@@ -968,9 +968,14 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
break;
}
// depth/stencil attachment must match the rendertarget sample count
// this is because EXT_multisampled_render_to_texture doesn't guarantee depth/stencil
// is resolved.
// depth/stencil attachments must match the rendertarget sample count
// because EXT_multisampled_render_to_texture[2] doesn't resolve the depth/stencil
// buffers:
// for EXT_multisampled_render_to_texture
// "the contents of the multisample buffer become undefined"
// for EXT_multisampled_render_to_texture2
// "the contents of the multisample buffer is discarded rather than resolved -
// equivalent to the application calling InvalidateFramebuffer for this attachment"
UTILS_UNUSED bool attachmentTypeNotSupportedByMSRTT = false;
switch (attachment) {
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -1018,22 +1023,8 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
attachmentTypeNotSupportedByMSRTT = true;
}
// There's a bug with certain drivers preventing us from emulating
// EXT_multisampled_render_to_texture when the texture is a TEXTURE_2D_ARRAY, so we'll simply
// fall back to non-MSAA rendering for now. Also, MSRTT is never available for TEXTURE_2D_ARRAY,
// and since we are a 2D array, we know we're sampleable and therefore that a resolve will
// be needed.
// Note that this affects VSM shadows in particular.
// TODO: a better workaround would be to do the resolve by hand in that case
const bool disableMultisampling =
gl.bugs.disable_sidecar_blit_into_texture_array &&
rt->gl.samples > 1 && t->samples <= 1 &&
(target == GL_TEXTURE_2D_ARRAY ||
target == GL_TEXTURE_CUBE_MAP_ARRAY); // implies MSRTT is not available
if (rt->gl.samples <= 1 ||
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture) ||
disableMultisampling) {
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture)) {
// on GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
// If multisampled textures are not supported and we end-up here, things should
// still work, albeit without MSAA.
@@ -1876,6 +1867,8 @@ bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
return mContext.bugs.allow_read_only_ancillary_feedback_loop;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return mContext.bugs.enable_initialize_non_used_uniform_array;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return mContext.bugs.disable_blit_into_texture_array;
default:
return false;
}

View File

@@ -116,8 +116,7 @@ VulkanBlitter::VulkanBlitter(VulkanStagePool& stagePool, VulkanPipelineCache& pi
mSamplerCache(samplerCache) {}
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands,
std::shared_ptr<VulkanTexture> emptyTexture) noexcept {
VmaAllocator allocator, VulkanCommands* commands, VulkanTexture* emptyTexture) noexcept {
mPhysicalDevice = physicalDevice;
mDevice = device;
mAllocator = allocator;
@@ -181,7 +180,7 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
args.dstRectPair);
}
void VulkanBlitter::shutdown() noexcept {
void VulkanBlitter::terminate() noexcept {
if (mDevice) {
delete mDepthResolveProgram;
mDepthResolveProgram = nullptr;
@@ -198,9 +197,6 @@ void VulkanBlitter::shutdown() noexcept {
mParamsBuffer = nullptr;
}
}
mCommands.reset();
mEmptyTexture.reset();
}
// If we created these shader modules in the constructor, the device might not be ready yet.

View File

@@ -36,8 +36,7 @@ public:
VulkanFboCache& fboCache, VulkanSamplerCache& samplerCache) noexcept;
void initialize(VkPhysicalDevice physicalDevice, VkDevice device, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands,
std::shared_ptr<VulkanTexture> emptyTexture) noexcept;
VulkanCommands* commands, VulkanTexture* emptyTexture) noexcept;
struct BlitArgs {
const VulkanRenderTarget* dstTarget;
@@ -51,7 +50,7 @@ public:
void blitColor(BlitArgs args);
void blitDepth(BlitArgs args);
void shutdown() noexcept;
void terminate() noexcept;
private:
void lazyInit() noexcept;
@@ -67,8 +66,8 @@ private:
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
VkDevice mDevice;
VmaAllocator mAllocator;
std::shared_ptr<VulkanCommands> mCommands;
std::shared_ptr<VulkanTexture> mEmptyTexture;
VulkanCommands* mCommands;
VulkanTexture* mEmptyTexture;
VulkanStagePool& mStagePool;
VulkanPipelineCache& mPipelineCache;

View File

@@ -23,7 +23,7 @@ using namespace bluevk;
namespace filament::backend {
VulkanBuffer::VulkanBuffer(VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands,
VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, VkBufferUsageFlags usage, uint32_t numBytes)
: mAllocator(allocator), mCommands(commands), mStagePool(stagePool), mUsage(usage) {
@@ -51,7 +51,6 @@ void VulkanBuffer::terminate() {
vmaDestroyBuffer(mAllocator, mGpuBuffer, mGpuMemory);
mGpuMemory = VK_NULL_HANDLE;
mGpuBuffer = VK_NULL_HANDLE;
mCommands.reset();
}
void VulkanBuffer::loadFromCpu(const void* cpuData, uint32_t byteOffset, uint32_t numBytes) const {
@@ -63,7 +62,7 @@ void VulkanBuffer::loadFromCpu(const void* cpuData, uint32_t byteOffset, uint32_
vmaUnmapMemory(mAllocator, stage->memory);
vmaFlushAllocation(mAllocator, stage->memory, byteOffset, numBytes);
const VkCommandBuffer cmdbuffer = mCommands->get().cmdbuffer;
VkCommandBuffer const cmdbuffer = mCommands->get(true).cmdbuffer;
VkBufferCopy region{ .size = numBytes };
vkCmdCopyBuffer(cmdbuffer, stage->buffer, mGpuBuffer, 1, &region);

View File

@@ -25,15 +25,15 @@ namespace filament::backend {
// Encapsulates a Vulkan buffer, its attached DeviceMemory and a staging area.
class VulkanBuffer {
public:
VulkanBuffer(VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands,
VulkanStagePool& stagePool, VkBufferUsageFlags usage, uint32_t numBytes);
VulkanBuffer(VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool,
VkBufferUsageFlags usage, uint32_t numBytes);
~VulkanBuffer();
void terminate();
void loadFromCpu(const void* cpuData, uint32_t byteOffset, uint32_t numBytes) const;
VkBuffer getGpuBuffer() const { return mGpuBuffer; }
private:
VmaAllocator mAllocator;
std::shared_ptr<VulkanCommands> mCommands;
VulkanCommands* mCommands;
VulkanStagePool& mStagePool;
VmaAllocation mGpuMemory = VK_NULL_HANDLE;

View File

@@ -81,8 +81,9 @@ VulkanCommands::~VulkanCommands() {
}
}
VulkanCommandBuffer const& VulkanCommands::get() {
VulkanCommandBuffer const& VulkanCommands::get(bool blockOnGC) {
if (mCurrent) {
mCurrent->blockOnGC = mCurrent->blockOnGC || blockOnGC;
return *mCurrent;
}
@@ -119,6 +120,8 @@ VulkanCommandBuffer const& VulkanCommands::get() {
};
vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCurrent->cmdbuffer);
mCurrent->blockOnGC = blockOnGC;
// Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership
// over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in
// VulkanDriver::destroyFence(), both of which are safe spots.
@@ -240,7 +243,9 @@ void VulkanCommands::wait() {
void VulkanCommands::gc() {
for (auto& wrapper : mStorage) {
if (wrapper.cmdbuffer != VK_NULL_HANDLE) {
VkResult result = vkWaitForFences(mDevice, 1, &wrapper.fence->fence, VK_TRUE, 0);
uint64_t const timeout = wrapper.blockOnGC ? UINT64_MAX : 0;
VkResult const result
= vkWaitForFences(mDevice, 1, &wrapper.fence->fence, VK_TRUE, timeout);
if (result == VK_SUCCESS) {
vkFreeCommandBuffers(mDevice, mPool, 1, &wrapper.cmdbuffer);
wrapper.cmdbuffer = VK_NULL_HANDLE;

View File

@@ -48,6 +48,7 @@ struct VulkanCommandBuffer {
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
VkCommandBuffer cmdbuffer = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fence;
bool blockOnGC = false;
};
// Allows classes to be notified after a new command buffer has been activated.
@@ -89,7 +90,10 @@ class VulkanCommands {
~VulkanCommands();
// Creates a "current" command buffer if none exists, otherwise returns the current one.
VulkanCommandBuffer const& get();
// `blockOnGC` guarrantees that this buffer will be waited on when gc() is called on it so
// that dependent resources can be gc'd safetly after the buffer is sumbitted, completed,
// and gc'd.
VulkanCommandBuffer const& get(bool blockOnGC = false);
// 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.

View File

@@ -39,73 +39,8 @@ struct VulkanTexture;
class VulkanStagePool;
struct VulkanTimerQuery;
// TODO: We used std::shared_ptr in various places across the vulkan backend, but VulkanTexture*
// also maps to HwTexture so it's not possible to switch in VulkanAttachment. Hence we introduce
// this temporary class. We need to revisit the ownership pattern and use of smart pointer in the
// vulkan backend, in particular std::shared_ptr uses atomic, which is overhead we do not need.
struct TexturePointer {
TexturePointer() = default;
explicit TexturePointer(VulkanTexture* tex) :mTexture(tex) {}
explicit TexturePointer(std::shared_ptr<VulkanTexture> tex) :mTexture(tex) {}
inline TexturePointer& operator=(VulkanTexture* tex) {
mTexture = tex;
return *this;
}
inline TexturePointer& operator=(std::shared_ptr<VulkanTexture> tex) {
mTexture = tex;
return *this;
}
// Be careful not to leak here. Should only be used in local scope.
explicit operator VulkanTexture*() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}
// Be careful not to leak here. Should only be used in local scope.
explicit operator VulkanTexture const*() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}
explicit operator std::shared_ptr<VulkanTexture>() const {
assert_invariant(mTexture.index() == 1);
return std::get<1>(mTexture);
}
inline operator bool() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture) != nullptr;
}
return (bool) std::get<1>(mTexture);
}
inline VulkanTexture* operator->() {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}
inline VulkanTexture const* operator->() const {
if (mTexture.index() == 0) {
return std::get<0>(mTexture);
}
return std::get<1>(mTexture).get();
}
private:
std::variant<VulkanTexture*, std::shared_ptr<VulkanTexture>> mTexture;
};
struct VulkanAttachment {
TexturePointer texture;
VulkanTexture* texture;
uint8_t level = 0;
uint16_t layer = 0;
VkImage getImage() const;

View File

@@ -84,7 +84,7 @@ void VulkanDisposer::gc() noexcept {
disposables.swap(mDisposables);
}
void VulkanDisposer::reset() noexcept {
void VulkanDisposer::terminate() noexcept {
#ifndef NDEBUG
utils::slog.i << mDisposables.size() << " disposables are outstanding." << utils::io::endl;
#endif

View File

@@ -44,7 +44,7 @@ public:
void gc() noexcept;
// Invokes the destructor function for all disposables, regardless of reference count.
void reset() noexcept;
void terminate() noexcept;
private:
struct Disposable {

View File

@@ -92,12 +92,11 @@ VmaAllocator createAllocator(VkInstance instance, VkPhysicalDevice physicalDevic
return allocator;
}
std::shared_ptr<VulkanTexture> createEmptyTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, VulkanStagePool& stagePool) {
std::shared_ptr<VulkanTexture> emptyTexture = std::make_shared<VulkanTexture>(device,
physicalDevice, context, allocator, commands, SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 1, 1, 1,
VulkanTexture* createEmptyTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool) {
VulkanTexture* emptyTexture = new VulkanTexture(device, physicalDevice, context, allocator,
commands, SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1, 1, 1, 1,
TextureUsage::DEFAULT | TextureUsage::COLOR_ATTACHMENT | TextureUsage::SUBPASS_INPUT,
stagePool);
uint32_t black = 0;
@@ -186,22 +185,22 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
}
#endif
mTimestamps = std::make_unique<VulkanTimestamps>(mPlatform->getDevice());
mCommands = std::make_shared<VulkanCommands>(mPlatform->getDevice(),
mCommands = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex());
mCommands->setObserver(&mPipelineCache);
mPipelineCache.setDevice(mPlatform->getDevice(), mAllocator);
// TOOD: move them all to be initialized by constructor
mStagePool.initialize(mAllocator, mCommands);
mStagePool.initialize(mAllocator, mCommands.get());
mFramebufferCache.initialize(mPlatform->getDevice());
mSamplerCache.initialize(mPlatform->getDevice());
mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
mContext, mAllocator, mCommands, mStagePool);
mEmptyTexture.reset(createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
mContext, mAllocator, mCommands.get(), mStagePool));
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
mCommands, mEmptyTexture);
mCommands.get(), mEmptyTexture.get());
}
VulkanDriver::~VulkanDriver() noexcept = default;
@@ -225,20 +224,22 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
}
void VulkanDriver::terminate() {
mEmptyTexture.reset();
// Command buffers should come first since it might have commands depending on resources that
// are about to be destroyed.
mCommands.reset();
mEmptyTexture.reset();
mTimestamps.reset();
mBlitter.shutdown();
mBlitter.terminate();
// Allow the stage pool and disposer to clean up.
mStagePool.gc();
mDisposer.reset();
mDisposer.terminate();
mStagePool.reset();
mPipelineCache.destroyCache();
mStagePool.terminate();
mPipelineCache.terminate();
mFramebufferCache.reset();
mSamplerCache.reset();
mSamplerCache.terminate();
vmaDestroyAllocator(mAllocator);
@@ -260,10 +261,12 @@ void VulkanDriver::tick(int) {
// rather than the wall clock, because we must wait 3 frames after a DriverAPI-level resource has
// been destroyed for safe destruction, due to outstanding command buffers and triple buffering.
void VulkanDriver::collectGarbage() {
// 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();
mStagePool.gc();
mFramebufferCache.gc();
mDisposer.gc();
mCommands->gc();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -333,7 +336,7 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t) getElementTypeSize(elementType);
auto indexBuffer = construct<VulkanIndexBuffer>(ibh, mAllocator, mCommands, mStagePool,
auto indexBuffer = construct<VulkanIndexBuffer>(ibh, mAllocator, mCommands.get(), mStagePool,
elementSize, indexCount);
mDisposer.createDisposable(indexBuffer,
[this, ibh]() { destructBuffer<VulkanIndexBuffer>(ibh); });
@@ -348,7 +351,7 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
auto bufferObject = construct<VulkanBufferObject>(boh, mAllocator, mCommands, mStagePool,
auto bufferObject = construct<VulkanBufferObject>(boh, mAllocator, mCommands.get(), mStagePool,
byteCount, bindingType, usage);
mDisposer.createDisposable(bufferObject,
[this, boh]() { destructBuffer<VulkanBufferObject>(boh); });
@@ -372,8 +375,8 @@ 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 = construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands, target, levels, format,
samples, w, h, depth, usage, mStagePool);
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
format, samples, w, h, depth, usage, mStagePool);
mDisposer.createDisposable(vktexture, [this, th]() { destruct<VulkanTexture>(th); });
}
@@ -384,8 +387,8 @@ void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targ
TextureSwizzle swizzleArray[] = {r, g, b, a};
const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
auto vktexture = construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands, target, levels, format,
samples, w, h, depth, usage, mStagePool, swizzleMap);
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
format, samples, w, h, depth, usage, mStagePool, swizzleMap);
mDisposer.createDisposable(vktexture, [this, th]() {
destruct<VulkanTexture>(th);
});
@@ -439,7 +442,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (color[i].handle) {
colorTargets[i] = {
.texture = TexturePointer(handle_cast<VulkanTexture*>(color[i].handle)),
.texture = handle_cast<VulkanTexture*>(color[i].handle),
.level = color[i].level,
.layer = color[i].layer,
};
@@ -453,7 +456,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
VulkanAttachment depthStencil[2] = {};
if (depth.handle) {
depthStencil[0] = {
.texture = TexturePointer(handle_cast<VulkanTexture*>(depth.handle)),
.texture = handle_cast<VulkanTexture*>(depth.handle),
.level = depth.level,
.layer = depth.layer,
};
@@ -465,7 +468,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
if (stencil.handle) {
depthStencil[1] = {
.texture = TexturePointer(handle_cast<VulkanTexture*>(stencil.handle)),
.texture = handle_cast<VulkanTexture*>(stencil.handle),
.level = stencil.level,
.layer = stencil.layer,
};
@@ -482,8 +485,8 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
assert_invariant(tmin.x >= width && tmin.y >= height);
auto renderTarget = construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands, width, height, samples,
colorTargets, depthStencil, mStagePool);
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), width, height,
samples, colorTargets, depthStencil, mStagePool);
mDisposer.createDisposable(renderTarget, [this, rth]() { destruct<VulkanRenderTarget>(rth); });
}
@@ -508,15 +511,15 @@ void VulkanDriver::createSyncR(Handle<HwSync> sh, int) {
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator, mCommands, mStagePool,
construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator, mCommands.get(), mStagePool,
nativeWindow, flags);
}
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator, mCommands, mStagePool, nullptr,
flags, VkExtent2D{width, height});
construct<VulkanSwapChain>(sch, mPlatform, mContext, mAllocator, mCommands.get(), mStagePool,
nullptr, flags, VkExtent2D{width, height});
}
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
@@ -778,6 +781,8 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
return false;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false;
default:
return false;
}

View File

@@ -138,9 +138,9 @@ private:
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
std::shared_ptr<VulkanCommands> mCommands;
std::unique_ptr<VulkanCommands> mCommands;
std::unique_ptr<VulkanTimestamps> mTimestamps;
std::shared_ptr<VulkanTexture> mEmptyTexture;
std::unique_ptr<VulkanTexture> mEmptyTexture;
VulkanSwapChain* mCurrentSwapChain = nullptr;
VulkanRenderTarget* mDefaultRenderTarget = nullptr;

View File

@@ -68,7 +68,7 @@ void VulkanFboCache::initialize(VkDevice device) noexcept { mDevice = device; }
VulkanFboCache::~VulkanFboCache() {
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
"Please explicitly call reset() while the VkDevice is still alive.");
"Please explicitly call terminate() while the VkDevice is still alive.");
}
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {

View File

@@ -132,15 +132,15 @@ VulkanRenderTarget::VulkanRenderTarget() : HwRenderTarget(0, 0), mOffscreen(fals
void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
assert_invariant(!mOffscreen);
VkExtent2D const extent = swapChain.getExtent();
mColor[0] = { .texture = TexturePointer(swapChain.getCurrentColor()) };
mDepth = { .texture = TexturePointer(swapChain.getDepth()) };
mColor[0] = { .texture = swapChain.getCurrentColor() };
mDepth = { .texture = swapChain.getDepth() };
width = extent.width;
height = extent.height;
}
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, uint32_t width, uint32_t height, uint8_t samples,
VulkanCommands* commands, uint32_t width, uint32_t height, uint8_t samples,
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool)
: HwRenderTarget(width, height), mOffscreen(true), mSamples(samples) {
@@ -167,13 +167,13 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
if (texture && texture->samples == 1) {
auto msTexture = texture->getSidecar();
if (UTILS_UNLIKELY(!msTexture)) {
msTexture = std::make_shared<VulkanTexture>(device, physicalDevice, context,
msTexture = new VulkanTexture(device, physicalDevice, context,
allocator, commands, texture->target,
((VulkanTexture const*) texture)->levels, texture->format, samples,
texture->width, texture->height, texture->depth, texture->usage, stagePool);
texture->setSidecar(msTexture);
}
mMsaaAttachments[index] = {.texture = TexturePointer(msTexture)};
mMsaaAttachments[index] = {.texture = msTexture};
}
if (texture && texture->samples > 1) {
mMsaaAttachments[index] = mColor[index];
@@ -194,9 +194,9 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
uint8_t const msLevel = 1;
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
std::shared_ptr<VulkanTexture> msTexture = depthTexture->getSidecar();
VulkanTexture* msTexture = depthTexture->getSidecar();
if (UTILS_UNLIKELY(!msTexture)) {
msTexture = std::make_shared<VulkanTexture>(device, physicalDevice, context, allocator,
msTexture = new VulkanTexture(device, physicalDevice, context, allocator,
commands, depthTexture->target, msLevel, depthTexture->format, samples,
depthTexture->width, depthTexture->height, depthTexture->depth, depthTexture->usage,
stagePool);
@@ -204,7 +204,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
}
mMsaaDepthAttachment = {
.texture = TexturePointer(msTexture),
.texture = msTexture,
.level = msLevel,
.layer = mDepth.layer,
};
@@ -264,7 +264,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
buffers(bufferCount, nullptr) {}
VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, VulkanStagePool& stagePool, uint32_t byteCount,
VulkanCommands* commands, VulkanStagePool& stagePool, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage)
: HwBufferObject(byteCount),
buffer(allocator, commands, stagePool, getBufferObjectUsage(bindingType), byteCount),

View File

@@ -53,7 +53,7 @@ struct VulkanRenderTarget : private HwRenderTarget {
// Creates an offscreen render target.
VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, uint32_t width, uint32_t height,
VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool);
@@ -90,7 +90,7 @@ struct VulkanVertexBuffer : public HwVertexBuffer {
};
struct VulkanIndexBuffer : public HwIndexBuffer {
VulkanIndexBuffer(VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands,
VulkanIndexBuffer(VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, uint8_t elementSize, uint32_t indexCount)
: HwIndexBuffer(elementSize, indexCount),
buffer(allocator, commands, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
@@ -102,7 +102,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer {
};
struct VulkanBufferObject : public HwBufferObject {
VulkanBufferObject(VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands,
VulkanBufferObject(VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, uint32_t byteCount, BufferObjectBinding bindingType,
BufferUsage usage);
void terminate() {

View File

@@ -86,7 +86,7 @@ VulkanPipelineCache::VulkanPipelineCache() : mCurrentRasterState(createDefaultRa
}
VulkanPipelineCache::~VulkanPipelineCache() {
// This does nothing because VulkanDriver::terminate() calls destroyCache() in order to
// This does nothing because VulkanDriver::terminate() calls terminate() in order to
// be explicit about teardown order of various components.
}
@@ -639,7 +639,7 @@ void VulkanPipelineCache::bindInputAttachment(uint32_t bindingIndex,
mDescriptorRequirements.inputAttachments[bindingIndex] = targetInfo;
}
void VulkanPipelineCache::destroyCache() noexcept {
void VulkanPipelineCache::terminate() noexcept {
// Symmetric to createLayoutsAndDescriptors.
destroyLayoutsAndDescriptors();
for (auto& iter : mPipelines) {

View File

@@ -169,7 +169,7 @@ public:
// NOTE: In theory we should proffer "unbindSampler" but in practice we never destroy samplers.
// Destroys all managed Vulkan objects. This should be called before changing the VkDevice.
void destroyCache() noexcept;
void terminate() noexcept;
// vkCmdBindPipeline and vkCmdBindDescriptorSets establish bindings to a specific command
// buffer; they are not global to the device. Therefore we need to be notified when a

View File

@@ -127,7 +127,7 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
return sampler;
}
void VulkanSamplerCache::reset() noexcept {
void VulkanSamplerCache::terminate() noexcept {
for (auto pair : mCache) {
vkDestroySampler(mDevice, pair.second, VKALLOC);
}

View File

@@ -29,7 +29,7 @@ class VulkanSamplerCache {
public:
void initialize(VkDevice device);
VkSampler getSampler(SamplerParams params) noexcept;
void reset() noexcept;
void terminate() noexcept;
private:
VkDevice mDevice;
tsl::robin_map<uint32_t, VkSampler> mCache;

View File

@@ -26,8 +26,7 @@ static constexpr uint32_t TIME_BEFORE_EVICTION = VK_MAX_COMMAND_BUFFERS;
namespace filament::backend {
void VulkanStagePool::initialize(VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands) noexcept {
void VulkanStagePool::initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept {
mAllocator = allocator;
mCommands = commands;
}
@@ -185,7 +184,7 @@ void VulkanStagePool::gc() noexcept {
}
}
void VulkanStagePool::reset() noexcept {
void VulkanStagePool::terminate() noexcept {
for (auto stage : mUsedStages) {
vmaDestroyBuffer(mAllocator, stage->buffer, stage->memory);
delete stage;
@@ -209,8 +208,6 @@ void VulkanStagePool::reset() noexcept {
delete image;
}
mFreeStages.clear();
mCommands.reset();
}
} // namespace filament::backend

View File

@@ -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, std::shared_ptr<VulkanCommands> commands) noexcept;
void initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept;
// 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.
@@ -60,11 +60,11 @@ public:
// Destroys all unused stages and asserts that there are no stages currently in use.
// This should be called while the context's VkDevice is still alive.
void reset() noexcept;
void terminate() noexcept;
private:
VmaAllocator mAllocator;
std::shared_ptr<VulkanCommands> mCommands;
VulkanCommands* mCommands;
// Use an ordered multimap for quick (capacity => stage) lookups using lower_bound().
std::multimap<uint32_t, VulkanStage const*> mFreeStages;

View File

@@ -26,8 +26,8 @@ using namespace utils;
namespace filament::backend {
VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands,
VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags, VkExtent2D extent)
VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool,
void* nativeWindow, uint64_t flags, VkExtent2D extent)
: mPlatform(platform),
mCommands(commands),
mAllocator(allocator),
@@ -48,6 +48,11 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
}
VulkanSwapChain::~VulkanSwapChain() {
// Must wait for the inflight command buffers to finish since they might contain the images
// we're about to destroy.
mCommands->flush();
mCommands->wait();
mPlatform->destroy(swapChain);
vkDestroySemaphore(mPlatform->getDevice(), mImageReady, VKALLOC);
}
@@ -60,11 +65,11 @@ void VulkanSwapChain::update() {
VkDevice const device = mPlatform->getDevice();
for (auto const color: bundle.colors) {
mColors.push_back(std::make_shared<VulkanTexture>(device, mAllocator, mCommands, color,
mColors.push_back(std::make_unique<VulkanTexture>(device, mAllocator, mCommands, color,
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height,
TextureUsage::COLOR_ATTACHMENT, mStagePool));
}
mDepth = std::make_shared<VulkanTexture>(device, mAllocator, mCommands, bundle.depth,
mDepth = std::make_unique<VulkanTexture>(device, mAllocator, mCommands, bundle.depth,
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height,
TextureUsage::DEPTH_ATTACHMENT, mStagePool);

View File

@@ -37,7 +37,7 @@ struct VulkanSurfaceSwapChain;
// A wrapper around the platform implementation of swapchain.
struct VulkanSwapChain : public HwSwapChain {
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, VulkanStagePool& stagePool,
VulkanCommands* commands, VulkanStagePool& stagePool,
void* nativeWindow, uint64_t flags, VkExtent2D extent = {0, 0});
~VulkanSwapChain();
@@ -46,12 +46,12 @@ struct VulkanSwapChain : public HwSwapChain {
void acquire(bool& reized);
inline std::shared_ptr<VulkanTexture> getCurrentColor() const noexcept {
return mColors[mCurrentSwapIndex];
inline VulkanTexture* getCurrentColor() const noexcept {
return mColors[mCurrentSwapIndex].get();
}
inline std::shared_ptr<VulkanTexture> getDepth() const noexcept {
return mDepth;
inline VulkanTexture* getDepth() const noexcept {
return mDepth.get();
}
inline bool isFirstRenderPass() const noexcept {
@@ -70,16 +70,15 @@ private:
void update();
VulkanPlatform* mPlatform;
std::shared_ptr<VulkanCommands> mCommands;
VulkanCommands* mCommands;
VmaAllocator mAllocator;
VulkanStagePool& mStagePool;
bool const mHeadless;
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
// transitions, which are useful here. We use std::shared_ptr because they will be shared with
// VulkanRenderTarget.
utils::FixedCapacityVector<std::shared_ptr<VulkanTexture>> mColors;
std::shared_ptr<VulkanTexture> mDepth;
// transitions, which are useful here.
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
std::unique_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
VkSemaphore mImageReady;
uint32_t mCurrentSwapIndex;

View File

@@ -30,7 +30,7 @@ namespace filament::backend {
using ImgUtil = VulkanImageUtility;
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, VkImage image, VkFormat format, uint8_t samples,
VulkanCommands* commands, VkImage image, VkFormat format, uint8_t samples,
uint32_t width, uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
tusage),
@@ -47,7 +47,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator,
std::shared_ptr<VulkanCommands> commands, SamplerType target, uint8_t levels,
VulkanCommands* commands, SamplerType target, uint8_t levels,
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool, VkComponentMapping swizzle)
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
@@ -252,7 +252,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
vmaUnmapMemory(mAllocator, stage->memory);
vmaFlushAllocation(mAllocator, stage->memory, 0, hostData->size);
const VkCommandBuffer cmdbuf = mCommands->get().cmdbuffer;
const VkCommandBuffer cmdbuf = mCommands->get(true).cmdbuffer;
VkBufferImageCopy copyRegion = {
.bufferOffset = {},

View File

@@ -28,17 +28,15 @@ namespace filament::backend {
struct VulkanTexture : public HwTexture {
// Standard constructor for user-facing textures.
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands, SamplerType target,
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool,
VkComponentMapping swizzle={});
VmaAllocator allocator, VulkanCommands* commands, SamplerType target, uint8_t levels,
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool, VkComponentMapping swizzle = {});
// Specialized constructor for internally created textures (e.g. from a swap chain)
// The texture will never destroy the given VkImage, but it does manages its subresources.
VulkanTexture(VkDevice device,
VmaAllocator allocator, std::shared_ptr<VulkanCommands> commands, VkImage image,
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool);
VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VkImage image,
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool);
~VulkanTexture();
@@ -68,12 +66,12 @@ struct VulkanTexture : public HwTexture {
VulkanLayout getLayout(uint32_t layer, uint32_t level) const;
void setSidecar(std::shared_ptr<VulkanTexture> sidecar) {
mSidecarMSAA = sidecar;
void setSidecar(VulkanTexture* sidecar) {
mSidecarMSAA.reset(sidecar);
}
std::shared_ptr<VulkanTexture> getSidecar() const {
return mSidecarMSAA;
VulkanTexture* getSidecar() const {
return mSidecarMSAA.get();
}
void transitionLayout(VkCommandBuffer commands, const VkImageSubresourceRange& range,
@@ -96,7 +94,8 @@ private:
void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height,
uint32_t depth, uint32_t miplevel);
std::shared_ptr<VulkanTexture> mSidecarMSAA;
// The texture with the sidecar owns the sidecar.
std::unique_ptr<VulkanTexture> mSidecarMSAA;
const VkFormat mVkFormat;
const VkImageViewType mViewType;
const VkComponentMapping mSwizzle;
@@ -114,7 +113,7 @@ private:
VulkanStagePool& mStagePool;
VkDevice mDevice;
VmaAllocator mAllocator;
std::shared_ptr<VulkanCommands> mCommands;
VulkanCommands* mCommands;
};
} // namespace filament::backend

View File

@@ -49,7 +49,7 @@ typedef std::unordered_set<std::string_view> ExtensionSet;
// These strings need to be allocated outside a function stack
const std::string_view DESIRED_LAYERS[] = {
"VK_LAYER_KHRONOS_validation",
#if defined(FILAMENT_VULKAN_DUMP_API)
#if FILAMENT_VULKAN_DUMP_API
"VK_LAYER_LUNARG_api_dump",
#endif
#if defined(ENABLE_RENDERDOC)
@@ -198,7 +198,7 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
return exts;
}
VkInstance createInstance(const ExtensionSet& requiredExts) {
VkInstance createInstance(ExtensionSet const& requiredExts) {
VkInstance instance;
VkInstanceCreateInfo instanceCreateInfo = {};
bool validationFeaturesSupported = false;
@@ -363,6 +363,13 @@ std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
&& newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
// debugMarker must also request debugReport the instance extension. So check if that's present.
if (newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()
&& newInstExts.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == newInstExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
return std::tuple(newInstExts, newDeviceExts);
}
@@ -414,12 +421,15 @@ inline int deviceTypeOrder(VkPhysicalDeviceType deviceType) {
}
}
VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
VulkanPlatform::GPUPreference const& gpuPreference) {
FixedCapacityVector<VkPhysicalDevice> const physicalDevices
= filament::backend::enumerate(vkEnumeratePhysicalDevices, instance);
struct DeviceInfo {
VkPhysicalDevice device = VK_NULL_HANDLE;
VkPhysicalDeviceType deviceType = VK_PHYSICAL_DEVICE_TYPE_OTHER;
int8_t index = -1;
std::string_view name;
};
FixedCapacityVector<DeviceInfo> deviceList(physicalDevices.size());
@@ -462,19 +472,39 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
}
deviceList[deviceInd].device = candidateDevice;
deviceList[deviceInd].deviceType = targetDeviceProperties.deviceType;
deviceList[deviceInd].index = deviceInd;
deviceList[deviceInd].name = targetDeviceProperties.deviceName;
}
// Sort the found devices
std::sort(deviceList.begin(), deviceList.end(), [](DeviceInfo const& a, DeviceInfo const& b) {
if (a.device == VK_NULL_HANDLE) {
return true;
}
if (b.device == VK_NULL_HANDLE) {
return false;
}
return deviceTypeOrder(a.deviceType) <= deviceTypeOrder(b.deviceType);
});
ASSERT_PRECONDITION(gpuPreference.index < static_cast<int32_t>(deviceList.size()),
"Provided GPU index=%d >= the number of GPUs=%d", gpuPreference.index,
static_cast<int32_t>(deviceList.size()));
// Sort the found devices
std::sort(deviceList.begin(), deviceList.end(),
[pref = gpuPreference](DeviceInfo const& a, DeviceInfo const& b) {
if (b.device == VK_NULL_HANDLE) {
return false;
}
if (a.device == VK_NULL_HANDLE) {
return true;
}
if (!pref.deviceName.empty()) {
if (a.name.find(pref.deviceName) != a.name.npos) {
return false;
}
if (b.name.find(pref.deviceName) != b.name.npos) {
return true;
}
}
if (pref.index == a.index) {
return false;
}
if (pref.index == b.index) {
return true;
}
return deviceTypeOrder(a.deviceType) < deviceTypeOrder(b.deviceType);
});
auto device = deviceList.back().device;
ASSERT_POSTCONDITION(device != VK_NULL_HANDLE, "Unable to find suitable device.");
return device;
@@ -510,6 +540,8 @@ struct VulkanPlatformPrivate {
// store the actual swapchain struct, which is either backed-by-surface or headless.
std::unordered_set<SwapChainPtr> mSurfaceSwapChains;
std::unordered_set<SwapChainPtr> mHeadlessSwapChains;
bool mSharedContext = false;
};
void VulkanPlatform::terminate() {
@@ -523,8 +555,10 @@ void VulkanPlatform::terminate() {
}
mImpl->mSurfaceSwapChains.clear();
vkDestroyDevice(mImpl->mDevice, VKALLOC);
vkDestroyInstance(mImpl->mInstance, VKALLOC);
if (!mImpl->mSharedContext) {
vkDestroyDevice(mImpl->mDevice, VKALLOC);
vkDestroyInstance(mImpl->mInstance, VKALLOC);
}
}
// This is the main entry point for context creation.
@@ -552,12 +586,18 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mDevice = scontext->logicalDevice;
mImpl->mGraphicsQueueFamilyIndex = scontext->graphicsQueueFamilyIndex;
mImpl->mGraphicsQueueIndex = scontext->graphicsQueueIndex;
mImpl->mSharedContext = true;
}
VulkanContext context;
auto instExts = getInstanceExtensions();
instExts.merge(getRequiredInstanceExtensions());
ExtensionSet instExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
instExts = getInstanceExtensions();
instExts.merge(getRequiredInstanceExtensions());
}
mImpl->mInstance
= mImpl->mInstance == VK_NULL_HANDLE ? createInstance(instExts) : mImpl->mInstance;
@@ -565,8 +605,13 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
bluevk::bindInstance(mImpl->mInstance);
VulkanPlatform::GPUPreference const pref = getPreferredGPU();
bool const hasGPUPreference = pref.index >= 0 || !pref.deviceName.empty();
ASSERT_PRECONDITION(!(hasGPUPreference && sharedContext),
"Cannot both share context and indicate GPU preference");
mImpl->mPhysicalDevice = mImpl->mPhysicalDevice == VK_NULL_HANDLE
? selectPhysicalDevice(mImpl->mInstance)
? selectPhysicalDevice(mImpl->mInstance, pref)
: mImpl->mPhysicalDevice;
assert_invariant(mImpl->mPhysicalDevice != VK_NULL_HANDLE);
@@ -591,8 +636,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mGraphicsQueueIndex
= mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex;
auto deviceExts = getDeviceExtensions(mImpl->mPhysicalDevice);
{
ExtensionSet deviceExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
deviceExts = getDeviceExtensions(mImpl->mPhysicalDevice);
auto [prunedInstExts, prunedDeviceExts]
= pruneExtensions(mImpl->mPhysicalDevice, instExts, deviceExts);
instExts = prunedInstExts;

View File

@@ -23,6 +23,10 @@
#include <bluevk/BlueVK.h>
#if defined(__linux__) || defined(__FreeBSD__)
#define LINUX_OR_FREEBSD 1
#endif
// Platform specific includes and defines
#if defined(__ANDROID__)
#include <android/native_window.h>
@@ -38,7 +42,7 @@
uint32_t height;
} wl;
}// anonymous namespace
#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_X11)
#elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11)
// TODO: we should allow for headless on Linux explicitly. Right now this is the headless path
// (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB).
#include <dlfcn.h>
@@ -86,7 +90,7 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getRequiredInstanceExtensions() {
ret.insert(VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME);
#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
ret.insert("VK_KHR_wayland_surface");
#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_X11)
#elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11)
#if defined(FILAMENT_SUPPORTS_XCB)
ret.insert("VK_KHR_xcb_surface");
#endif
@@ -146,7 +150,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
VkResult const result = vkCreateWaylandSurfaceKHR(instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateWaylandSurfaceKHR error.");
#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_X11)
#elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11)
if (g_x11_vk.library == nullptr) {
g_x11_vk.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
ASSERT_PRECONDITION(g_x11_vk.library, "Unable to open X11 library.");
@@ -211,3 +215,5 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
}
} // namespace filament::backend
#undef LINUX_OR_FREEBSD

View File

@@ -191,7 +191,7 @@ private:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* // Declares a "linear sRGB" color space.
* ColorSpace myColorSpace = Rec709-Linear-sRGB;
* ColorSpace myColorSpace = Rec709-Linear-D65;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class PartialColorSpace {

View File

@@ -74,9 +74,9 @@ UTILS_NOINLINE
bool MaterialParser::MaterialParserDetails::getFromSimpleChunk(
filamat::ChunkType type, T* value) const noexcept {
ChunkContainer const& chunkContainer = mChunkContainer;
ChunkContainer::ChunkDesc const* pChunkDesc;
if (chunkContainer.hasChunk(type, &pChunkDesc)) {
Unflattener unflattener(pChunkDesc->start, pChunkDesc->start + pChunkDesc->size);
ChunkContainer::ChunkDesc chunkDesc;
if (chunkContainer.hasChunk(type, &chunkDesc)) {
Unflattener unflattener(chunkDesc.start, chunkDesc.start + chunkDesc.size);
return unflattener.read(value);
}
return false;

View File

@@ -107,7 +107,7 @@ void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction,
PerShadowMapUniforms::Transaction PerShadowMapUniforms::open(backend::DriverApi& driver) noexcept {
Transaction transaction;
// TODO: use out-of-line buffer if too large
transaction.uniforms = (PerViewUib *)driver.allocate(sizeof(PerViewUib));
transaction.uniforms = (PerViewUib *)driver.allocate(sizeof(PerViewUib), 16);
assert_invariant(transaction.uniforms);
return transaction;
}

View File

@@ -824,7 +824,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
return shadowTechnique;
}
void ShadowMapManager::calculateTextureRequirements(FEngine&, FView& view,
void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view,
FScene::LightSoa const&) noexcept {
// Lay out the shadow maps. For now, we take the largest requested dimension and allocate a
@@ -854,7 +854,10 @@ void ShadowMapManager::calculateTextureRequirements(FEngine&, FView& view,
const bool useMipmapping = view.hasVSM() &&
((vsmShadowOptions.anisotropy > 0) || vsmShadowOptions.mipmapping);
const uint8_t msaaSamples = vsmShadowOptions.msaaSamples;
uint8_t msaaSamples = vsmShadowOptions.msaaSamples;
if (engine.getDriverApi().isWorkaroundNeeded(Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY)) {
msaaSamples = 1;
}
TextureFormat format = TextureFormat::DEPTH16;
if (view.hasVSM()) {

View File

@@ -447,8 +447,10 @@ void FRenderableManager::create(
}
}
else {
// When boneCount is 0, do an initialization for the bones uniform array to avoid crash on adreno gpu.
if (UTILS_UNLIKELY(driver.isWorkaroundNeeded(Workaround::ADRENO_UNIFORM_ARRAY_CRASH))) {
// When boneCount is 0, do an initialization for the bones uniform array to
// avoid crash on adreno gpu.
if (UTILS_UNLIKELY(driver.isWorkaroundNeeded(
Workaround::ADRENO_UNIFORM_ARRAY_CRASH))) {
auto *initBones = driver.allocatePod<PerRenderableBoneUib::BoneData>(1);
std::uninitialized_fill_n(initBones, 1, FSkinningBuffer::makeBone({}));
driver.updateBufferObject(bones.handle, {
@@ -458,7 +460,8 @@ void FRenderableManager::create(
}
}
// Create and initialize all needed MorphTargets. It's required to avoid branches in hot loops.
// Create and initialize all needed MorphTargets.
// It's required to avoid branches in hot loops.
MorphTargets* morphTargets = new MorphTargets[entryCount];
for (size_t i = 0; i < entryCount; ++i) {
morphTargets[i] = { mEngine.getDummyMorphTargetBuffer(), 0, 0 };
@@ -488,8 +491,10 @@ void FRenderableManager::create(
(uint32_t)morphing.count };
}
// When targetCount equal 0, boneCount>0 in this case, do an initialization for the morphWeights uniform array to avoid crash on adreno gpu.
if (UTILS_UNLIKELY(targetCount == 0 && driver.isWorkaroundNeeded(Workaround::ADRENO_UNIFORM_ARRAY_CRASH))) {
// When targetCount equal 0, boneCount>0 in this case, do an initialization for the
// morphWeights uniform array to avoid crash on adreno gpu.
if (UTILS_UNLIKELY(targetCount == 0 &&
driver.isWorkaroundNeeded(Workaround::ADRENO_UNIFORM_ARRAY_CRASH))) {
float initWeights[1] = {0};
setMorphWeights(ci, initWeights, 1, 0);
}

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.38.0"
spec.version = "1.39.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.38.0/filament-v1.38.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.39.0/filament-v1.39.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 38;
static constexpr size_t MATERIAL_VERSION = 39;
/**
* Supported shading models

View File

@@ -67,25 +67,16 @@ public:
}
std::pair<uint8_t const*, uint8_t const*> getChunkRange(Type type) const noexcept {
ChunkDesc const* pChunkDesc;
bool success = hasChunk(type, &pChunkDesc);
ChunkDesc chunkDesc;
bool const success = hasChunk(type, &chunkDesc);
if (success) {
return { pChunkDesc->start, pChunkDesc->start + pChunkDesc->size };
return { chunkDesc.start, chunkDesc.start + chunkDesc.size };
}
return { nullptr, nullptr };
}
bool hasChunk(Type type, ChunkDesc const** pChunkDesc = nullptr) const noexcept {
auto& chunks = mChunks;
auto pos = chunks.find(type);
if (pos != chunks.end()) {
if (pChunkDesc) {
*pChunkDesc = &pos.value();
}
return true;
}
return false;
}
bool hasChunk(Type type) const noexcept;
bool hasChunk(Type type, ChunkDesc* pChunkDesc) const noexcept;
void const* getData() const { return mData; }

View File

@@ -22,6 +22,8 @@
#include <private/filament/Variant.h>
#include <type_traits>
#include <stdint.h>
namespace filaflat {
@@ -46,7 +48,7 @@ public:
return mCursor < mEnd;
}
inline bool willOverflow(size_t size) const noexcept {
bool willOverflow(size_t size) const noexcept {
return (mCursor + size) > mEnd;
}
@@ -56,100 +58,41 @@ public:
assert_invariant(0 == (intptr_t(mCursor) % 8));
}
bool read(bool* b) noexcept {
if (willOverflow(1)) {
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
bool read(T* const out) noexcept {
if (UTILS_UNLIKELY(willOverflow(sizeof(T)))) {
return false;
}
*b = mCursor[0];
mCursor += 1;
auto const* const cursor = mCursor;
mCursor += sizeof(T);
T v = 0;
for (size_t i = 0; i < sizeof(T); i++) {
v |= T(cursor[i]) << (8 * i);
}
*out = v;
return true;
}
bool read(uint8_t* i) noexcept {
if (willOverflow(1)) {
return false;
}
*i = mCursor[0];
mCursor += 1;
return true;
bool read(float* f) noexcept {
return read(reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(f)));
}
bool read(filament::Variant* v) noexcept {
return read(&v->key);
}
bool read(uint16_t* i) noexcept {
if (willOverflow(2)) {
return false;
}
*i = 0;
*i |= mCursor[0];
*i |= mCursor[1] << 8;
mCursor += 2;
return true;
}
bool read(uint32_t* i) noexcept {
if (willOverflow(4)) {
return false;
}
*i = 0;
*i |= mCursor[0];
*i |= mCursor[1] << 8;
*i |= mCursor[2] << 16;
*i |= mCursor[3] << 24;
mCursor += 4;
return true;
}
bool read(uint64_t* i) noexcept {
if (willOverflow(8)) {
return false;
}
*i = 0;
*i |= static_cast<int64_t>(mCursor[0]);
*i |= static_cast<int64_t>(mCursor[1]) << 8;
*i |= static_cast<int64_t>(mCursor[2]) << 16;
*i |= static_cast<int64_t>(mCursor[3]) << 24;
*i |= static_cast<int64_t>(mCursor[4]) << 32;
*i |= static_cast<int64_t>(mCursor[5]) << 40;
*i |= static_cast<int64_t>(mCursor[6]) << 48;
*i |= static_cast<int64_t>(mCursor[7]) << 56;
mCursor += 8;
return true;
}
bool read(utils::CString* s) noexcept;
bool read(const char** blob, size_t* size) noexcept;
bool read(const char** s) noexcept;
bool read(float* f) noexcept {
if (willOverflow(4)) {
return false;
}
uint32_t i;
i = 0;
i |= mCursor[0];
i |= mCursor[1] << 8;
i |= mCursor[2] << 16;
i |= mCursor[3] << 24;
*f = reinterpret_cast<float&>(i);
mCursor += 4;
return true;
}
const uint8_t* getCursor() const noexcept {
return mCursor;
}
void setCursor(const uint8_t* cursor) noexcept {
if (mSrc <= cursor && cursor < mEnd) {
mCursor = cursor;
} else {
mCursor = mEnd;
}
mCursor = (cursor >= mSrc && cursor < mEnd) ? cursor : mEnd;
}
private:

View File

@@ -22,6 +22,23 @@ namespace filaflat {
ChunkContainer::~ChunkContainer() noexcept = default;
bool ChunkContainer::hasChunk(Type type) const noexcept {
auto const& chunks = mChunks;
auto pos = chunks.find(type);
return pos != chunks.end();
}
bool ChunkContainer::hasChunk(Type type, ChunkDesc* pChunkDesc) const noexcept {
assert_invariant(pChunkDesc);
auto const& chunks = mChunks;
auto pos = chunks.find(type);
if (UTILS_LIKELY(pos != chunks.end())) {
*pChunkDesc = pos.value();
return true;
}
return false;
}
bool ChunkContainer::parseChunk(Unflattener& unflattener) {
uint64_t type;
if (!unflattener.read(&type)) {

View File

@@ -18,19 +18,6 @@
namespace filaflat {
bool Unflattener::read(utils::CString* s) noexcept {
const uint8_t* start = mCursor;
while (mCursor < mEnd && *mCursor != '\0') {
mCursor++;
}
bool overflowed = mCursor >= mEnd;
if (!overflowed) {
*s = utils::CString{ (const char*)start, (utils::CString::size_type)(mCursor - start) };
mCursor++;
}
return !overflowed;
}
bool Unflattener::read(const char** blob, size_t* size) noexcept {
uint64_t nbytes;
if (!read(&nbytes)) {
@@ -38,7 +25,7 @@ bool Unflattener::read(const char** blob, size_t* size) noexcept {
}
const uint8_t* start = mCursor;
mCursor += nbytes;
bool overflowed = mCursor > mEnd;
bool const overflowed = mCursor > mEnd;
if (!overflowed) {
*blob = (const char*)start;
*size = nbytes;
@@ -46,16 +33,35 @@ bool Unflattener::read(const char** blob, size_t* size) noexcept {
return !overflowed;
}
bool Unflattener::read(const char** s) noexcept {
const uint8_t* start = mCursor;
while (mCursor < mEnd && *mCursor != '\0') {
mCursor++;
bool Unflattener::read(utils::CString* const s) noexcept {
const uint8_t* const start = mCursor;
const uint8_t* const last = mEnd;
const uint8_t* curr = start;
while (curr < last && *curr != '\0') {
curr++;
}
bool overflowed = mCursor >= mEnd;
if (!overflowed) {
mCursor++;
bool const overflowed = start >= last;
if (UTILS_LIKELY(!overflowed)) {
*s = utils::CString{ (const char*)start, utils::CString::size_type(curr - start) };
curr++;
}
*s = (char*)start;
mCursor = curr;
return !overflowed;
}
bool Unflattener::read(const char** const s) noexcept {
const uint8_t* const start = mCursor;
const uint8_t* const last = mEnd;
const uint8_t* curr = start;
while (curr < last && *curr != '\0') {
curr++;
}
bool const overflowed = start >= last;
if (UTILS_LIKELY(!overflowed)) {
*s = (char const*)start;
curr++;
}
mCursor = curr;
return !overflowed;
}

View File

@@ -95,6 +95,7 @@ public:
ImGuiContext* mImGuiContext;
filament::TextureSampler mSampler;
bool mFlipVertical = false;
utils::Path mSettingsPath;
};
} // namespace filagui

View File

@@ -32,6 +32,7 @@
#include <filament/TextureSampler.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <utils/EntityManager.h>
using namespace filament::math;
@@ -50,6 +51,13 @@ ImGuiHelper::ImGuiHelper(Engine* engine, filament::View* view, const Path& fontP
: mEngine(engine), mView(view), mScene(engine->createScene()),
mImGuiContext(imGuiContext ? imGuiContext : ImGui::CreateContext()) {
ImGuiIO& io = ImGui::GetIO();
mSettingsPath.setPath(
Path::getUserSettingsDirectory() +
Path(std::string(".") + Path::getCurrentExecutable().getNameWithoutExtension()) +
Path("imgui_settings.ini")
);
mSettingsPath.getParent().mkdirRecursive();
io.IniFilename = mSettingsPath.c_str();
// Create a simple alpha-blended 2D blitting material.
mMaterial = Material::Builder()

View File

@@ -128,6 +128,10 @@ if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0 /Zc:__cplusplus")
endif()
if (FILAMENT_ENABLE_MATDBG)
add_definitions(-DFILAMENT_ENABLE_MATDBG)
endif()
# ==================================================================================================
# Installation
# ==================================================================================================

View File

@@ -377,7 +377,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
msl::collectSibs(config, sibs);
spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput,
config.shaderModel, config.hasFramebufferFetch, sibs,
&internalConfig.minifier);
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
} else {
slog.e << "GLSL post-processor invoked with optimization level NONE"
@@ -394,17 +394,18 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
}
if (internalConfig.glslOutput) {
*internalConfig.glslOutput =
internalConfig.minifier.removeWhitespace(
*internalConfig.glslOutput,
mOptimization == MaterialBuilder::Optimization::SIZE);
if (!mGenerateDebugInfo) {
*internalConfig.glslOutput =
internalConfig.minifier.removeWhitespace(
*internalConfig.glslOutput,
mOptimization == MaterialBuilder::Optimization::SIZE);
// In theory this should only be enabled for SIZE, but in practice we often use PERFORMANCE.
if (mOptimization != MaterialBuilder::Optimization::NONE) {
*internalConfig.glslOutput =
internalConfig.minifier.renameStructFields(*internalConfig.glslOutput);
// In theory this should only be enabled for SIZE, but in practice we often use PERFORMANCE.
if (mOptimization != MaterialBuilder::Optimization::NONE) {
*internalConfig.glslOutput =
internalConfig.minifier.renameStructFields(*internalConfig.glslOutput);
}
}
if (mPrintShaders) {
slog.i << *internalConfig.glslOutput << io::endl;
}
@@ -460,7 +461,8 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT);
msl::collectSibs(config, sibs);
spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel,
config.hasFramebufferFetch, sibs, &internalConfig.minifier);
config.hasFramebufferFetch, sibs,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.glslOutput) {
@@ -508,7 +510,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT);
msl::collectSibs(config, sibs);
spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch,
sibs, &internalConfig.minifier);
sibs, mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
// Transpile back to GLSL
@@ -567,10 +569,10 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
registerPerformancePasses(*optimizer, config);
// Metal doesn't support relaxed precision, but does have support for float16 math operations.
if (config.targetApi == MaterialBuilder::TargetApi::METAL) {
optimizer->RegisterPass(CreateConvertRelaxedToHalfPass());
optimizer->RegisterPass(CreateSimplificationPass());
optimizer->RegisterPass(CreateRedundancyEliminationPass());
optimizer->RegisterPass(CreateAggressiveDCEPass());
optimizer->RegisterPass(CreateConvertRelaxedToHalfPass());
optimizer->RegisterPass(CreateSimplificationPass());
optimizer->RegisterPass(CreateRedundancyEliminationPass());
optimizer->RegisterPass(CreateAggressiveDCEPass());
}
}
@@ -600,46 +602,55 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
optimizer.RegisterPass(CreateMergeReturnPass());
}
auto const localCreateSimplificationPass = [&config] {
// Adreno GPU show artifacts after running simplication passes. We workaround this just by
// disabling the simplification pass for mobile + vulkan.
return config.shaderModel == ShaderModel::MOBILE
&& config.targetApi == MaterialBuilder::TargetApi::VULKAN
? std::move(CreateNullPass())
: std::move(CreateSimplificationPass());
// CreateSimplificationPass() creates a lot of problems:
// - Adreno GPU show artifacts after running simplication passes (Vulkan)
// - spirv-cross fails generating working glsl
// (https://github.com/KhronosGroup/SPIRV-Cross/issues/2162)
// - generally it makes the code more complicated, e.g.: replacing for loops with
// while-if-break, unclear if it helps for anything.
// However, the simplification passes below are necessary when targeting Metal, otherwise the
// result is mismatched half / float assignments in MSL.
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
MaterialBuilder::TargetApi apiFilter =
MaterialBuilder::TargetApi::ALL) {
if (!(config.targetApi & apiFilter)) {
return;
}
optimizer.RegisterPass(std::move(pass));
};
optimizer.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreatePrivateToLocalPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateScalarReplacementPass())
.RegisterPass(CreateLocalAccessChainConvertPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
.RegisterPass(CreateLocalSingleStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateLocalMultiStoreElimPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateCCPPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateCombineAccessChainsPass())
.RegisterPass(localCreateSimplificationPass())
.RegisterPass(CreateVectorDCEPass())
.RegisterPass(CreateDeadInsertElimPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(localCreateSimplificationPass())
.RegisterPass(CreateIfConversionPass())
.RegisterPass(CreateCopyPropagateArraysPass())
.RegisterPass(CreateReduceLoadSizePass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(CreateRedundancyEliminationPass())
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateBlockMergePass())
.RegisterPass(localCreateSimplificationPass());
RegisterPass(CreateInlineExhaustivePass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreatePrivateToLocalPass());
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
RegisterPass(CreateLocalSingleStoreElimPass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreateScalarReplacementPass());
RegisterPass(CreateLocalAccessChainConvertPass());
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
RegisterPass(CreateLocalSingleStoreElimPass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreateLocalMultiStoreElimPass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreateCCPPass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreateRedundancyEliminationPass());
RegisterPass(CreateCombineAccessChainsPass());
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateVectorDCEPass());
RegisterPass(CreateDeadInsertElimPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateIfConversionPass());
RegisterPass(CreateCopyPropagateArraysPass());
RegisterPass(CreateReduceLoadSizePass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateRedundancyEliminationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
}
void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& config) {

View File

@@ -34,6 +34,9 @@ struct Config {
filament::camutils::Mode cameraMode = filament::camutils::Mode::ORBIT;
bool resizeable = true;
bool headless = false;
// Provided to indicate GPU preference for vulkan
std::string vulkanGPUHint;
};
#endif // TNT_FILAMENT_SAMPLE_CONFIG_H

View File

@@ -49,6 +49,11 @@ class ImGuiHelper;
class IBL;
class MeshAssimp;
// For customizing the vulkan backend
namespace filament::backend {
class VulkanPlatform;
}
class FilamentApp {
public:
using SetupCallback = std::function<void(filament::Engine*, filament::View*, filament::Scene*)>;
@@ -244,6 +249,8 @@ private:
float mCameraFocalLength = 28.0f;
float mCameraNear = 0.1f;
float mCameraFar = 100.0f;
filament::backend::VulkanPlatform* mVulkanPlatform = nullptr;
};
#endif // TNT_FILAMENT_SAMPLE_FILAMENTAPP_H

View File

@@ -44,6 +44,8 @@
#include <filament/DebugRegistry.h>
#endif
#include <backend/platforms/VulkanPlatform.h>
#include <filagui/ImGuiHelper.h>
#include <filamentapp/Cube.h>
@@ -58,6 +60,34 @@ using namespace filagui;
using namespace filament::math;
using namespace utils;
namespace {
using namespace filament::backend;
class FilamentAppVulkanPlatform : public VulkanPlatform {
public:
FilamentAppVulkanPlatform(std::string const& gpuHint) {
if (gpuHint.empty()) {
return;
}
// Check to see if it is an integer, if so turn it into an index.
if (std::all_of(gpuHint.begin(), gpuHint.end(), ::isdigit)) {
mPreference.index = static_cast<int8_t>(std::stoi(gpuHint));
return;
}
mPreference.deviceName = gpuHint;
}
virtual VulkanPlatform::GPUPreference getPreferredGPU() noexcept override {
return mPreference;
}
private:
VulkanPlatform::GPUPreference mPreference;
};
} // anonymous namespace
FilamentApp& FilamentApp::get() {
static FilamentApp filamentApp;
return filamentApp;
@@ -397,7 +427,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
// TODO: Use SDL_GL_SetSwapInterval for proper vsync
SDL_DisplayMode Mode;
int refreshIntervalMS = (SDL_GetDesktopDisplayMode(
SDL_GetWindowDisplayIndex(window->mWindow), &Mode) == 0 &&
SDL_GetWindowDisplayIndex(window->mWindow), &Mode) == 0 &&
Mode.refresh_rate != 0) ? round(1000.0 / Mode.refresh_rate) : 16;
SDL_Delay(refreshIntervalMS);
@@ -442,6 +472,10 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
mEngine->destroy(mScene);
Engine::destroy(&mEngine);
mEngine = nullptr;
if (mVulkanPlatform) {
delete mVulkanPlatform;
}
}
// RELATIVE_ASSET_PATH is set inside samples/CMakeLists.txt and used to support multi-configuration
@@ -537,8 +571,30 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
// events.
mWindow = SDL_CreateWindow(title.c_str(), x, y, (int) w, (int) h, windowFlags);
auto const createEngine = [&config, this]() {
auto backend = config.backend;
// This mirrors the logic for choosing a backend given compile-time flags and client having
// provided DEFAULT as the backend (see PlatformFactory.cpp)
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(IOS) && \
!defined(__APPLE__) && defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
if (backend == Engine::Backend::DEFAULT) {
backend = Engine::Backend::VULKAN;
}
#endif
if (backend == Engine::Backend::VULKAN) {
mFilamentApp->mVulkanPlatform = new FilamentAppVulkanPlatform(config.vulkanGPUHint);
return Engine::Builder()
.backend(backend)
.platform(mFilamentApp->mVulkanPlatform)
.build();
}
return Engine::Builder().backend(backend).build();
};
if (config.headless) {
mFilamentApp->mEngine = Engine::create(config.backend);
mFilamentApp->mEngine = createEngine();
mSwapChain = mFilamentApp->mEngine->createSwapChain((uint32_t) w, (uint32_t) h);
mWidth = w;
mHeight = h;
@@ -549,7 +605,7 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
// Create the Engine after the window in case this happens to be a single-threaded platform.
// For single-threaded platforms, we need to ensure that Filament's OpenGL context is
// current, rather than the one created by SDL.
mFilamentApp->mEngine = Engine::create(config.backend);
mFilamentApp->mEngine = createEngine();
// get the resolved backend
mBackend = config.backend = mFilamentApp->mEngine->getBackend();
@@ -829,7 +885,7 @@ FilamentApp::CView::~CView() {
engine.destroy(view);
}
void FilamentApp::CView::setViewport(Viewport const& viewport) {
void FilamentApp::CView::setViewport(filament::Viewport const& viewport) {
mViewport = viewport;
view->setViewport(viewport);
if (mCameraManipulator) {

View File

@@ -91,6 +91,9 @@ if (APPLE)
list(APPEND SRCS src/darwin/Path.mm)
list(APPEND SRCS src/darwin/Systrace.cpp)
endif()
if (WEBGL)
list(APPEND SRCS src/web/Path.cpp)
endif()
# ==================================================================================================
# Includes and target definition

View File

@@ -253,6 +253,12 @@ public:
*/
static Path getTemporaryDirectory();
/**
* @return a path representing a directory where settings files can be stored,
* it is recommended to append an app specific folder name to that path
*/
static Path getUserSettingsDirectory();
/**
* Creates a directory denoted by the given path.
* This is not recursive and doesn't create intermediate directories.

View File

@@ -19,6 +19,8 @@
#include <vector>
#include <dirent.h>
#include <pwd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <mach-o/dyld.h>
@@ -49,6 +51,17 @@ Path Path::getTemporaryDirectory() {
return Path([tempDir cStringUsingEncoding:NSUTF8StringEncoding]);
}
Path Path::getUserSettingsDirectory() {
const char* home = getenv("HOME");
if (!home) {
struct passwd* pwd = getpwuid(getuid());
if (pwd) {
home = pwd->pw_dir;
}
}
return Path(home);
}
std::vector<Path> Path::listContents() const {
// Return an empty vector if the path doesn't exist or is not a directory
if (!isDirectory() || !exists()) {

View File

@@ -17,6 +17,8 @@
#include <utils/Path.h>
#include <dirent.h>
#include <pwd.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -45,6 +47,17 @@ Path Path::getTemporaryDirectory() {
return Path("/tmp/");
}
Path Path::getUserSettingsDirectory() {
const char* home = getenv("HOME");
if (!home) {
struct passwd* pwd = getpwuid(getuid());
if (pwd) {
home = pwd->pw_dir;
}
}
return Path(home);
}
std::vector<Path> Path::listContents() const {
// Return an empty vector if the path doesn't exist or is not a directory
if (!isDirectory() || !exists()) {

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 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.
*/
#include <utils/Path.h>
namespace utils {
bool Path::mkdir() const {
return true;
}
Path Path::getCurrentExecutable() {
return Path("filament-wasm");
}
Path Path::getUserSettingsDirectory() {
return Path(".");
}
} // namespace utils

View File

@@ -18,6 +18,7 @@
#include <direct.h>
#include <Strsafe.h>
#include <shlobj.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <windows.h>
@@ -46,6 +47,12 @@ Path Path::getTemporaryDirectory() {
return Path(lpTempPathBuffer);
}
Path Path::getUserSettingsDirectory() {
TCHAR home[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, home);
return Path(home);
}
std::vector<Path> Path::listContents() const {
// Return an empty vector if the path doesn't exist or is not a directory
if (!isDirectory() || !exists()) {

View File

@@ -182,7 +182,11 @@ static void printUsage(char* name) {
" A / D: left / right\n"
" E / Q: up / down\n\n"
" --split-view, -v\n"
" Splits the window into 4 views\n"
" Splits the window into 4 views\n\n"
" --vulkan-gpu-hint=<hint>, -g\n"
" Vulkan backend allows user to choose their GPU.\n"
" You can provide the index of the GPU or\n"
" a substring to match against the device name\n\n"
);
const std::string from("SHOWCASE");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
@@ -197,20 +201,21 @@ static std::ifstream::pos_type getFileSize(const char* filename) {
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
static constexpr const char* OPTSTR = "ha:f:i:usc:rt:b:ev";
static constexpr const char* OPTSTR = "ha:f:i:usc:rt:b:evg:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ "feature-level",required_argument, nullptr, 'f' },
{ "batch", required_argument, nullptr, 'b' },
{ "headless", no_argument, nullptr, 'e' },
{ "ibl", required_argument, nullptr, 'i' },
{ "ubershader", no_argument, nullptr, 'u' },
{ "actual-size", no_argument, nullptr, 's' },
{ "camera", required_argument, nullptr, 'c' },
{ "recompute-aabb", no_argument, nullptr, 'r' },
{ "settings", required_argument, nullptr, 't' },
{ "split-view", no_argument, nullptr, 'v' },
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ "feature-level", required_argument, nullptr, 'f' },
{ "batch", required_argument, nullptr, 'b' },
{ "headless", no_argument, nullptr, 'e' },
{ "ibl", required_argument, nullptr, 'i' },
{ "ubershader", no_argument, nullptr, 'u' },
{ "actual-size", no_argument, nullptr, 's' },
{ "camera", required_argument, nullptr, 'c' },
{ "recompute-aabb", no_argument, nullptr, 'r' },
{ "settings", required_argument, nullptr, 't' },
{ "split-view", no_argument, nullptr, 'v' },
{ "vulkan-gpu-hint", required_argument, nullptr, 'g' },
{ nullptr, 0, nullptr, 0 }
};
int opt;
@@ -279,6 +284,10 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
app->config.splitView = true;
break;
}
case 'g': {
app->config.vulkanGPUHint = arg;
break;
}
}
}
if (app->config.headless && app->batchFile.empty()) {

View File

@@ -237,7 +237,7 @@ float filterPCSS(const mediump sampler2DArray map,
const highp vec2 filterRadii, const mat2 R, const highp vec2 dz_duv,
const uint tapCount) {
float occludedCount = 0.0;
float occludedCount = 0.0; // must be highp to workaround a spirv-tools issue
for (uint i = 0u; i < tapCount; i++) {
highp vec2 duv = R * (poissonDisk[i] * filterRadii);

View File

@@ -7,14 +7,12 @@ set(TARGET cmgen)
# Sources and headers
# ==================================================================================================
set(HDRS
src/JobQueue.h
src/ProgressUpdater.h
src/ProgressUpdater.h
)
set(SRCS
src/cmgen.cpp
src/JobQueue.cpp
src/ProgressUpdater.cpp
src/ProgressUpdater.cpp
)
# ==================================================================================================

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#include "JobQueue.h"
JobQueue::Job JobQueue::pop() {
Job job;
std::lock_guard<std::mutex> lock(m_lock);
if (!m_queue.empty()) {
std::swap(job, m_queue.front());
m_queue.pop_front();
}
return job;
}
void JobQueue::enqueue(JobQueue::Job&& job) {
std::lock_guard<std::mutex> lock(m_lock);
m_queue.push_back(std::forward<JobQueue::Job>(job));
}
bool JobQueue::runJobIfAny() {
Job job(pop());
// call the job without holding our lock
if (job) {
job();
return true;
}
return false;
}
void JobQueue::runAllJobs() {
std::deque<Job> q;
std::unique_lock<std::mutex> lock(m_lock);
std::swap(q, m_queue);
lock.unlock();
for (auto& job : q) {
job();
}
}
bool JobQueue::isEmpty() const {
std::lock_guard<std::mutex> lock(m_lock);
return m_queue.empty();
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#pragma once
#include <deque>
#include <functional>
#include <mutex>
/**
* A simple thread-safe job queue.
*
* JobQueue allows to push "jobs" (i.e.: code and its state) into a queue and later execute these
* job in FIFO order, possibly (and generally) from another thread.
*
* example:
* @code
* using utils::JobQueue;
* static JobQueue sJobs;
*
* struct Foo {
* void bar();
* void baz(int);
* } foo;
*
* sJobs.push([]() {
* // one can use a lambda
* });
*
* sJobs.push(&Foo::bar, foo) {
* // ...or a method of a class
* }
*
* sJobs.push(&Foo::bar, baz, 42) {
* // ... even a method with arguments
* }
*
* // Later, in another thread for instance...
*
* // empty the queue and runs all jobs in FIFO order
* sJobs.runAllJobs();
*
* // runs the oldest job if there is one
* bool got_one = sJobs.runJobIfAny();
*
* // dequeue a job, but run it manually.
* Job job(sJobs.pop());
* if (job) {
* job();
* }
*
* @endcode
*
* @warning When both sides of the JobQueue are in different threads (as it is usually the case),
* make sure to capture all stack parameters of the job by value (i.e.: do not capture by
* reference, parameters that live on the stack).
*/
class JobQueue {
public:
using Job = std::function<void()>;
JobQueue() = default;
/**
* Push a job to the back of the queue.
* @param func anything that can be called
* (e.g.: lambda, function or method with optional parameters)
* @param args optional parameters to method or function.
*/
template<typename CALLABLE, typename ... ARGS>
void push(CALLABLE&& func, ARGS&&... args) {
enqueue(Job(std::bind(std::forward<CALLABLE>(func), std::forward<ARGS>(args)...)));
}
/**
* Checks whether the JobQueue is empty.
* @return true if there is no jobs in the queue.
*/
bool isEmpty() const;
/**
* Dequeues the oldest job and executes (runs) it.
* @return true if a job was run.
*/
bool runJobIfAny();
/**
* Empties the queue and runs all jobs atomically w.r.t. the queue. Jobs are run in FIFO order.
*/
void runAllJobs();
/**
* Dequeues the oldest job.
* @return a handle to the oldest job or null if the queue was empty.
* @note the job can be manually run by calling job().
*/
Job pop();
private:
JobQueue(const JobQueue& queue) = delete;
JobQueue& operator=(const JobQueue& queue) = delete;
void enqueue(Job&& job);
std::deque<Job> m_queue;
mutable std::mutex m_lock;
};

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.38.0",
"version": "1.39.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",