Compare commits

...
Author SHA1 Message Date
Run Yu 5f14ebba87 Use MapAsync and AllowProcessEvents for callback 2025-11-19 11:19:27 -05:00
Doris Wu aa4e2c56b5 buffer update opt: Skip releaseFreeSlots when no frees are pending (#9437) 2025-11-15 10:23:27 +08:00
Powei Feng aa4f1910b8 android: add static annotation for Utils.init() (#9435)
This allows for static call from java, e.g.:

```
static {
    Utils.init();
}
```
2025-11-13 22:01:49 +00:00
Mathias Agopian 223a4b18a8 Fix several problems with RenderPass descriptor sets (#9431)
The root of the problem is that in the main rendering loop we need
to set the correct per-view descriptor-set based on the material and
variants.

But we have two cases, either the descriptor set is always constant, 
which is the case with ssr, structure, shadows and postfx passes, or
it need to be dynamically changed based on the material & variant.

In the 2nd case, where was a problem where the postfx descriptor set
could be used, which is wrong (e.g. while rendering shadows we need
the shadow UBO, even with a postfx material), and would corrupt the
correct descriptor set, which would never be set back.

Another issue was related to running a custom command, it could 
change the state without updating the local copy, causing corruptions
or validation errors.

In this CL we:
- use the same descriptorsetlayout for both postfx and depth
- invalidate the state after a custom command
- only switch to postfx descriptor set in dynamic mode (color pass)


This fixes setChannelDepthClearEnabled() which could cause 
validation error, corruption or crashes.

FIXES=[459567258]
2025-11-13 12:07:16 -08:00
rafadevai 57ef534acd VK: Add support for getting pipeline creation stats (#9422)
* VK: Add support for getting pipeline creation stats

When debugging is pipeline compilation related hitches
is useful to know more information about what happened
when building a new pipeline.

By levering the VK_EXT_pipeline_creation_feedback
extension, we can know if the pipeline creation was
speed up because of a cache hit, how much time it spent
creating it and also the stats related to each
pipeline stage.

This is only available when using the debug flag
FVK_DEBUG_SHADER_MODULE.

* Fix the unused build error
2025-11-13 09:01:02 -08:00
rafadevai 719914fb84 VK: Better support for renderdoc captures (#9423)
Doing a renderdoc capture when using transient memory
or importing AHardwareBuffers into Vulkan, makes it fail,
crash or not replay properly.

To improve the capture support, added a new constant to let the
backend know when renderdoc is expected to be used and configure
things appropietly.

- In the case of transient attachments that uses lazy allocated
memory, it will be disabled.
- In the case of AHardwareBuffers, a more robust memory heap
selection algorithm will be used.

Also a new VulkanPlatform function will be added, to let other
subclasses know that the transient attachments are enabled or not.
2025-11-13 07:40:51 -08:00
Powei Feng 080f958da3 android: remove checked in asset (#9426)
For sample-gltf-viewer, an asset was checked into the source tree.
But other assets are generated or copied from asset directories.

We remove the checked in gltf and copy the asset over during
build (as with other existing files).
2025-11-13 06:29:45 +00:00
Doris Wu 7547aa3807 buffer update opt: Integrate UboManager into the engine (#9397) 2025-11-13 02:19:48 +00:00
Sungun Park 67a0c6e0e1 fix a typo in VertexBuffer (#9427) 2025-11-12 15:31:55 -08:00
Ben Doherty 37cb842993 Fix possible null string crash inside TextWriter (#9428) 2025-11-12 12:07:16 -08:00
36 changed files with 436 additions and 152 deletions
+2
View File
@@ -8,3 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut ## Release notes for next branch cut
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622) - engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
@@ -22,6 +22,7 @@ object Utils {
/** /**
* Initializes the utils JNI layer. Must be called before using any utils functionality. * Initializes the utils JNI layer. Must be called before using any utils functionality.
*/ */
@JvmStatic
fun init() { fun init() {
// Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv. // Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv.
Filament.init() Filament.init()
@@ -17,12 +17,20 @@ filamentTools {
} }
// don't forget to update MainACtivity.kt when/if changing this. // don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyMesh', Copy) { tasks.register('copyDamagedHelmetGltf', Copy) {
from file("../../../third_party/models/DamagedHelmet/DamagedHelmet.glb")
into file("src/main/assets/models")
rename {String fileName -> "helmet.glb"}
}
// don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyBusterGltf', Copy) {
from "../../../third_party/models/BusterDrone" from "../../../third_party/models/BusterDrone"
into "src/main/assets/models" into "src/main/assets/models"
} }
preBuild.dependsOn copyMesh preBuild.dependsOn copyDamagedHelmetGltf
preBuild.dependsOn copyBusterGltf
clean.doFirst { clean.doFirst {
delete "src/main/assets" delete "src/main/assets"
@@ -444,6 +444,11 @@ protected:
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept; virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
/**
* Query if transient attachments are supported by the backend.
*/
bool isTransientAttachmentSupported() const noexcept;
private: private:
friend struct VulkanPlatformPrivate; friend struct VulkanPlatformPrivate;
}; };
@@ -214,4 +214,10 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
// destroying any unused pipeline object. // destroying any unused pipeline object.
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS); static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS);
// Indicates if the backend must be setup to allow doing a RenderDoc capture.
//
// If this is true, the features not supported by RenderDoc must be disabled, otherwise
// when using RenderDoc the application will crash or will fail to do a capture.
constexpr static const int FVK_RENDERDOC_CAPTURE_MODE = false;
#endif #endif
@@ -154,6 +154,10 @@ public:
return mStagingBufferBypassEnabled; return mStagingBufferBypassEnabled;
} }
inline bool pipelineCreationFeedbackSupported() const noexcept {
return mPipelineCreationFeedbackSupported;
}
private: private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
@@ -181,6 +185,7 @@ private:
bool mProtectedMemorySupported = false; bool mProtectedMemorySupported = false;
bool mIsUnifiedMemoryArchitecture = false; bool mIsUnifiedMemoryArchitecture = false;
bool mStagingBufferBypassEnabled = false; bool mStagingBufferBypassEnabled = false;
bool mPipelineCreationFeedbackSupported = false;
fvkutils::VkFormatList mDepthStencilFormats; fvkutils::VkFormatList mDepthStencilFormats;
fvkutils::VkFormatList mBlittableDepthStencilFormats; fvkutils::VkFormatList mBlittableDepthStencilFormats;
+1 -1
View File
@@ -231,7 +231,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager), mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
mPipelineLayoutCache(mPlatform->getDevice()), mPipelineLayoutCache(mPlatform->getDevice()),
mPipelineCache(mPlatform->getDevice()), mPipelineCache(mPlatform->getDevice(), mContext),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()), mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
mBufferCache(mContext, mResourceManager, mAllocator), mBufferCache(mContext, mResourceManager, mAllocator),
mFramebufferCache(mPlatform->getDevice()), mFramebufferCache(mPlatform->getDevice()),
@@ -34,8 +34,41 @@ using namespace bluevk;
namespace filament::backend { namespace filament::backend {
VulkanPipelineCache::VulkanPipelineCache(VkDevice device) namespace {
: mDevice(device) {
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
void printPipelineFeedbackInfo(VkPipelineCreationFeedbackCreateInfo const& feedbackInfo) {
VkPipelineCreationFeedback const& pipelineInfo = *feedbackInfo.pPipelineCreationFeedback;
if (!(pipelineInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT)) {
return;
}
bool const isCacheHit =
(pipelineInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT);
FVK_LOGD << "Pipeline build stats - Cache hit: " << isCacheHit
<< ", Time: " << pipelineInfo.duration / 1000000.0 << "ms";
for (uint32_t i = 0; i < feedbackInfo.pipelineStageCreationFeedbackCount; ++i) {
VkPipelineCreationFeedback const& stageInfo = feedbackInfo.pPipelineStageCreationFeedbacks[i];
if (!(stageInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT)) {
continue;
}
bool const isVertexShader = (i == 0);
bool const isCacheHit = (stageInfo.flags &
VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT);
FVK_LOGD << (isVertexShader ? "Vertex" : "Fragment")
<< " shader build stats - Cache hit: " << isCacheHit
<< ", Time: " << stageInfo.duration / 1000000.0 << "ms";
}
}
#endif
} // namespace
VulkanPipelineCache::VulkanPipelineCache(VkDevice device, VulkanContext const& context)
: mDevice(device),
mContext(context) {
VkPipelineCacheCreateInfo createInfo = { VkPipelineCacheCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
}; };
@@ -217,14 +250,39 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
} }
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
<< shaderStages[0].module << ", " << shaderStages[1].module << ")"; << shaderStages[1].module << ")";
VkPipelineCreationFeedback stageFeedbacks[SHADER_MODULE_COUNT] = {};
VkPipelineCreationFeedback pipelineFeedback = {};
VkPipelineCreationFeedbackCreateInfo feedbackInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT,
.pNext = nullptr,
.pPipelineCreationFeedback = &pipelineFeedback,
.pipelineStageCreationFeedbackCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1,
.pPipelineStageCreationFeedbacks = stageFeedbacks,
};
if (mContext.pipelineCreationFeedbackSupported()) {
feedbackInfo.pNext = pipelineCreateInfo.pNext;
pipelineCreateInfo.pNext = &feedbackInfo;
}
#endif #endif
PipelineCacheEntry cacheEntry = { PipelineCacheEntry cacheEntry = {
.lastUsed = mCurrentTime, .lastUsed = mCurrentTime,
}; };
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo, VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle); VKALLOC, &cacheEntry.handle);
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
<< shaderStages[1].module << ")";
if (mContext.pipelineCreationFeedbackSupported()) {
printPipelineFeedbackInfo(feedbackInfo);
}
#endif
assert_invariant(error == VK_SUCCESS); assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) { if (error != VK_SUCCESS) {
FVK_LOGE << "vkCreateGraphicsPipelines error " << error; FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
@@ -86,7 +86,7 @@ public:
static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding."); static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding.");
VulkanPipelineCache(VkDevice device); VulkanPipelineCache(VkDevice device, VulkanContext const& context);
void bindLayout(VkPipelineLayout layout) noexcept; void bindLayout(VkPipelineLayout layout) noexcept;
@@ -210,6 +210,8 @@ private:
// Current bindings for the pipeline and descriptor sets. // Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {}; PipelineKey mBoundPipeline = {};
[[maybe_unused]] VulkanContext const& mContext;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -228,6 +228,10 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
#endif #endif
VK_KHR_MULTIVIEW_EXTENSION_NAME, VK_KHR_MULTIVIEW_EXTENSION_NAME,
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME,
#endif
}; };
ExtensionSet exts; ExtensionSet exts;
// Identify supported physical device extensions // Identify supported physical device extensions
@@ -865,6 +869,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
if (!mImpl->mSharedContext) { if (!mImpl->mSharedContext) {
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mPipelineCreationFeedbackSupported =
setContains(deviceExts, VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
} else { } else {
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext; VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
context.mDebugUtilsSupported = scontext->debugUtilsSupported; context.mDebugUtilsSupported = scontext->debugUtilsSupported;
@@ -873,6 +879,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// Check the availability of lazily allocated memory // Check the availability of lazily allocated memory
context.mLazilyAllocatedMemorySupported = false; context.mLazilyAllocatedMemorySupported = false;
// RenderDoc doesn't support lazy allocated memory
if constexpr (!FVK_RENDERDOC_CAPTURE_MODE) {
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount; for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
++i) { ++i) {
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i]; VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
@@ -882,6 +890,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
break; break;
} }
} }
}
context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties); context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties);
@@ -1022,4 +1031,8 @@ VkExternalFenceHandleTypeFlagBits VulkanPlatform::getFenceExportFlags() const no
return static_cast<VkExternalFenceHandleTypeFlagBits>(0); return static_cast<VkExternalFenceHandleTypeFlagBits>(0);
} }
bool VulkanPlatform::isTransientAttachmentSupported() const noexcept {
return mImpl->mContext.isLazilyAllocatedMemorySupported();
}
} // namespace filament::backend } // namespace filament::backend
@@ -158,6 +158,46 @@ std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardware
}; };
} }
uint32_t selectMemoryTypeForExternalImage(VkPhysicalDevice physicalDevice, VkDevice device,
VkImage image, uint32_t types, VkFlags requiredMemoryFlags) {
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
uint32_t const memoryTypeIndex =
VulkanContext::selectMemoryType(memoryProperties, types, requiredMemoryFlags);
if constexpr (FVK_RENDERDOC_CAPTURE_MODE) {
// RenderDoc will replay external resources as non-external.
// Adjust properties that will trip it up when replaying, even though these are not valid.
// Update memory type index if necessary so that we can replay the capture.
VkMemoryRequirements imageMemoryRequirements;
vkGetImageMemoryRequirements(device, image, &imageMemoryRequirements);
uint32_t const imageMemoryTypeBits = imageMemoryRequirements.memoryTypeBits;
bool const isMemoryTypeSupported = ((1 << memoryTypeIndex) & imageMemoryTypeBits) != 0;
if (isMemoryTypeSupported) {
return memoryTypeIndex;
}
// Current memory type will not be replayable by RenderDoc.
// Attempt to change the memory type index
VkMemoryPropertyFlags const kRenderDocFallBackReqs = 0;
uint32_t commonMemoryTypeBits = types & imageMemoryTypeBits;
uint32_t commonTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
commonMemoryTypeBits, kRenderDocFallBackReqs);
if (commonMemoryTypeBits && commonTypeIndex != VK_MAX_MEMORY_TYPES) {
return commonTypeIndex;
}
uint32_t imageTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
imageMemoryTypeBits, kRenderDocFallBackReqs);
if (imageTypeIndex != VK_MAX_MEMORY_TYPES) {
return imageTypeIndex;
}
}
return memoryTypeIndex;
}
} // namespace } // namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() { VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
@@ -354,8 +394,6 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
.image = image, .image = image,
.buffer = VK_NULL_HANDLE, .buffer = VK_NULL_HANDLE,
}; };
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
VkMemoryPropertyFlags requiredMemoryFlags = VkMemoryPropertyFlags requiredMemoryFlags =
!isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE) !isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
@@ -365,8 +403,8 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT; requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
} }
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties, uint32_t const memoryTypeIndex = selectMemoryTypeForExternalImage(physicalDevice, device,
metadata.memoryTypeBits, requiredMemoryFlags); image, metadata.memoryTypeBits, requiredMemoryFlags);
VkMemoryAllocateInfo const allocInfo = { VkMemoryAllocateInfo const allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
@@ -63,7 +63,7 @@ WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::Buffe
// WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a // WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a
// multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple // multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple
// of 4 by padding with zeros. // of 4 by padding with zeros.
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor, void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor&& bufferDescriptor,
const uint32_t byteOffset, wgpu::Device const& device, const uint32_t byteOffset, wgpu::Device const& device,
WebGPUQueueManager* const webGPUQueueManager) { WebGPUQueueManager* const webGPUQueueManager) {
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer) FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
@@ -89,24 +89,49 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
wgpu::BufferDescriptor descriptor{ wgpu::BufferDescriptor descriptor{
.label = "Filament WebGPU Staging Buffer", .label = "Filament WebGPU Staging Buffer",
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, .usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
.size = stagingBufferSize, .size = stagingBufferSize};
.mappedAtCreation = true };
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor); wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
void* mappedRange = stagingBuffer.GetMappedRange(); struct UserData final {
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size); uint32_t byteOffset;
size_t stagingBufferSize;
// Make sure the padded memory is set to 0 to have deterministic behaviors size_t remainder;
if (remainder != 0) { BufferDescriptor srcBufferDescriptor;
uint8_t* paddingStart = static_cast<uint8_t*>(mappedRange) + bufferDescriptor.size; wgpu::Buffer stagingBuffer;
memset(paddingStart, 0, FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - remainder); WebGPUQueueManager* const webGPUQueueManager;
wgpu::Buffer dstBuffer;
};
auto userData = std::make_unique<UserData>(UserData{
.byteOffset = byteOffset,
.stagingBufferSize = stagingBufferSize,
.remainder = remainder,
.srcBufferDescriptor = std::move(bufferDescriptor),
.stagingBuffer = stagingBuffer,
.webGPUQueueManager = webGPUQueueManager,
.dstBuffer = mBuffer});
stagingBuffer.MapAsync(
wgpu::MapMode::Write, 0, stagingBufferSize, wgpu::CallbackMode::AllowProcessEvents,
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
void* mappedRange = data->stagingBuffer.GetMappedRange();
memcpy(mappedRange, data->srcBufferDescriptor.buffer,
data->srcBufferDescriptor.size);
if (data->remainder != 0) {
uint8_t* paddingStart =
static_cast<uint8_t*>(mappedRange) + data->srcBufferDescriptor.size;
memset(paddingStart, 0,
FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - data->remainder);
} }
data->stagingBuffer.Unmap();
stagingBuffer.Unmap(); data->webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(
data->stagingBuffer, 0, data->dstBuffer, data->byteOffset,
// Copy the staging buffer contents to the destination buffer. data->stagingBufferSize);
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer, } else {
byteOffset, stagingBufferSize); FWGPU_LOGE << "Failed to map staging buffer for readPixels: " << message;
}
},
userData.release());
} }
} // namespace filament::backend } // namespace filament::backend
@@ -39,7 +39,7 @@ public:
* happen after draw commands encoded in the encoder. Submitting any commands up to this point * happen after draw commands encoded in the encoder. Submitting any commands up to this point
* ensures the calls happen in the expected sequence. * ensures the calls happen in the expected sequence.
*/ */
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device, void updateGPUBuffer(BufferDescriptor&&, uint32_t byteOffset, wgpu::Device const& device,
WebGPUQueueManager* const webGPUQueueManager); WebGPUQueueManager* const webGPUQueueManager);
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; } [[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
+3 -3
View File
@@ -856,7 +856,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
// draw calls are made. // draw calls are made.
flush(); flush();
handleCast<WebGPUIndexBuffer>(indexBufferHandle) handleCast<WebGPUIndexBuffer>(indexBufferHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager); ->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor)); scheduleDestroy(std::move(bufferDescriptor));
} }
@@ -867,14 +867,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
// draw calls are made. // draw calls are made.
flush(); flush();
handleCast<WebGPUBufferObject>(bufferObjectHandle) handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager); ->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor)); scheduleDestroy(std::move(bufferDescriptor));
} }
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle, void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) { BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
handleCast<WebGPUBufferObject>(bufferObjectHandle) handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager); ->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
scheduleDestroy(std::move(bufferDescriptor)); scheduleDestroy(std::move(bufferDescriptor));
} }
@@ -21,6 +21,7 @@
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <thread> #include <thread>
#include <iostream>
namespace filament::backend { namespace filament::backend {
@@ -58,6 +59,7 @@ WebGPUQueueManager::WebGPUQueueManager(wgpu::Device const& device)
WebGPUQueueManager::~WebGPUQueueManager() = default; WebGPUQueueManager::~WebGPUQueueManager() = default;
wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() { wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
// std::unique_lock<std::mutex> lock(mLock);
if (!mCommandEncoder) { if (!mCommandEncoder) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "Filament Command Encoder", .label = "Filament Command Encoder",
@@ -90,7 +92,9 @@ std::shared_ptr<WebGPUSubmissionState> WebGPUQueueManager::getLatestSubmissionSt
} }
void WebGPUQueueManager::submit() { void WebGPUQueueManager::submit() {
// std::unique_lock<std::mutex> lock(mLock);
if (!mCommandEncoder) { if (!mCommandEncoder) {
std::cout << "Run Yu: no mCommandEncoder found!\n";
return; return;
} }
@@ -74,6 +74,7 @@ private:
wgpu::Queue mQueue; wgpu::Queue mQueue;
wgpu::CommandEncoder mCommandEncoder; wgpu::CommandEncoder mCommandEncoder;
std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState; std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState;
std::mutex mLock;
}; };
} // namespace filament::backend } // namespace filament::backend
+23 -19
View File
@@ -258,6 +258,10 @@ void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) const
{ { 0, 0 }, driver }); { { 0, 0 }, driver });
} }
UboManager* PostProcessManager::getUboManager() const noexcept {
return mEngine.getUboManager();
}
UTILS_NOINLINE UTILS_NOINLINE
void PostProcessManager::registerPostProcessMaterial(std::string_view const name, void PostProcessManager::registerPostProcessMaterial(std::string_view const name,
StaticMaterialInfo const& info) { StaticMaterialInfo const& info) {
@@ -513,7 +517,7 @@ UTILS_NOINLINE
void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver, void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
FrameGraphResources::RenderPassInfo const& out, FMaterialInstance const* mi, FrameGraphResources::RenderPassInfo const& out, FMaterialInstance const* mi,
PostProcessVariant const variant) const noexcept { PostProcessVariant const variant) const noexcept {
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
FMaterial const* const ma = mi->getMaterial(); FMaterial const* const ma = mi->getMaterial();
PipelineState const pipeline = getPipelineState(ma, variant); PipelineState const pipeline = getPipelineState(ma, variant);
@@ -645,7 +649,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
auto th = driver.createTextureView(in, level, 1); auto th = driver.createTextureView(in, level, 1);
mi->setParameter("depth", th, SamplerParams{ mi->setParameter("depth", th, SamplerParams{
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuad(out, pipeline, driver); renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL); DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
@@ -1079,7 +1083,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
mi->setParameter("ssctRayCount", mi->setParameter("ssctRayCount",
float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) }); float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -1200,7 +1204,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
mi->setParameter("sampleCount", kGaussianCount); mi->setParameter("sampleCount", kGaussianCount);
mi->setParameter("farPlaneOverEdgeDistance", -zf / config.bilateralThreshold); mi->setParameter("farPlaneOverEdgeDistance", -zf / config.bilateralThreshold);
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -1895,7 +1899,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres? mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres?
mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h }); mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuad(out, pipeline, driver); renderFullScreenQuad(out, pipeline, driver);
@@ -2425,7 +2429,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
mi->setParameter("source", hwOutView, SamplerParams{ mi->setParameter("source", hwOutView, SamplerParams{
.filterMag = SamplerMagFilter::LINEAR, .filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST}); .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuad(hwDstRT, pipeline, driver); renderFullScreenQuad(hwDstRT, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL); DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
@@ -2533,7 +2537,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
FMaterialInstance* const mi = FMaterialInstance* const mi =
configureColorGradingMaterial(material, colorGrading, colorGradingConfig, configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
vignetteOptions, width, height); vignetteOptions, width, height);
mi->commit(driver); mi->commit(driver, getUboManager());
} }
void PostProcessManager::colorGradingSubpass(DriverApi& driver, void PostProcessManager::colorGradingSubpass(DriverApi& driver,
@@ -2566,7 +2570,7 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma); auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
mFixedMaterialInstanceIndex.customResolve = fixedIndex; mFixedMaterialInstanceIndex.customResolve = fixedIndex;
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f), mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
mi->commit(driver); mi->commit(driver, getUboManager());
material.getMaterial(mEngine); material.getMaterial(mEngine);
} }
@@ -2619,7 +2623,7 @@ void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver) noexcep
auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE); auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma); auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
mFixedMaterialInstanceIndex.clearDepth = fixedIndex; mFixedMaterialInstanceIndex.clearDepth = fixedIndex;
mi->commit(driver); mi->commit(driver, getUboManager());
material.getMaterial(mEngine); material.getMaterial(mEngine);
} }
@@ -3111,7 +3115,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
mat4f{ historyProjection * inverse(current.projection) } * mat4f{ historyProjection * inverse(current.projection) } *
normalizedToClip); normalizedToClip);
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
if (colorGradingConfig.asSubpass) { if (colorGradingConfig.asSubpass) {
@@ -3196,7 +3200,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
mi->setParameter("resolution", float4{ mi->setParameter("resolution", float4{
outputDesc.width, outputDesc.height, outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height }); 1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(material.getMaterial(mEngine), variant); auto pipeline = getPipelineState(material.getMaterial(mEngine), variant);
@@ -3287,7 +3291,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
float(vp.width) / inputDesc.width, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height float(vp.height) / inputDesc.height
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto out = resources.getRenderPassInfo(); auto out = resources.getRenderPassInfo();
@@ -3374,7 +3378,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
float(inputDesc.height) float(inputDesc.height)
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto const out = resources.getRenderPassInfo(); auto const out = resources.getRenderPassInfo();
@@ -3469,7 +3473,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
mi->setParameter("resolution", mi->setParameter("resolution",
float4{ outputDesc.width, outputDesc.height, float4{ outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height }); 1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
} }
@@ -3496,7 +3500,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
float(vp.width) / inputDesc.width, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height float(vp.height) / inputDesc.height
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
} }
@@ -3582,7 +3586,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
if (layer) { if (layer) {
mi->setParameter("layerIndex", layer); mi->setParameter("layerIndex", layer);
} }
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -3840,7 +3844,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
}); });
mi->setParameter("layer", uint32_t(layer)); mi->setParameter("layer", uint32_t(layer));
mi->setParameter("uvscale", 1.0f / float(dim)); mi->setParameter("uvscale", 1.0f / float(dim));
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
renderFullScreenQuadWithScissor(out, pipeline, scissor, driver); renderFullScreenQuadWithScissor(out, pipeline, scissor, driver);
@@ -3942,7 +3946,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
float(vp.width) / inputDesc.width, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height float(vp.height) / inputDesc.height
}); });
mi->commit(driver); mi->commit(driver, getUboManager());
mi->use(driver); mi->use(driver);
auto pipeline = getPipelineState(ma); auto pipeline = getPipelineState(ma);
@@ -3959,7 +3963,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
// Render all layers of the texture to the screen side-by-side. // Render all layers of the texture to the screen side-by-side.
for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) { for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) {
mi->setParameter("layerIndex", i); mi->setParameter("layerIndex", i);
mi->commit(driver); mi->commit(driver, getUboManager());
renderFullScreenQuad(out, pipeline, driver); renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL); DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
// From the second draw, don't clear the targetbuffer. // From the second draw, don't clear the targetbuffer.
+3
View File
@@ -65,6 +65,7 @@ class FMaterialInstance;
class FrameGraph; class FrameGraph;
class RenderPass; class RenderPass;
class RenderPassBuilder; class RenderPassBuilder;
class UboManager;
struct CameraInfo; struct CameraInfo;
class PostProcessManager { class PostProcessManager {
@@ -422,6 +423,8 @@ private:
return getMaterialInstance(ma); return getMaterialInstance(ma);
} }
UboManager* getUboManager() const noexcept;
backend::RenderPrimitiveHandle mFullScreenQuadRph; backend::RenderPrimitiveHandle mFullScreenQuadRph;
backend::VertexBufferInfoHandle mFullScreenQuadVbih; backend::VertexBufferInfoHandle mFullScreenQuadVbih;
backend::DescriptorSetLayoutHandle mPerRenderableDslh; backend::DescriptorSetLayoutHandle mPerRenderableDslh;
+18 -8
View File
@@ -1012,6 +1012,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT; uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size()); assert_invariant(index < mCustomCommands.size());
pCustomCommands[index](); pCustomCommands[index]();
currentPipeline = {};
currentPrimitiveHandle ={};
continue; continue;
} }
@@ -1069,7 +1071,13 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] = pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] =
ma->getDescriptorSetLayout(info.materialVariant).getHandle(); ma->getDescriptorSetLayout(info.materialVariant).getHandle();
// If we have a ColorPassDescriptorSet we use it to bind the per-view
// descriptor-set (ideally only if it changed).
// If we don't, it means the descriptor-set is already bound and the layout we
// got from the material above should match. This is the case for situations
// where we have a known per-view descriptor-set layout,
// e.g.: postfx, shadow-maps, ssr and structure passes.
if (mColorPassDescriptorSet) {
if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) { if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) {
// It is possible to get a post-process material here (even though it's // It is possible to get a post-process material here (even though it's
// not technically a public API yet, it is used by the IBLPrefilterLibrary. // not technically a public API yet, it is used by the IBLPrefilterLibrary.
@@ -1077,18 +1085,20 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
// to set the post-process descriptor-set. // to set the post-process descriptor-set.
engine.getPostProcessManager().bindPostProcessDescriptorSet(driver); engine.getPostProcessManager().bindPostProcessDescriptorSet(driver);
} else { } else {
// If we have a ColorPassDescriptorSet we use it to bind the per-view
// descriptor-set (ideally only if it changed). If we don't, it means
// the descriptor-set is already bound and the layout we got from the
// material above should match. This is the case for situations where we
// have a known per-view descriptor-set layout, e.g.: shadow-maps, ssr and
// structure passes.
if (mColorPassDescriptorSet) {
// We have a ColorPassDescriptorSet, we need to go through it for binding // We have a ColorPassDescriptorSet, we need to go through it for binding
// the per-view descriptor-set because its layout can change based on the // the per-view descriptor-set because its layout can change based on the
// material. // material.
mColorPassDescriptorSet->bind(driver, ma->getPerViewLayoutIndex()); mColorPassDescriptorSet->bind(driver, ma->getPerViewLayoutIndex());
} }
} else {
// if we're here it means the per-view descriptor set is constant and
// already set. This will be the case for postfx, ssr, structure and
// shadow passes. All these passes use a static descriptor set layout
// (albeit potentially different for each pass). In particular the
// per-view UBO must be compatible for all material domains.
// This is the case by construction for postfx, ssr. However, shadows
// and structure have their own UBO, but it's content is (must be)
// compatible with POST_PROCESS and COMPUTE materials.
} }
// Each MaterialInstance has its own descriptor set. This binds it. // Each MaterialInstance has its own descriptor set. This binds it.
+15 -2
View File
@@ -142,7 +142,11 @@ void BufferAllocator::retire(AllocationId id) {
InternalSlotNode* targetNode = getNodeById(id); InternalSlotNode* targetNode = getNodeById(id);
assert_invariant(targetNode != nullptr); assert_invariant(targetNode != nullptr);
targetNode->slot.isAllocated = false; Slot& slot = targetNode->slot;
slot.isAllocated = false;
if (slot.gpuUseCount == 0) {
mHasPendingFrees = true;
}
} }
void BufferAllocator::acquireGpu(AllocationId id) { void BufferAllocator::acquireGpu(AllocationId id) {
@@ -157,10 +161,18 @@ void BufferAllocator::releaseGpu(AllocationId id) {
assert_invariant(targetNode != nullptr); assert_invariant(targetNode != nullptr);
assert_invariant(targetNode->slot.gpuUseCount > 0); assert_invariant(targetNode->slot.gpuUseCount > 0);
targetNode->slot.gpuUseCount--; Slot& slot = targetNode->slot;
slot.gpuUseCount--;
if (slot.gpuUseCount == 0 && !slot.isAllocated) {
mHasPendingFrees = true;
}
} }
void BufferAllocator::releaseFreeSlots() { void BufferAllocator::releaseFreeSlots() {
if (!mHasPendingFrees) {
return;
}
auto curr = mSlotPool.begin(); auto curr = mSlotPool.begin();
while (curr != mSlotPool.end()) { while (curr != mSlotPool.end()) {
if (!curr->slot.isFree()) { if (!curr->slot.isFree()) {
@@ -199,6 +211,7 @@ void BufferAllocator::releaseFreeSlots() {
curr = next; curr = next;
} }
mHasPendingFrees = false;
} }
BufferAllocator::allocation_size_t BufferAllocator::getTotalSize() const noexcept { BufferAllocator::allocation_size_t BufferAllocator::getTotalSize() const noexcept {
+2 -1
View File
@@ -113,8 +113,9 @@ private:
[[nodiscard]] InternalSlotNode* getNodeById(AllocationId id) const noexcept; [[nodiscard]] InternalSlotNode* getNodeById(AllocationId id) const noexcept;
bool mHasPendingFrees = false;
allocation_size_t mTotalSize; allocation_size_t mTotalSize;
const allocation_size_t mSlotSize; // Size of a single slot in bytes. const allocation_size_t mSlotSize; // Size of a single slot in bytes
std::list<InternalSlotNode> mSlotPool; // All slots, including both allocated and freed std::list<InternalSlotNode> mSlotPool; // All slots, including both allocated and freed
std::multimap</*slot size*/allocation_size_t, InternalSlotNode*> mFreeList; std::multimap</*slot size*/allocation_size_t, InternalSlotNode*> mFreeList;
std::unordered_map</*slot offset*/allocation_size_t, InternalSlotNode*> mOffsetMap; std::unordered_map</*slot offset*/allocation_size_t, InternalSlotNode*> mOffsetMap;
+53 -6
View File
@@ -468,11 +468,26 @@ void FEngine::init() {
} }
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this)); mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this));
} }
// We must commit the default material instance here. It may not be used in a scene, but its // We must commit the default material instance here. It may not be used in a scene, but its
// descriptor set may still be used for shared variants. // descriptor set may still be used for shared variants.
mDefaultMaterial->getDefaultInstance()->commit(driverApi); //
// Note that this material instance is instantiated before the creation of UboManager, so at
// this point `isUboBatchingEnabled` is `false`, and it will fall back to individual UBO
// automatically.
mDefaultMaterial->getDefaultInstance()->commit(driverApi, mUboManager);
if (UTILS_UNLIKELY(getSupportedFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) { if (UTILS_UNLIKELY(getSupportedFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) {
// UBO batching is not supported in feature level 0
if (features.material.enable_material_instance_uniform_batching) {
// Ubo size of each material instance is at least 16 bytes.
constexpr BufferAllocator::allocation_size_t minSlotSize = 16;
auto const uboOffsetAlignment = static_cast<BufferAllocator::allocation_size_t>(
driverApi.getUniformBufferOffsetAlignment());
BufferAllocator::allocation_size_t slotSize = std::max(minSlotSize, uboOffsetAlignment);
mUboManager = new UboManager(getDriverApi(), slotSize, mConfig.sharedUboInitialSizeInBytes);
}
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this)); mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
constexpr float3 dummyPositions[1] = {}; constexpr float3 dummyPositions[1] = {};
@@ -653,6 +668,12 @@ void FEngine::shutdown() {
driver.destroyRenderTarget(std::move(mDefaultRenderTarget)); driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
if (isUboBatchingEnabled()) {
mUboManager->terminate(driver);
delete mUboManager;
mUboManager = nullptr;
}
/* /*
* Shutdown the backend... * Shutdown the backend...
*/ */
@@ -693,18 +714,31 @@ void FEngine::prepare() {
// UBOs that are visible only. It's not such a big issue because the actual upload() is // UBOs that are visible only. It's not such a big issue because the actual upload() is
// skipped if the UBO hasn't changed. Still we could have a lot of these. // skipped if the UBO hasn't changed. Still we could have a lot of these.
DriverApi& driver = getDriverApi(); DriverApi& driver = getDriverApi();
const bool useUboBatching = isUboBatchingEnabled();
if (useUboBatching) {
assert_invariant(mUboManager != nullptr);
mUboManager->beginFrame(driver, mMaterialInstances);
}
UboManager* uboManager = mUboManager;
for (auto& materialInstanceList: mMaterialInstances) { for (auto& materialInstanceList: mMaterialInstances) {
materialInstanceList.second.forEach([&driver](FMaterialInstance* item) { materialInstanceList.second.forEach([&driver, uboManager](FMaterialInstance* item) {
// post-process materials instances must be commited explicitly because their // post-process materials instances must be commited explicitly because their
// parameters are typically not set at this point in time. // parameters are typically not set at this point in time.
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) { if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
item->commitStreamUniformAssociations(driver); item->commitStreamUniformAssociations(driver);
item->commit(driver); item->commit(driver, uboManager);
} }
}); });
} }
if (useUboBatching) {
assert_invariant(mUboManager != nullptr);
getUboManager()->finishBeginFrame(getDriverApi());
}
mMaterials.forEach([](FMaterial* material) { mMaterials.forEach([](FMaterial* material) {
#if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner) #if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner)
material->checkProgramEdits(); material->checkProgramEdits();
@@ -721,6 +755,13 @@ void FEngine::gc() {
mCameraManager.gc(*this, em); mCameraManager.gc(*this, em);
} }
void FEngine::submitFrame() {
if (isUboBatchingEnabled()) {
DriverApi& driver = getDriverApi();
getUboManager()->endFrame(driver, getMaterialInstanceResourceList());
}
}
void FEngine::flush() { void FEngine::flush() {
// flush the command buffer // flush the command buffer
flushCommandBuffer(mCommandBufferQueue); flushCommandBuffer(mCommandBufferQueue);
@@ -951,9 +992,10 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
return p; return p;
} }
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
const char* name) noexcept { UboBatchingMode batchingMode) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name); FMaterialInstance* p =
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
if (UTILS_LIKELY(p)) { if (UTILS_LIKELY(p)) {
auto pos = mMaterialInstances.emplace(material, "MaterialInstance"); auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p); pos.first->second.insert(p);
@@ -1243,6 +1285,11 @@ UTILS_NOINLINE
bool FEngine::destroy(const FMaterialInstance* p) { bool FEngine::destroy(const FMaterialInstance* p) {
if (p == nullptr) return true; if (p == nullptr) return true;
if (p->isUsingUboBatching()) {
assert_invariant(isUboBatchingEnabled());
mUboManager->retireSlot(p->getAllocationId());
}
// Check that the material instance we're destroying is not in use in the RenderableManager // Check that the material instance we're destroying is not in use in the RenderableManager
// To do this, we currently need to inspect all render primitives in the RenderableManager // To do this, we currently need to inspect all render primitives in the RenderableManager
EntityManager const& em = mEntityManager; EntityManager const& em = mEntityManager;
+26 -5
View File
@@ -21,16 +21,17 @@
#include "Allocators.h" #include "Allocators.h"
#include "DFG.h" #include "DFG.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "HwDescriptorSetLayoutFactory.h" #include "HwDescriptorSetLayoutFactory.h"
#include "HwVertexBufferInfoFactory.h" #include "HwVertexBufferInfoFactory.h"
#include "MaterialCache.h" #include "MaterialCache.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "UboManager.h"
#include "components/CameraManager.h" #include "components/CameraManager.h"
#include "components/LightManager.h" #include "components/LightManager.h"
#include "components/TransformManager.h"
#include "components/RenderableManager.h" #include "components/RenderableManager.h"
#include "components/TransformManager.h"
#include "ds/DescriptorSetLayout.h" #include "ds/DescriptorSetLayout.h"
@@ -263,6 +264,14 @@ public:
return mBackend; return mBackend;
} }
bool isUboBatchingEnabled() const noexcept {
return mUboManager != nullptr;
}
UboManager* getUboManager() noexcept {
return mUboManager;
}
Platform* getPlatform() const noexcept { Platform* getPlatform() const noexcept {
return mPlatform; return mPlatform;
} }
@@ -336,11 +345,21 @@ public:
FRenderer* createRenderer() noexcept; FRenderer* createRenderer() noexcept;
// Defines whether a material instance should use UBO batching or not.
enum class UboBatchingMode {
// For default, it follows the engine settings.
// If UBO batching is enabled on the engine and the material domain is not SURFACE, it
// turns on the UBO batching. Otherwise, it turns off the UBO batching.
DEFAULT,
NO_UBO_BATCHING,
UBO_BATCHING
};
FMaterialInstance* createMaterialInstance(const FMaterial* material, FMaterialInstance* createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept; const FMaterialInstance* other, const char* name) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material, FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
const char* name) noexcept; UboBatchingMode batchingMode) noexcept;
FScene* createScene() noexcept; FScene* createScene() noexcept;
FView* createView() noexcept; FView* createView() noexcept;
@@ -446,6 +465,7 @@ public:
void prepare(); void prepare();
void gc(); void gc();
void submitFrame();
using ShaderContent = utils::FixedCapacityVector<uint8_t>; using ShaderContent = utils::FixedCapacityVector<uint8_t>;
@@ -650,6 +670,7 @@ private:
uint32_t mFlushCounter = 0; uint32_t mFlushCounter = 0;
UboManager* mUboManager = nullptr;
RootArenaScope::Arena mPerRenderPassArena; RootArenaScope::Arena mPerRenderPassArena;
HeapAllocatorArena mHeapAllocator; HeapAllocatorArena mHeapAllocator;
+8 -6
View File
@@ -73,6 +73,7 @@ namespace filament {
using namespace backend; using namespace backend;
using namespace filaflat; using namespace filaflat;
using namespace utils; using namespace utils;
using UboBatchingMode = FEngine::UboBatchingMode;
struct Material::BuilderDetails { struct Material::BuilderDetails {
const void* mPayload = nullptr; const void* mPayload = nullptr;
@@ -217,16 +218,17 @@ void FMaterial::terminate(FEngine& engine) {
filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout( filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept { Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
if (mDefinition.materialDomain == MaterialDomain::SURFACE) {
// `variant` is only sensical for MaterialDomain::SURFACE
if (Variant::isValidDepthVariant(variant)) { if (Variant::isValidDepthVariant(variant)) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mEngine.getPerViewDescriptorSetLayoutDepthVariant(); return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
} }
if (Variant::isSSRVariant(variant)) { if (Variant::isSSRVariant(variant)) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mEngine.getPerViewDescriptorSetLayoutSsrVariant(); return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
} }
}
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
if (useVsmDescriptorSetLayout) { if (useVsmDescriptorSetLayout) {
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
return mDefinition.perViewDescriptorSetLayoutVsm; return mDefinition.perViewDescriptorSetLayoutVsm;
} }
return mDefinition.perViewDescriptorSetLayout; return mDefinition.perViewDescriptorSetLayout;
@@ -280,14 +282,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name); return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
} else { } else {
// but if we don't, just create an instance with all the default parameters // but if we don't, just create an instance with all the default parameters
return mEngine.createMaterialInstance(this, name); return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
} }
} }
FMaterialInstance* FMaterial::getDefaultInstance() noexcept { FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) { if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
mDefaultMaterialInstance = mEngine.createMaterialInstance(this, mDefaultMaterialInstance =
mDefinition.name.c_str()); mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
mDefaultMaterialInstance->setDefaultInstance(true); mDefaultMaterialInstance->setDefaultInstance(true);
} }
return mDefaultMaterialInstance; return mDefaultMaterialInstance;
+1 -1
View File
@@ -108,7 +108,7 @@ public:
backend::CallbackHandler* handler, backend::CallbackHandler* handler,
utils::Invocable<void(Material*)>&& callback) noexcept; utils::Invocable<void(Material*)>&& callback) noexcept;
// Create an instance of this material // Creates an instance of this material, specifying the batching mode.
FMaterialInstance* createInstance(const char* name) const noexcept; FMaterialInstance* createInstance(const char* name) const noexcept;
bool hasParameter(const char* name) const noexcept; bool hasParameter(const char* name) const noexcept;
+42 -16
View File
@@ -58,15 +58,19 @@ using namespace utils;
namespace filament { namespace filament {
using namespace backend; using namespace backend;
using UboBatchingMode = FEngine::UboBatchingMode;
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
const char* name) noexcept if (batchingMode != UboBatchingMode::DEFAULT) {
: FMaterialInstance(engine, material, name, return batchingMode == UboBatchingMode::UBO_BATCHING;
engine.features.material.enable_material_instance_uniform_batching) {
} }
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, return engine.isUboBatchingEnabled() && domain == MaterialDomain::SURFACE;
const char* name, bool useUboBatching) noexcept : mMaterial(material), }
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
UboBatchingMode batchingMode) noexcept
: mMaterial(material),
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()), mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK), mCulling(CullingMode::BACK),
mShadowCulling(CullingMode::BACK), mShadowCulling(CullingMode::BACK),
@@ -76,10 +80,12 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
mHasScissor(false), mHasScissor(false),
mIsDoubleSided(false), mIsDoubleSided(false),
mIsDefaultInstance(false), mIsDefaultInstance(false),
mUseUboBatching(useUboBatching), mUseUboBatching(
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
mTransparencyMode(TransparencyMode::DEFAULT), mTransparencyMode(TransparencyMode::DEFAULT),
mName(name ? CString(name) : material->getName()) { mName(name ? CString(name) : material->getName()) {
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
<< "UBO batching is not enabled.";
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
// even if the material doesn't have any parameters, we allocate a small UBO because it's // even if the material doesn't have any parameters, we allocate a small UBO because it's
@@ -153,7 +159,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUseUboBatching(other->mUseUboBatching), mUseUboBatching(other->mUseUboBatching),
mScissorRect(other->mScissorRect), mScissorRect(other->mScissorRect),
mName(name ? CString(name) : other->mName) { mName(name ? CString(name) : other->mName) {
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
FMaterial const* const material = other->getMaterial(); FMaterial const* const material = other->getMaterial();
@@ -193,8 +199,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
} }
} }
FMaterialInstance* FMaterialInstance::duplicate( FMaterialInstance* FMaterialInstance::duplicate(FMaterialInstance const* other,
FMaterialInstance const* other, const char* name) noexcept { const char* name) noexcept {
FMaterial const* const material = other->getMaterial(); FMaterial const* const material = other->getMaterial();
FEngine& engine = material->getEngine(); FEngine& engine = material->getEngine();
return engine.createMaterialInstance(material, other, name); return engine.createMaterialInstance(material, other, name);
@@ -238,15 +244,21 @@ void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driv
void FMaterialInstance::commit(FEngine& engine) const { void FMaterialInstance::commit(FEngine& engine) const {
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) { if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
commit(engine.getDriverApi()); commit(engine.getDriverApi(), engine.getUboManager());
} }
} }
void FMaterialInstance::commit(FEngine::DriverApi& driver) const { void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
if (mUniforms.isDirty() || mHasStreamUniformAssociations) { if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
mUniforms.clean(); mUniforms.clean();
if (mUseUboBatching) { if (mUseUboBatching) {
// TODO: update the content by `copyToMemoryMappedBuffer` assert_invariant(uboManager != nullptr);
if (!BufferAllocator::isValid(getAllocationId())) {
// The allocation hasn't happened yet, return.
return;
}
uboManager->updateSlot(driver, getAllocationId(), mUniforms.toBufferDescriptor(driver));
} }
else { else {
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData); auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
@@ -271,6 +283,10 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver) const {
// TODO: eventually we should remove this in RELEASE builds // TODO: eventually we should remove this in RELEASE builds
fixMissingSamplers(); fixMissingSamplers();
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return;
}
// Commit descriptors if needed (e.g. when textures are updated,or the first time) // Commit descriptors if needed (e.g. when textures are updated,or the first time)
mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver); mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver);
} }
@@ -413,6 +429,13 @@ const char* FMaterialInstance::getName() const noexcept {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const { void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
if (!mDescriptorSet.getHandle()) {
return;
}
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return;
}
if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) { if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) {
std::call_once(mMissingSamplersFlag, [this] { std::call_once(mMissingSamplersFlag, [this] {
@@ -439,7 +462,8 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
return; return;
} }
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL); mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL,
{ { mUboOffset }, driver });
} }
void FMaterialInstance::assignUboAllocation( void FMaterialInstance::assignUboAllocation(
@@ -449,8 +473,10 @@ void FMaterialInstance::assignUboAllocation(
assert_invariant(mUseUboBatching); assert_invariant(mUseUboBatching);
mUboData = id; mUboData = id;
mUboOffset = offset;
if (BufferAllocator::isValid(id)) { if (BufferAllocator::isValid(id)) {
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, offset, // Use dynamic offset during binding, so the offset here is always zero.
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, 0,
mUniforms.getSize()); mUniforms.getSize());
} }
} }
+5 -7
View File
@@ -56,11 +56,8 @@ class FTexture;
class FMaterialInstance : public MaterialInstance { class FMaterialInstance : public MaterialInstance {
public: public:
FMaterialInstance(FEngine& engine, FMaterial const* material, FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
const char* name) noexcept; FEngine::UboBatchingMode batchingMode) noexcept;
// Use this constructor when you need to override the ubo batching flag for an individual MI.
FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name, bool useUboBatching) noexcept;
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name); FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
FMaterialInstance(const FMaterialInstance& rhs) = delete; FMaterialInstance(const FMaterialInstance& rhs) = delete;
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete; FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
@@ -75,7 +72,7 @@ public:
void commit(FEngine& engine) const; void commit(FEngine& engine) const;
void commit(FEngine::DriverApi& driver) const; void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
void use(FEngine::DriverApi& driver, Variant variant = {}) const; void use(FEngine::DriverApi& driver, Variant variant = {}) const;
@@ -287,6 +284,7 @@ private:
}; };
std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData; std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData;
BufferAllocator::allocation_size_t mUboOffset = 0;
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters; tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
mutable DescriptorSet mDescriptorSet; mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms; UniformBuffer mUniforms;
@@ -308,7 +306,7 @@ private:
bool mHasScissor : 1; bool mHasScissor : 1;
bool mIsDoubleSided : 1; bool mIsDoubleSided : 1;
bool mIsDefaultInstance : 1; bool mIsDefaultInstance : 1;
bool mUseUboBatching : 1; const bool mUseUboBatching : 1;
TransparencyMode mTransparencyMode : 2; TransparencyMode mTransparencyMode : 2;
uint64_t mMaterialSortingKey = 0; uint64_t mMaterialSortingKey = 0;
+4
View File
@@ -452,6 +452,8 @@ void FRenderer::endFrame() {
mFrameInfoManager.endFrame(driver); mFrameInfoManager.endFrame(driver);
mFrameSkipper.submitFrame(driver); mFrameSkipper.submitFrame(driver);
engine.submitFrame();
driver.endFrame(mFrameId); driver.endFrame(mFrameId);
// gives the backend a chance to execute periodic tasks // gives the backend a chance to execute periodic tasks
@@ -587,6 +589,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
// happen with Renderer::beginFrame/endFrame. // happen with Renderer::beginFrame/endFrame.
renderInternal(view, true); renderInternal(view, true);
engine.submitFrame();
driver.endFrame(mFrameId); driver.endFrame(mFrameId);
// engine.flush() has already been called by renderInternal(), but we need an extra one // engine.flush() has already been called by renderInternal(), but we need an extra one
+1 -1
View File
@@ -302,7 +302,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
if (!mBufferObjectsEnabled) { if (!mBufferObjectsEnabled) {
// If buffer objects are not enabled at the API level, then we create them internally. // If buffer objects are not enabled at the API level, then we create them internally.
#pragma nounroll #pragma nounroll
for (size_t index = 0; index < MAX_VERTEX_BUFFER_COUNT; ++index) { for (size_t index = 0; index < MAX_VERTEX_ATTRIBUTE_COUNT; ++index) {
size_t const i = mAttributes[index].buffer; size_t const i = mAttributes[index].buffer;
if (i != Attribute::BUFFER_UNUSED) { if (i != Attribute::BUFFER_UNUSED) {
assert_invariant(bufferSizes[i] > 0); assert_invariant(bufferSizes[i] > 0);
+1 -1
View File
@@ -39,7 +39,7 @@ void PostProcessDescriptorSet::init(FEngine& engine) noexcept {
// create the descriptor-set layout // create the descriptor-set layout
mDescriptorSetLayout = filament::DescriptorSetLayout{ mDescriptorSetLayout = filament::DescriptorSetLayout{
engine.getDescriptorSetLayoutFactory(), engine.getDescriptorSetLayoutFactory(),
engine.getDriverApi(), descriptor_sets::getPostProcessLayout() }; engine.getDriverApi(), descriptor_sets::getDepthVariantLayout() };
// create the descriptor-set from the layout // create the descriptor-set from the layout
mDescriptorSet = DescriptorSet{ "PostProcessDescriptorSet", mDescriptorSetLayout }; mDescriptorSet = DescriptorSet{ "PostProcessDescriptorSet", mDescriptorSetLayout };
@@ -28,9 +28,9 @@
namespace filament::descriptor_sets { namespace filament::descriptor_sets {
backend::DescriptorSetLayout const& getPostProcessLayout() noexcept;
backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept; backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept;
backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept; backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept;
backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept; backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayout( backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
+3 -16
View File
@@ -40,12 +40,7 @@ namespace filament::descriptor_sets {
using namespace backend; using namespace backend;
// used for post-processing passes // used to generate shadow-maps, structure and postfx passes
static constexpr std::initializer_list<DescriptorSetLayoutBinding> postProcessDescriptorSetLayoutList = {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
};
// used to generate shadow-maps
static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = { static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS }, { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
}; };
@@ -142,10 +137,6 @@ static const std::unordered_map<
{{ SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_EXTERNAL } {{ SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_EXTERNAL }
}; };
// used for post-processing passes
static DescriptorSetLayout const postProcessDescriptorSetLayout{ utils::StaticString("postProcess"),
postProcessDescriptorSetLayoutList };
// used to generate shadow-maps // used to generate shadow-maps
static DescriptorSetLayout const depthVariantDescriptorSetLayout{ static DescriptorSetLayout const depthVariantDescriptorSetLayout{
utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList
@@ -163,10 +154,6 @@ static DescriptorSetLayout perRenderableDescriptorSetLayout = {
utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList
}; };
DescriptorSetLayout const& getPostProcessLayout() noexcept {
return postProcessDescriptorSetLayout;
}
DescriptorSetLayout const& getDepthVariantLayout() noexcept { DescriptorSetLayout const& getDepthVariantLayout() noexcept {
return depthVariantDescriptorSetLayout; return depthVariantDescriptorSetLayout;
} }
@@ -280,10 +267,10 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
return layout; return layout;
} }
case MaterialDomain::POST_PROCESS: case MaterialDomain::POST_PROCESS:
return postProcessDescriptorSetLayout; return depthVariantDescriptorSetLayout;
case MaterialDomain::COMPUTE: case MaterialDomain::COMPUTE:
// TODO: what's the layout for compute? // TODO: what's the layout for compute?
return postProcessDescriptorSetLayout; return depthVariantDescriptorSetLayout;
} }
} }
+1 -1
View File
@@ -131,7 +131,7 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
layout.bindings.push_back(DescriptorSetLayoutBinding{ DescriptorType::UNIFORM_BUFFER, layout.bindings.push_back(DescriptorSetLayoutBinding{ DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 }); +PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::DYNAMIC_OFFSET, 0 });
for (auto const& sampler: samplers) { for (auto const& sampler: samplers) {
DescriptorSetLayoutBinding layoutBinding{ DescriptorSetLayoutBinding layoutBinding{
@@ -225,7 +225,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER)); f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER));
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)); f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(0); f.writeUint8(0);
f.writeUint8(uint8_t(DescriptorFlags::NONE)); f.writeUint8(uint8_t(DescriptorFlags::DYNAMIC_OFFSET));
f.writeUint16(0); f.writeUint16(0);
// all the material's sampler descriptors // all the material's sampler descriptors
+3 -3
View File
@@ -281,7 +281,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
} }
text << " " text << " "
<< setw(alignment) << fieldName.c_str() << setw(alignment) << fieldName.c_str_safe()
<< setw(shortAlignment) << toString(UniformType(fieldType)) << setw(shortAlignment) << toString(UniformType(fieldType))
<< arraySizeToString(fieldSize) << arraySizeToString(fieldSize)
<< setw(shortAlignment) << toString(Precision(fieldPrecision)) << setw(shortAlignment) << toString(Precision(fieldPrecision))
@@ -325,7 +325,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
} }
text << " " text << " "
<< setw(alignment) << fieldName.c_str() << setw(alignment) << fieldName.c_str_safe()
<< setw(shortAlignment) << +fieldBinding << setw(shortAlignment) << +fieldBinding
<< setw(shortAlignment) << toString(SamplerType(fieldType)) << setw(shortAlignment) << toString(SamplerType(fieldType))
<< setw(shortAlignment) << toString(Precision(fieldPrecision)) << setw(shortAlignment) << toString(Precision(fieldPrecision))
@@ -364,7 +364,7 @@ static bool printConstantInfo(ostream& text, const ChunkContainer& container) {
} }
text << " " text << " "
<< setw(alignment) << fieldName.c_str() << setw(alignment) << fieldName.c_str_safe()
<< setw(shortAlignment) << toString(ConstantType(fieldType)) << setw(shortAlignment) << toString(ConstantType(fieldType))
<< endl; << endl;
} }