Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11fbacea20 | ||
|
|
dba49f00df | ||
|
|
89c0b44da9 | ||
|
|
4f450fd5c4 | ||
|
|
f0943cfca2 | ||
|
|
9aa52b79d4 | ||
|
|
a82125dbbd | ||
|
|
24fcb299b5 | ||
|
|
b0238c1560 | ||
|
|
5ffb52a17d | ||
|
|
6b34e72418 | ||
|
|
d43c632e55 | ||
|
|
d55dfdebde | ||
|
|
4b12876ebc | ||
|
|
3e12449b8f | ||
|
|
fdc3f302d9 | ||
|
|
07642fec83 | ||
|
|
4ce93232c3 | ||
|
|
ccea370cd9 | ||
|
|
8f6885689b | ||
|
|
64b15fafe2 | ||
|
|
d891acb8c0 | ||
|
|
de0bc718be | ||
|
|
283a06e200 | ||
|
|
3cfb2f8339 | ||
|
|
21164885e8 | ||
|
|
0d745c4fea |
@@ -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
|
||||
|
||||
@@ -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**]
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ®ion);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -191,7 +191,7 @@ private:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* // Declares a "linear sRGB" color space.
|
||||
* ColorSpace myColorSpace = Rec709-Linear-sRGB;
|
||||
* ColorSpace myColorSpace = Rec709-Linear-D65;
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
class PartialColorSpace {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
ImGuiContext* mImGuiContext;
|
||||
filament::TextureSampler mSampler;
|
||||
bool mFlipVertical = false;
|
||||
utils::Path mSettingsPath;
|
||||
};
|
||||
|
||||
} // namespace filagui
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
33
libs/utils/src/web/Path.cpp
Normal file
33
libs/utils/src/web/Path.cpp
Normal 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
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user