Compare commits

..

30 Commits

Author SHA1 Message Date
Andy Hovingh
5ef77cf003 webgpu: debug descriptor sets option/functionality 2025-05-05 22:42:18 -05:00
Konrad Piascik
fcc2819293 Fix windows compile errors 2025-05-05 20:14:37 -05:00
Juan David Caldas
75d6076326 Change order of args for DrawIndexed, makes the triangle spin 2025-05-05 17:22:13 -04:00
Andy Hovingh
b1c6fd5d16 webgpu: legacy draw call should call draw2 2025-05-04 12:04:15 -05:00
Andy Hovingh
a1170924f9 webgpu: sets bind groups in the render pass 2025-05-04 11:48:07 -05:00
Andy Hovingh
1b53a2b680 webgpu: depth texture and attachment 2025-05-03 11:11:59 -05:00
Andy Hovingh
6cb7762751 webgpu: (temporarily) fix invalid texture views (hello-triangle specific) 2025-05-03 09:22:56 -05:00
Andy Hovingh
f87abf32ba webgpu: fix dimension param when creating texture views 2025-05-02 16:38:30 -05:00
Andy Hovingh
f874778a11 webgpu: fix missing color targets in render pipeline 2025-05-02 15:30:05 -05:00
Andy Hovingh
c6c6cae21b webgpu: fix vertex attribute shader location 2025-05-02 15:29:23 -05:00
Andy Hovingh
4cd3d1917b webgpu: do not include constants that are absent in the WGSL 2025-05-02 14:18:12 -05:00
Andy Hovingh
c436fc5260 webgpu: fix dummy bindings for bind groups that don't have resources corresponding to their layouts. 2025-05-02 14:18:12 -05:00
bridgewaterrobbie
a5b5359449 WARNING: Used Gemini to try to fix my bug. Haven't reviewed it but makes related errors go away so it might be right 2025-05-02 14:18:11 -05:00
bridgewaterrobbie
cd4c02252c Set a few more dummy params. DON'T use CreateSampler desc as the layout complains 2025-05-02 14:18:11 -05:00
Syed Idris Shah
836b2a5669 Pass the descriptor to sampler creation api 2025-05-02 14:18:11 -05:00
bridgewaterrobbie
1686e560b3 Throw together dummy descriptor entries. Very messy and at least one major bug 2025-05-02 14:18:11 -05:00
bridgewaterrobbie
e18e452770 Hack: Get the material label (Some of this was debugging until I figured out different issue) 2025-05-02 14:18:11 -05:00
bridgewaterrobbie
50e78636b7 Minor- make layout str outside constructor so it is safe from being destructed before copy 2025-05-02 14:18:11 -05:00
bridgewaterrobbie
6777f9f619 Test adding a label to descriptor sets for WebGPU 2025-05-02 14:18:10 -05:00
bridgewaterrobbie
4a938ec198 NEVER MERGE ME debugging logs to get to the bottom of issues pt1 2025-05-02 14:18:10 -05:00
bridgewaterrobbie
3273023db7 TO REVERT: Hacks to make descriptorset behave itself
Earlier I made mistakes. This hack fixes them but is suboptimal
2025-05-02 14:18:10 -05:00
Juan David Caldas
4afca93c7c Return the string value for the Spec constant 2025-05-02 14:18:10 -05:00
Syed Idris Shah
860a36256c wip: fixing errors 2025-05-02 14:18:10 -05:00
Syed Idris Shah
afd1fef657 Dawn change to enable writeBuffer for size != multiple of 4 2025-05-02 14:18:10 -05:00
Syed Idris Shah
0250d68b4b wip: webgpu: render target impl 2025-05-02 14:18:06 -05:00
bridgewaterrobbie
418a8d75ad Sampler creation as part of the driver 2025-05-02 14:14:11 -05:00
bridgewaterrobbie
8d1caadd7e Add comment explaining public texture format enum conversion 2025-05-02 14:14:10 -05:00
bridgewaterrobbie
3d3aaff83e Add a required feature for Filament to requestDevice 2025-05-02 14:14:10 -05:00
bridgewaterrobbie
cd4aa0e01e Fix mistake- entries size is smaller than the max index, need to fill one at a time with emplace back 2025-05-02 14:14:10 -05:00
bridgewaterrobbie
3c10c20ea1 Texture and Texture View creation 2025-05-02 14:14:10 -05:00
42 changed files with 1044 additions and 1098 deletions

View File

@@ -7,5 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]

View File

@@ -5,18 +5,6 @@ set(TARGET backend)
set(PUBLIC_HDR_DIR include)
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
# ==================================================================================================
# Compilation options
# ==================================================================================================
#
set(BACKEND_SANITIZATION "" CACHE STRING "Sanitization option")
set_property(CACHE BACKEND_SANITIZATION PROPERTY STRINGS ";ASAN")
set(BACKEND_SANITIZERS)
if (BACKEND_SANITIZATION STREQUAL "ASAN")
set(BACKEND_SANITIZERS -fsanitize=address)
endif()
# ==================================================================================================
# Sources and headers
# ==================================================================================================
@@ -484,7 +472,6 @@ target_compile_options(${TARGET} PRIVATE
${OSMESA_COMPILE_FLAGS}
$<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}>
$<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}>
${BACKEND_SANITIZERS}
)
if (FILAMENT_SUPPORTS_METAL)
@@ -495,8 +482,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
endif()
target_link_options(${TARGET} PRIVATE ${BACKEND_SANITIZERS})
target_link_libraries(${TARGET} PRIVATE
${OSMESA_LINKER_FLAGS}
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
@@ -566,8 +551,6 @@ if (APPLE AND NOT IOS)
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
target_compile_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
target_link_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
set(BACKEND_TEST_DEPS
OSDependent
@@ -606,7 +589,6 @@ if (APPLE AND NOT IOS)
# linker from removing "unused" symbols.
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
target_link_options(backend_test_mac PRIVATE ${BACKEND_SANITIZERS})
# This is needed after XCode 15.3
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
@@ -616,8 +598,6 @@ endif()
if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_compile_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
target_link_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
endif()

View File

@@ -30,11 +30,13 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/StaticString.h>
#include <math/vec4.h>
#include <array>
#if !defined(NDEBUG)
#include <string>
#endif
#include <type_traits>
#include <variant>
@@ -1140,7 +1142,9 @@ struct ExternalSamplerDatum {
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
std::variant<utils::StaticString, utils::CString, std::monostate> label;
#if !defined(NDEBUG)
std::string label;
#endif
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed

View File

@@ -44,8 +44,8 @@ public:
struct Descriptor {
utils::CString name;
DescriptorType type;
descriptor_binding_t binding;
backend::DescriptorType type;
backend::descriptor_binding_t binding;
};
struct SpecializationConstant {

View File

@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
Profiler profiler;
if (SYSTRACE_TAG) {
if constexpr (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
@@ -100,7 +100,7 @@ void CommandStream::execute(void* buffer) {
}
});
if (SYSTRACE_TAG) {
if constexpr (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled
profiler.stop();

View File

@@ -185,7 +185,6 @@ inline MTLPixelFormat getMetalFormat(PixelDataFormat format, PixelDataType type)
CONVERT(RGBA_INTEGER, UINT, RGBA32Uint);
CONVERT(RGBA_INTEGER, INT, RGBA32Sint);
CONVERT(RGBA, FLOAT, RGBA32Float);
CONVERT(DEPTH_COMPONENT, FLOAT, Depth32Float);
#undef CONVERT
return MTLPixelFormatInvalid;

View File

@@ -152,12 +152,12 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL()
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL()
#else
#define FVK_SYSTRACE_CONTEXT()

View File

@@ -198,6 +198,8 @@ WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
ShaderStage::FRAGMENT)),
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::COMPUTE)),
constants(convertConstants(program.getSpecializationConstants())) {}
constants(convertConstants(program.getSpecializationConstants())) {
auto test = program.getShadersSource();
}
}// namespace filament::backend

View File

@@ -34,6 +34,8 @@
// order of calls).
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
#define FWGPU_DEBUG_DESCRIPTOR_SETS 0x00000008
// Useful default combinations
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <utils/Hash.h>
#include "webgpu/WebGPUDriver.h"
#include "WebGPUPipelineCreation.h"
@@ -470,9 +470,8 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
assert_invariant(mSwapChain);
WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(mDevice,
mSwapChain->getColorFormat());
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
FWGPU_LOGW << "WebGPU support is highly limited, only supporting the hello-triangle sample in "
"a brittle/hacked way and untested, likely, breaking in other samples."
<< utils::io::endl;
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
@@ -591,7 +590,14 @@ void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
auto layout = handleCast<WebGPUDescriptorSetLayout>(dslh);
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getBindGroupEntries());
constructHandle<WebGPUDescriptorSet>(
dsh,
#ifndef NDEBUG
wgpu::StringView(layout->getLabel()),
#endif
layout->getLayout(),
layout->getBindGroupEntries()
);
}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
@@ -916,14 +922,6 @@ void WebGPUDriver::blit(
}
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
// TODO Investigate implications of this hash more closely. Vulkan has a whole class
// VulkanPipelineCache to handle this, may be missing nuance
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
auto hash = pipleineStateHasher(pipelineState);
if(mPipelineMap.find(hash) != mPipelineMap.end()){
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
return;
}
const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr &&
@@ -960,7 +958,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat());
mPipelineMap[hash] = pipeline;
// TODO: uncomment once we have a valid pipeline to set
mRenderPassEncoder.SetPipeline(pipeline);
}
@@ -982,12 +980,12 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
// Calling DrawIndexed with "firstInstance = 0" results in a NON spinning triangle
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
// Calling DrawIndexed with "firstInstance = 1" results in a spinning triangle
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 1);
// Calling Draw with "firstInstance = 0" results in a NON spinning triangle
// Calling Draw with "firstInstance = 1" results in a spinning triangle
// mRenderPassEncoder.Draw(indexCount, instanceCount, 0, 1);
// mRenderPassEncoder.Draw(indexCount, instanceCount, 0, 1);
}
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,

View File

@@ -93,8 +93,6 @@ private:
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
/*
* Driver interface
*/

View File

@@ -16,14 +16,28 @@
#include "WebGPUHandles.h"
#include "WebGPUConstants.h"
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
#include <utils/ostream.h>
#endif
#include <webgpu/webgpu_cpp.h>
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
#include <webgpu/webgpu_cpp_print.h>
#endif
#include <algorithm>
#include <cstdint>
#ifndef NDEBUG
#include <string>
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
#include <string_view>
#endif
#include <utility>
#include <vector>
@@ -157,6 +171,226 @@ wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target)
}
}
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(filament::backend::DescriptorType type) {
using filament::backend::DescriptorType;
switch (type) {
case DescriptorType::UNIFORM_BUFFER:
return "UNIFORM_BUFFER";
case DescriptorType::SHADER_STORAGE_BUFFER:
return "SHADER_STORAGE_BUFFER";
case DescriptorType::SAMPLER:
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(filament::backend::ShaderStageFlags flags) {
using filament::backend::ShaderStageFlags;
switch (flags) {
case ShaderStageFlags::NONE:
return "NONE";
case ShaderStageFlags::VERTEX:
return "VERTEX";
case ShaderStageFlags::FRAGMENT:
return "FRAGMENT";
case ShaderStageFlags::COMPUTE:
return "COMPUTE";
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS:
return "ALL_SHADER_STAGE_FLAGS";
}
if (any(flags & ShaderStageFlags::VERTEX)) {
if (any(flags & ShaderStageFlags::FRAGMENT)) {
return "VERTEX | FRAGMENT";
}
if (any(flags & ShaderStageFlags::COMPUTE)) {
return "VERTEX | COMPUTE";
}
}
if (any(flags & ShaderStageFlags::FRAGMENT)) {
if (any(flags & ShaderStageFlags::COMPUTE)) {
return "FRAGMENT | COMPUTE";
}
}
PANIC_POSTCONDITION("IMPOSSIBLE ShaderStageFlags combination. flags: %d", flags);
}
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(wgpu::ShaderStage stages) {
switch (stages) {
case wgpu::ShaderStage::None:
return "None";
case wgpu::ShaderStage::Vertex:
return "Vertex";
case wgpu::ShaderStage::Fragment:
return "Fragment";
case wgpu::ShaderStage::Compute:
return "Compute";
}
wgpu::ShaderStage allStages =
wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute;
if ((stages & allStages) == allStages) {
return "Vertex | Fragment | Compute";
}
if ((stages & wgpu::ShaderStage::Vertex) != 0) {
if ((stages & wgpu::ShaderStage::Fragment) != 0) {
return "Vertex | Fragment";
}
if ((stages & wgpu::ShaderStage::Compute) != 0) {
return "Vertex | Compute";
}
}
if ((stages & wgpu::ShaderStage::Fragment) != 0) {
if ((stages & wgpu::ShaderStage::Compute) != 0) {
return "Fragment | Compute";
}
}
}
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
std::string_view toString(filament::backend::DescriptorFlags flags) {
using filament::backend::DescriptorFlags;
switch (flags) {
case DescriptorFlags::NONE:
return "NONE";
case DescriptorFlags::DYNAMIC_OFFSET:
return "DYNAMIC_OFFSET";
}
}
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
void printFilamentDescriptorSetLayout(filament::backend::DescriptorSetLayout const& layout) {
using filament::backend::DescriptorSetLayoutBinding;
FWGPU_LOGD << "filament::backend::DescriptorSetLayout \"" << layout.label
<< "\":" << utils::io::endl;
FWGPU_LOGD << " bindings (" << layout.bindings.size() << "):" << utils::io::endl;
for (DescriptorSetLayoutBinding const& binding: layout.bindings) {
FWGPU_LOGD << " binding " << static_cast<uint32_t>(binding.binding) << ":"
<< " type " << toString(binding.type) << " stageFlags "
<< toString(binding.stageFlags) << " flags " << toString(binding.flags)
<< " count " << binding.count << utils::io::endl;
}
}
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
void printBindGroupLayout(wgpu::BindGroupLayoutDescriptor const& layout) {
FWGPU_LOGD << "wgpu::BindGroupLayoutDescriptor label \"" << layout.label
<< "\":" << utils::io::endl;
FWGPU_LOGD << " entries (" << layout.entryCount << "):" << utils::io::endl;
for (size_t entryIndex = 0; entryIndex < layout.entryCount; entryIndex++) {
wgpu::BindGroupLayoutEntry const& entry = layout.entries[entryIndex];
FWGPU_LOGD << " binding " << entry.binding << " visibility "
<< toString(entry.visibility);
if (entry.buffer.type != wgpu::BufferBindingType::BindingNotUsed &&
entry.buffer.type != wgpu::BufferBindingType::Undefined) {
// buffer entry...
assert_invariant(
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
"entry is for a buffer but one of the other layout types (sampler, texture, "
"storage) are somehow also being used?");
std::stringstream typeStream;
typeStream << entry.buffer.type;
FWGPU_LOGD << " " << typeStream.str() << " hasDynamicOffset "
<< bool(entry.buffer.hasDynamicOffset) << " minBindingSize "
<< entry.buffer.minBindingSize;
}
if (entry.sampler.type != wgpu::SamplerBindingType::BindingNotUsed &&
entry.sampler.type != wgpu::SamplerBindingType::Undefined) {
// sampler entry...
assert_invariant(
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
"entry is for a sampler but one of the other layout types (buffer, texture, "
"storage) are somehow also being used?");
std::stringstream typeStream;
typeStream << entry.sampler.type;
FWGPU_LOGD << " " << typeStream.str();
}
if (entry.texture.sampleType != wgpu::TextureSampleType::BindingNotUsed &&
entry.texture.sampleType != wgpu::TextureSampleType::Undefined) {
// texture entry...
assert_invariant(
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
entry.storageTexture.access == wgpu::StorageTextureAccess::BindingNotUsed &&
"entry is for a texture but one of the other layout types (buffer, sampler, "
"storage) are somehow also being used?");
std::stringstream typeStream;
typeStream << entry.texture.sampleType;
std::stringstream dimensionStream;
dimensionStream << entry.texture.viewDimension;
FWGPU_LOGD << " " << typeStream.str() << " " << dimensionStream.str()
<< " multisampled " << bool(entry.texture.multisampled);
}
if (entry.storageTexture.access != wgpu::StorageTextureAccess::BindingNotUsed &&
entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
// storageTexture entry...
assert_invariant(
entry.buffer.type == wgpu::BufferBindingType::BindingNotUsed &&
entry.sampler.type == wgpu::SamplerBindingType::BindingNotUsed &&
entry.texture.sampleType == wgpu::TextureSampleType::BindingNotUsed &&
"entry is for a storage texture but one of the other layout types (buffer, "
"sampler, texture) are somehow also being used?");
std::stringstream accessStream;
accessStream << entry.storageTexture.access;
std::stringstream formatStream;
formatStream << entry.storageTexture.format;
std::stringstream dimensionStream;
dimensionStream << entry.storageTexture.viewDimension;
FWGPU_LOGD << " " << accessStream.str() << " " << formatStream.str() << " "
<< dimensionStream.str();
}
FWGPU_LOGD << utils::io::endl;
}
}
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
template <size_t ENTRY_SIZE>
void printBindGroup(wgpu::BindGroupDescriptor const& bindGroup,
std::bitset<ENTRY_SIZE> const& entriesByBindingWithDynamicOffsets,
std::bitset<ENTRY_SIZE> const& entriesByBindingAdded) {
assert_invariant(bindGroup.layout);
FWGPU_LOGD << "wgpu::BindGroupDescriptor label \"" << bindGroup.label
<< "\":" << utils::io::endl;
FWGPU_LOGD << " entries (" << bindGroup.entryCount << "):" << utils::io::endl;
for (size_t entryIndex = 0; entryIndex < bindGroup.entryCount; entryIndex++) {
wgpu::BindGroupEntry const& entry = bindGroup.entries[entryIndex];
FWGPU_LOGD << " binding " << entry.binding;
if (entry.buffer) {
assert_invariant(entry.sampler == nullptr && entry.textureView == nullptr &&
"buffer set but also sampler and/or textureView?");
FWGPU_LOGD << " buffer";
}
if (entry.sampler) {
assert_invariant(entry.buffer == nullptr && entry.textureView == nullptr &&
"sampler set but also buffer and/or textureView?");
FWGPU_LOGD << " sampler";
}
if (entry.textureView) {
assert_invariant(entry.buffer == nullptr && entry.sampler == nullptr &&
"textureView set but also buffer and/or sampler?");
FWGPU_LOGD << " textureView";
}
FWGPU_LOGD << " offset " << entry.offset << " size " << entry.size << " hasDynamicOffset "
<< bool(entriesByBindingWithDynamicOffsets[entry.binding]) << " added "
<< bool(entriesByBindingAdded[entry.binding]) << utils::io::endl;
}
}
#endif
}// namespace
namespace filament::backend {
@@ -236,20 +470,16 @@ wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStag
}
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
wgpu::Device const& device) {
wgpu::Device const& device)
#ifndef NDEBUG
: mLabel(layout.label)
#endif
{
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
printFilamentDescriptorSetLayout(layout);
#endif
assert_invariant(device);
std::string baseLabel;
if (std::holds_alternative<utils::StaticString>(layout.label)) {
const auto& temp = std::get_if<utils::StaticString>(&layout.label);
baseLabel = temp->c_str();
} else if (std::holds_alternative<utils::CString>(layout.label)) {
const auto& temp = std::get_if<utils::CString>(&layout.label);
baseLabel = temp->c_str();
}
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value.
static int layoutNum = 0;
unsigned int samplerCount =
@@ -283,13 +513,18 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering; // Example default
wEntry.texture.sampleType = wgpu::TextureSampleType::Float; // Example default
// TODO: FIX! THIS IS HACK FOR HELLO-TRIANGLE!
if (baseLabel.find("Skybox") != std::string::npos ||
(baseLabel == "Filament Default Material_perView" && wEntry.binding == 22)) {
#ifndef NDEBUG
if (layout.label.find("Skybox") != std::string::npos ||
(layout.label == "Filament Default Material1" && wEntry.binding == 22)) {
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
} else {
wEntry.texture.viewDimension =
wgpu::TextureViewDimension::e2D;// Example default
}
#else
// TODO: AGAIN, fix the above because hello-triangle will break on release builds
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2D;// Example default
#endif
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW;
break;
}
@@ -313,12 +548,17 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
}
// fEntry.count is unused currently
}
std::string label = "layout_" + baseLabel + std::to_string(++layoutNum) ;
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
.label{label.c_str()}, // Use .c_str() if label needs to be const char*
.entryCount = wEntries.size(),
.entries = wEntries.data()
};
#ifndef NDEBUG
std::string label = "layout_" + layout.label + std::to_string(++layoutNum);
layoutDescriptor.label = label.c_str();// Use .c_str() if label needs to be const char*
#endif
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
printBindGroupLayout(layoutDescriptor);
#endif
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
}
@@ -411,9 +651,17 @@ std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedB
return entries;
}
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
WebGPUDescriptorSet::WebGPUDescriptorSet(
#ifndef NDEBUG
wgpu::StringView const& label,
#endif
wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
: mLayout(layout),
:
#ifndef NDEBUG
mLabel(label),
#endif
mLayout(layout),
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) {
// Establish the size of entries based on the layout. This should be reliable and efficient.
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
@@ -436,23 +684,26 @@ WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
mBindGroup = nullptr;
mLayout = nullptr;
}
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
if (mBindGroup) {
return mBindGroup;
}
// TODO label? Should we just copy layout label?
wgpu::BindGroupDescriptor desc{
#ifndef NDEBUG
.label = mLabel,
#endif
.layout = mLayout,
.entryCount = mEntriesSortedByBinding.size(),
.entries = mEntriesSortedByBinding.data()
};
#if FWGPU_ENABLED(FWGPU_DEBUG_DESCRIPTOR_SETS)
printBindGroup(desc, mEntriesByBindingWithDynamicOffsets, mEntriesByBindingAdded);
#endif
mBindGroup = device.CreateBindGroup(&desc);
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
// once we have created the bind group itself we should no longer need any other state
mLayout = nullptr;
mEntriesSortedByBinding.clear();
mEntriesSortedByBinding.shrink_to_fit();
return mBindGroup;

View File

@@ -14,7 +14,6 @@
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
@@ -30,6 +29,10 @@
#include <array>
#include <bitset>
#include <cstdint>
#ifndef NDEBUG
#include <string>
#include <string_view>
#endif
#include <vector>
namespace filament::backend {
@@ -118,6 +121,9 @@ public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
~WebGPUDescriptorSetLayout();
#ifndef NDEBUG
[[nodiscard]] std::string const& getLabel() const { return mLabel; }
#endif
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
[[nodiscard]] std::vector<BindGroupEntryInfo> const& getBindGroupEntries() const {
return mBindGroupEntries;
@@ -127,6 +133,9 @@ private:
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
#ifndef NDEBUG
std::string const mLabel;
#endif
std::vector<BindGroupEntryInfo> mBindGroupEntries;
wgpu::BindGroupLayout mLayout;
};
@@ -136,7 +145,11 @@ public:
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
wgpu::TextureFormat aColorFormat);
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
WebGPUDescriptorSet(
#ifndef NDEBUG
wgpu::StringView const& label,
#endif
wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
~WebGPUDescriptorSet();
@@ -154,12 +167,14 @@ private:
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);
#ifndef NDEBUG
const wgpu::StringView mLabel;
#endif
// TODO: Consider storing what we used to make the layout. However we need to essentially
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
// there isn't a mismatch may be easiest.
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
wgpu::BindGroupLayout mLayout = nullptr;
wgpu::BindGroupLayout const& mLayout;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;

View File

@@ -99,45 +99,21 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
// TODO consider passing limits
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
constexpr std::array desiredFeatures = { wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits,
wgpu::FeatureName::TransientAttachments };
std::vector<wgpu::FeatureName> requiredFeatures;
requiredFeatures.reserve(desiredFeatures.size());
wgpu::SupportedFeatures supportedFeatures;
adapter.GetFeatures(&supportedFeatures);
std::vector<wgpu::FeatureName> enabledFeatures;
enabledFeatures.reserve(requiredFeatures.size() + optionalFeatures.size());
std::set_intersection(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount, requiredFeatures.begin(),
requiredFeatures.end(), std::back_inserter(enabledFeatures));
if (enabledFeatures.size() != requiredFeatures.size()) {
std::vector<wgpu::FeatureName> missingFeatures;
std::set_difference(requiredFeatures.begin(), requiredFeatures.end(),
supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
std::back_inserter(missingFeatures));
std::stringstream missingFeaturesStream{};
for (const auto& entry: missingFeatures) {
missingFeaturesStream << std::to_string(static_cast<uint32_t>(entry)) << " ";
}
PANIC_POSTCONDITION("Some required features are not available %s/n",
missingFeaturesStream.str().c_str());
}
std::set_intersection(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount, optionalFeatures.begin(),
optionalFeatures.end(), std::back_inserter(enabledFeatures));
supportedFeatures.features + supportedFeatures.featureCount, desiredFeatures.begin(),
desiredFeatures.end(), std::back_inserter(requiredFeatures));
wgpu::DeviceDescriptor deviceDescriptor{};
deviceDescriptor.label = "graphics_device";
deviceDescriptor.defaultQueue.label = "default_queue";
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
deviceDescriptor.requiredFeatures = enabledFeatures.data();
deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
deviceDescriptor.requiredFeatures = requiredFeatures.data();
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
wgpu::StringView message) {

View File

@@ -57,7 +57,7 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
if (!kLayouts.empty()) {
mDescriptorSetLayout =
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ .bindings = kLayouts }));
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
}
}

View File

@@ -648,32 +648,44 @@ bool ChunkAttributeInfo::unflatten(Unflattener& unflattener,
bool ChunkDescriptorBindingsInfo::unflatten(Unflattener& unflattener,
MaterialParser::DescriptorBindingsContainer* container) {
static_assert(sizeof(DescriptorSetBindingPoints) == sizeof(uint8_t));
uint8_t descriptorCount;
if (!unflattener.read(&descriptorCount)) {
uint8_t setCount;
if (!unflattener.read(&setCount)) {
return false;
}
auto& descriptors = (*container)[+DescriptorSetBindingPoints::PER_MATERIAL];
descriptors.reserve(descriptorCount);
for (size_t i = 0; i < descriptorCount; i++) {
CString name;
if (!unflattener.read(&name)) {
for (size_t j = 0; j < setCount; j++) {
static_assert(sizeof(DescriptorSetBindingPoints) == sizeof(uint8_t));
DescriptorSetBindingPoints set;
if (!unflattener.read(reinterpret_cast<uint8_t*>(&set))) {
return false;
}
uint8_t type;
if (!unflattener.read(&type)) {
uint8_t descriptorCount;
if (!unflattener.read(&descriptorCount)) {
return false;
}
uint8_t binding;
if (!unflattener.read(&binding)) {
return false;
auto& descriptors = (*container)[+set];
descriptors.reserve(descriptorCount);
for (size_t i = 0; i < descriptorCount; i++) {
CString name;
if (!unflattener.read(&name)) {
return false;
}
uint8_t type;
if (!unflattener.read(&type)) {
return false;
}
uint8_t binding;
if (!unflattener.read(&binding)) {
return false;
}
descriptors.push_back({
std::move(name),
DescriptorType(type),
descriptor_binding_t(binding)});
}
descriptors.push_back({
std::move(name),
DescriptorType(type),
descriptor_binding_t(binding)});
}
return true;
@@ -681,40 +693,42 @@ bool ChunkDescriptorBindingsInfo::unflatten(Unflattener& unflattener,
bool ChunkDescriptorSetLayoutInfo::unflatten(Unflattener& unflattener,
MaterialParser::DescriptorSetLayoutContainer* container) {
uint8_t descriptorCount;
if (!unflattener.read(&descriptorCount)) {
return false;
}
auto& descriptors = container->bindings;
descriptors.reserve(descriptorCount);
for (size_t i = 0; i < descriptorCount; i++) {
uint8_t type;
if (!unflattener.read(&type)) {
for (size_t j = 0; j < 2; j++) {
uint8_t descriptorCount;
if (!unflattener.read(&descriptorCount)) {
return false;
}
uint8_t stageFlags;
if (!unflattener.read(&stageFlags)) {
return false;
auto& descriptors = (*container)[j].bindings;
descriptors.reserve(descriptorCount);
for (size_t i = 0; i < descriptorCount; i++) {
uint8_t type;
if (!unflattener.read(&type)) {
return false;
}
uint8_t stageFlags;
if (!unflattener.read(&stageFlags)) {
return false;
}
uint8_t binding;
if (!unflattener.read(&binding)) {
return false;
}
uint8_t flags;
if (!unflattener.read(&flags)) {
return false;
}
uint16_t count;
if (!unflattener.read(&count)) {
return false;
}
descriptors.push_back({
DescriptorType(type),
ShaderStageFlags(stageFlags),
descriptor_binding_t(binding),
DescriptorFlags(flags),
count,
});
}
uint8_t binding;
if (!unflattener.read(&binding)) {
return false;
}
uint8_t flags;
if (!unflattener.read(&flags)) {
return false;
}
uint16_t count;
if (!unflattener.read(&count)) {
return false;
}
descriptors.push_back({
DescriptorType(type),
ShaderStageFlags(stageFlags),
descriptor_binding_t(binding),
DescriptorFlags(flags),
count,
});
}
return true;
}

View File

@@ -93,7 +93,7 @@ public:
using DescriptorBindingsContainer = backend::Program::DescriptorSetInfo;
bool getDescriptorBindings(DescriptorBindingsContainer* container) const noexcept;
using DescriptorSetLayoutContainer = backend::DescriptorSetLayout;
using DescriptorSetLayoutContainer = std::array<backend::DescriptorSetLayout, 2>;
bool getDescriptorSetLayout(DescriptorSetLayoutContainer* container) const noexcept;
bool getDepthWriteSet(bool* value) const noexcept;

View File

@@ -609,12 +609,9 @@ Program FMaterial::getProgramWithVariants(
program.attributes(mAttributeInfo);
}
program.descriptorBindings(+DescriptorSetBindingPoints::PER_VIEW,
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_VIEW]);
program.descriptorBindings(+DescriptorSetBindingPoints::PER_RENDERABLE,
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
program.descriptorBindings(0, mProgramDescriptorBindings[0]);
program.descriptorBindings(1, mProgramDescriptorBindings[1]);
program.descriptorBindings(2, mProgramDescriptorBindings[2]);
program.specializationConstants(mSpecializationConstants);
program.pushConstants(ShaderStage::VERTEX, mPushConstants[uint8_t(ShaderStage::VERTEX)]);
@@ -1142,46 +1139,29 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* const parser) {
UTILS_UNUSED_IN_RELEASE bool success;
utils::CString namestr;
parser->getName(&namestr);
success = parser->getDescriptorBindings(&mProgramDescriptorBindings);
assert_invariant(success);
backend::DescriptorSetLayout descriptorSetLayout;
std::array<backend::DescriptorSetLayout, 2> descriptorSetLayout;
success = parser->getDescriptorSetLayout(&descriptorSetLayout);
assert_invariant(success);
auto perMatLabel = mName;
perMatLabel.append("_perMat");
descriptorSetLayout.label = std::move(perMatLabel);
// get the PER_VIEW descriptor binding info
auto perViewDescriptorSetLayout =
descriptor_sets::getPerViewDescriptorSetLayout(mMaterialDomain, mVariantFilterMask,
mIsVariantLit || mHasShadowMultiplier, mReflectionMode, mRefractionMode);
auto perViewLabel = mName;
perViewLabel.append("_perView");
perViewDescriptorSetLayout.label = std::move(perViewLabel);
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
for (auto&& [bindingPoint, descriptorSetLayout] : {
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
descriptor_sets::getPerRenderableLayout() },
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
perViewDescriptorSetLayout }}) {
Program::DescriptorBindingsInfo& descriptors = mProgramDescriptorBindings[+bindingPoint];
descriptors.reserve(descriptorSetLayout.bindings.size());
for (auto const& entry: descriptorSetLayout.bindings) {
auto const& name = descriptor_sets::getDescriptorName(bindingPoint, entry.binding);
descriptors.push_back({ name, entry.type, entry.binding });
}
}
descriptorSetLayout[0].label = namestr.c_str();
descriptorSetLayout[1].label = namestr.c_str();
descriptorSetLayout[0].label+="0\0";
descriptorSetLayout[1].label+="1\0";
mDescriptorSetLayout = {
engine.getDescriptorSetLayoutFactory(),
engine.getDriverApi(), std::move(descriptorSetLayout) };
engine.getDriverApi(), std::move(descriptorSetLayout[0]) };
mPerViewDescriptorSetLayout = {
engine.getDescriptorSetLayoutFactory(),
engine.getDriverApi(), perViewDescriptorSetLayout };
engine.getDriverApi(), std::move(descriptorSetLayout[1]) };
}
descriptor_binding_t FMaterial::getSamplerBinding(

View File

@@ -20,7 +20,6 @@
#include <backend/DriverEnums.h>
#include <private/filament/EngineEnums.h>
#include <private/filament/Variant.h>
#include <filament/MaterialEnums.h>
@@ -40,16 +39,8 @@ backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
ReflectionMode reflectionMode,
RefractionMode refractionMode) noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
Variant variant,
MaterialDomain domain,
UserVariantFilterMask variantFilter,
bool isLit,
ReflectionMode reflectionMode,
RefractionMode refractionMode) noexcept;
utils::CString getDescriptorName(
DescriptorSetBindingPoints set,
filament::DescriptorSetBindingPoints set,
backend::descriptor_binding_t binding) noexcept;
} // namespace filament::descriptor_sets

View File

@@ -23,8 +23,6 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <private/filament/DescriptorSets.h>
#include <initializer_list>
#include <unordered_map>
#include <string_view>
@@ -130,9 +128,6 @@ public:
static utils::CString generateUniformName(const char* group, const char* sampler) noexcept;
static SamplerInfoList filterSamplerList(SamplerInfoList list,
backend::DescriptorSetLayout const& descriptorSetLayout);
private:
friend class Builder;

View File

@@ -17,7 +17,6 @@
#include "private/filament/DescriptorSets.h"
#include <private/filament/EngineEnums.h>
#include <private/filament/Variant.h>
#include <filament/MaterialEnums.h>
@@ -27,35 +26,34 @@
#include <utils/debug.h>
#include <algorithm>
#include <initializer_list>
#include <string_view>
#include <unordered_map>
#include <string_view>
namespace filament::descriptor_sets {
using namespace backend;
static constexpr std::initializer_list<DescriptorSetLayoutBinding> postProcessDescriptorSetLayoutList = {
static DescriptorSetLayout const postProcessDescriptorSetLayout{"postProcess", {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
};
}};
static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = {
static DescriptorSetLayout const depthVariantDescriptorSetLayout{"depthVariant",{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
};
}};
// ssrVariantDescriptorSetLayout must match perViewDescriptorSetLayout's vertex stage. This is
// because the SSR variant is always using the "standard" vertex shader (i.e. there is no
// dedicated SSR vertex shader), which uses perViewDescriptorSetLayout.
// This means that PerViewBindingPoints::SHADOWS must be in the layout even though it's not used
// by the SSR variant.
static constexpr std::initializer_list<DescriptorSetLayoutBinding> ssrVariantDescriptorSetLayoutList = {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
};
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{"ssrVariant", {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
}};
static constexpr std::initializer_list<DescriptorSetLayoutBinding> perViewDescriptorSetLayoutList = {
static DescriptorSetLayout perViewDescriptorSetLayout = {"perView", {
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
@@ -68,37 +66,16 @@ static constexpr std::initializer_list<DescriptorSetLayoutBinding> perViewDescri
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSAO },
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
};
}};
static constexpr std::initializer_list<DescriptorSetLayoutBinding> perRenderableDescriptorSetLayoutList = {
static DescriptorSetLayout perRenderableDescriptorSetLayout = {"preRenderable",{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
{ DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS },
{ DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS },
{ DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS },
};
// used for post-processing passes
static DescriptorSetLayout const postProcessDescriptorSetLayout{ utils::StaticString("postProcess"),
postProcessDescriptorSetLayoutList };
// used to generate shadow-maps
static DescriptorSetLayout const depthVariantDescriptorSetLayout{
utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList
};
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{ utils::StaticString("ssrVariant"),
ssrVariantDescriptorSetLayoutList };
// Used for generating the color pass (i.e. the main pass). This is in fact a template that gets
// declined into 8 different layouts, based on variants.
static DescriptorSetLayout perViewDescriptorSetLayout = { utils::StaticString("perView"),
perViewDescriptorSetLayoutList };
static DescriptorSetLayout perRenderableDescriptorSetLayout = {
utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList
};
}};
DescriptorSetLayout const& getPostProcessLayout() noexcept {
return postProcessDescriptorSetLayout;
@@ -116,8 +93,8 @@ DescriptorSetLayout const& getPerRenderableLayout() noexcept {
return perRenderableDescriptorSetLayout;
}
utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
descriptor_binding_t const binding) noexcept {
utils::CString getDescriptorName(DescriptorSetBindingPoints set,
descriptor_binding_t binding) noexcept {
using namespace std::literals;
static std::unordered_map<descriptor_binding_t, std::string_view> const set0{{
@@ -163,11 +140,11 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
}
DescriptorSetLayout getPerViewDescriptorSetLayout(
MaterialDomain const domain,
UserVariantFilterMask const variantFilter,
bool const isLit,
ReflectionMode const reflectionMode,
RefractionMode const refractionMode) noexcept {
MaterialDomain domain,
UserVariantFilterMask variantFilter,
bool isLit,
ReflectionMode reflectionMode,
RefractionMode refractionMode) noexcept {
bool const ssr = reflectionMode == ReflectionMode::SCREEN_SPACE ||
refractionMode == RefractionMode::SCREEN_SPACE;
@@ -210,65 +187,11 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
return layout;
}
case MaterialDomain::POST_PROCESS:
return postProcessDescriptorSetLayout;
return descriptor_sets::getPostProcessLayout();
case MaterialDomain::COMPUTE:
// TODO: what's the layout for compute?
return postProcessDescriptorSetLayout;
return descriptor_sets::getPostProcessLayout();
}
}
DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
Variant const variant,
MaterialDomain domain,
UserVariantFilterMask const variantFilter,
bool const isLit,
ReflectionMode const reflectionMode,
RefractionMode const refractionMode) noexcept {
if (Variant::isValidDepthVariant(variant)) {
return depthVariantDescriptorSetLayout;
}
if (Variant::isSSRVariant(variant)) {
return ssrVariantDescriptorSetLayout;
}
// We need to filter out all the descriptors not included in the "resolved" layout below
return getPerViewDescriptorSetLayout(domain, variantFilter,
isLit, reflectionMode, refractionMode);
}
template<class ITERATOR, class PREDICATE>
constexpr static ITERATOR find_if(ITERATOR first, ITERATOR last, PREDICATE pred) {
for (; first != last; ++first)
if (pred(*first)) break;
return first;
}
constexpr static bool checkConsistency() noexcept {
// check that all descriptors that apply to the vertex stage in perViewDescriptorSetLayout
// are present in ssrVariantDescriptorSetLayout; meaning that the latter is compatible
// with the former.
for (auto const& r: perViewDescriptorSetLayoutList) {
if (hasShaderType(r.stageFlags, ShaderStage::VERTEX)) {
auto const pos = find_if(
ssrVariantDescriptorSetLayoutList.begin(),
ssrVariantDescriptorSetLayoutList.end(),
[r](auto const& l) {
return l.count == r.count &&
l.type == r.type &&
l.binding == r.binding &&
l.flags == r.flags &&
l.stageFlags == r.stageFlags;
});
if (pos == ssrVariantDescriptorSetLayoutList.end()) {
return false;
}
}
}
return true;
}
static_assert(checkConsistency(), "ssrVariantDescriptorSetLayout is not compatible with "
"perViewDescriptorSetLayout");
} // namespace filament::descriptor_sets

View File

@@ -16,7 +16,6 @@
#include "private/filament/SamplerInterfaceBlock.h"
#include <private/filament/DescriptorSets.h>
#include <backend/DriverEnums.h>
@@ -103,7 +102,7 @@ const SamplerInterfaceBlock::SamplerInfo* SamplerInterfaceBlock::getSamplerInfo(
return &mSamplersInfoList[pos->second];
}
CString SamplerInterfaceBlock::generateUniformName(const char* group, const char* sampler) noexcept {
utils::CString SamplerInterfaceBlock::generateUniformName(const char* group, const char* sampler) noexcept {
char uniformName[256];
// sampler interface block name
@@ -118,27 +117,9 @@ CString SamplerInterfaceBlock::generateUniformName(const char* group, const char
std::min(sizeof(uniformName) / 2 - 2, strlen(sampler)),
prefix + 1);
*last++ = 0; // null terminator
assert_invariant(last <= std::end(uniformName));
assert(last <= std::end(uniformName));
return CString{ uniformName, size_t(last - uniformName) - 1u };
}
SamplerInterfaceBlock::SamplerInfoList SamplerInterfaceBlock::filterSamplerList(
SamplerInfoList list, backend::DescriptorSetLayout const& descriptorSetLayout) {
// remove all the samplers that are not included in the descriptor-set layout
list.erase(
std::remove_if(list.begin(), list.end(),
[&](auto const& entry) {
auto pos = std::find_if(
descriptorSetLayout.bindings.begin(),
descriptorSetLayout.bindings.end(),
[&entry](const auto& item) {
return item.binding == entry.binding;
});
return pos == descriptorSetLayout.bindings.end();
}), list.end());
return list;
}
} // namespace filament

View File

@@ -603,7 +603,7 @@ public:
* extension will be derived from the shader stage. For example, mymaterial_0x0e.frag,
* mymaterial_0x18.vert, etc.
*/
MaterialBuilder& saveRawVariants(bool saveRawVariants) noexcept;
MaterialBuilder& saveRawVariants(bool saveVariants) noexcept;
//! If true, will include debugging information in generated SPIRV.
MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept;
@@ -635,7 +635,7 @@ public:
* Build the material. If you are using the Filament engine with this library, you should use
* the job system provided by Engine.
*/
Package build(utils::JobSystem& jobSystem);
Package build(utils::JobSystem& jobSystem) noexcept;
public:
// The methods and types below are for internal use
@@ -809,7 +809,7 @@ private:
// Multiple calls to findProperties accumulate the property sets across fragment
// and vertex shaders in mProperties.
bool findProperties(filament::backend::ShaderStage type,
MaterialBuilder::PropertyList const& allProperties,
MaterialBuilder::PropertyList& allProperties,
CodeGenParams const& semanticCodeGenParams) noexcept;
bool runSemanticAnalysis(MaterialInfo* inOutInfo,

View File

@@ -34,19 +34,14 @@
#include "MetalArgumentBuffer.h"
#include "SpirvFixup.h"
#include "utils/ostream.h"
#include <filament/MaterialEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <algorithm>
#include <optional>
#include <sstream>
#include <unordered_map>
#include <utility>
#include <vector>
#ifdef FILAMENT_SUPPORTS_WEBGPU
@@ -156,16 +151,21 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
return layout;
}
static void collectDescriptorsForSet(DescriptorSetBindingPoints set,
static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
const GLSLPostProcessor::Config& config, DescriptorSetInfo& descriptors) {
const MaterialInfo& material = *config.materialInfo;
// get the descriptor set layout for the given pinding point
DescriptorSetLayout const descriptorSetLayout = [&] {
DescriptorSetLayout const info = [&]() {
switch (set) {
case DescriptorSetBindingPoints::PER_VIEW: {
return descriptor_sets::getPerViewDescriptorSetLayoutWithVariant(
config.variant, config.domain, config.variantFilter,
if (filament::Variant::isValidDepthVariant(config.variant)) {
return descriptor_sets::getDepthVariantLayout();
}
if (filament::Variant::isSSRVariant(config.variant)) {
return descriptor_sets::getSsrVariantLayout();
}
return descriptor_sets::getPerViewDescriptorSetLayout(config.domain,
config.variantFilter,
material.isLit || material.hasShadowMultiplier,
material.reflectionMode,
material.refractionMode);
@@ -179,8 +179,7 @@ static void collectDescriptorsForSet(DescriptorSetBindingPoints set,
}
}();
// get the sampler list for this binding point
auto samplerList = [&] {
auto samplerList = [&]() {
switch (set) {
case DescriptorSetBindingPoints::PER_VIEW:
return SibGenerator::getPerViewSib(config.variant).getSamplerInfoList();
@@ -193,34 +192,42 @@ static void collectDescriptorsForSet(DescriptorSetBindingPoints set,
}
}();
// filter the list with the descriptor set layout
auto const descriptorSetSamplerList =
SamplerInterfaceBlock::filterSamplerList(std::move(samplerList), descriptorSetLayout);
// remove all the samplers that are not included in the descriptor-set layout
samplerList.erase(std::remove_if(samplerList.begin(), samplerList.end(),
[&info](auto const& entry) {
auto pos = std::find_if(info.bindings.begin(),
info.bindings.end(), [&entry](const auto& item) {
return item.binding == entry.binding;
});
return pos == info.bindings.end();
}),
samplerList.end());
// helper to get the name of a descriptor for this set, given a binding.
auto getDescriptorName = [set, &descriptorSetSamplerList](descriptor_binding_t binding) {
auto getDescriptorName = [&](DescriptorSetBindingPoints set, descriptor_binding_t binding) {
if (set == DescriptorSetBindingPoints::PER_MATERIAL) {
auto pos = std::find_if(descriptorSetSamplerList.begin(), descriptorSetSamplerList.end(),
auto pos = std::find_if(samplerList.begin(), samplerList.end(),
[&](const auto& entry) { return entry.binding == binding; });
if (pos == descriptorSetSamplerList.end()) {
if (pos == samplerList.end()) {
return descriptor_sets::getDescriptorName(set, binding);
}
return pos->uniformName;
SamplerInterfaceBlock::SamplerInfo& sampler = *pos;
return sampler.uniformName;
}
return descriptor_sets::getDescriptorName(set, binding);
};
for (auto descriptor : descriptorSetLayout.bindings) {
descriptor_binding_t binding = descriptor.binding;
auto name = getDescriptorName(binding);
if (descriptor.type == DescriptorType::SAMPLER ||
descriptor.type == DescriptorType::SAMPLER_EXTERNAL) {
auto pos = std::find_if(descriptorSetSamplerList.begin(), descriptorSetSamplerList.end(),
for (size_t i = 0; i < info.bindings.size(); i++) {
backend::descriptor_binding_t binding = info.bindings[i].binding;
auto name = getDescriptorName(set, binding);
if (info.bindings[i].type == DescriptorType::SAMPLER ||
info.bindings[i].type == DescriptorType::SAMPLER_EXTERNAL) {
auto pos = std::find_if(samplerList.begin(), samplerList.end(),
[&](const auto& entry) { return entry.binding == binding; });
assert_invariant(pos != descriptorSetSamplerList.end());
descriptors.emplace_back(name, descriptor, *pos);
assert_invariant(pos != samplerList.end());
SamplerInterfaceBlock::SamplerInfo& sampler = *pos;
descriptors.emplace_back(name, info.bindings[i], sampler);
} else {
descriptors.emplace_back(name, descriptor, std::nullopt);
descriptors.emplace_back(name, info.bindings[i], std::nullopt);
}
}
@@ -358,7 +365,7 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const
}
void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
ShaderStage stage, ShaderModel shaderModel,
filament::backend::ShaderStage stage, filament::backend::ShaderModel shaderModel,
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
const ShaderMinifier* minifier) {
using namespace msl;
@@ -667,7 +674,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
// SpvRules should be enough.
// I think this could cause the compilation to fail on gl_VertexID.
using Type = std::underlying_type_t<EShMessages>;
msg = EShMessages(Type(msg) | Type(EShMsgVulkanRules));
msg = EShMessages(Type(msg) | Type(EShMessages::EShMsgVulkanRules));
}
bool const ok = tShader.parse(&DefaultTBuiltInResource, internalConfig.langVersion, false, msg);
@@ -677,7 +684,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
}
// add texture lod bias
if (config.shaderType == ShaderStage::FRAGMENT &&
if (config.shaderType == backend::ShaderStage::FRAGMENT &&
config.domain == MaterialDomain::SURFACE) {
GLSLTools::textureLodBias(tShader);
}
@@ -753,8 +760,8 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
return true;
}
bool GLSLPostProcessor::preprocessOptimization(TShader& tShader,
Config const& config, InternalConfig& internalConfig) const {
bool GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
using TargetApi = MaterialBuilder::TargetApi;
assert_invariant(bool(internalConfig.spirvOutput) == (config.targetApi != TargetApi::OPENGL));
@@ -825,7 +832,7 @@ bool GLSLPostProcessor::preprocessOptimization(TShader& tShader,
}
bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
Config const& config, InternalConfig& internalConfig) const {
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
SpirvBlob spirv;
bool const optimizeForSize = mOptimization == MaterialBuilderBase::Optimization::SIZE;
@@ -921,7 +928,7 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
#else
try {
*internalConfig.glslOutput = glslCompiler.compile();
} catch (CompilerError e) {
} catch (spirv_cross::CompilerError e) {
slog.e << "ERROR: " << e.what() << io::endl;
return false;
}
@@ -941,8 +948,8 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
return true;
}
std::shared_ptr<Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
auto optimizer = std::make_shared<Optimizer>(SPV_ENV_UNIVERSAL_1_3);
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
auto optimizer = std::make_shared<spvtools::Optimizer>(SPV_ENV_UNIVERSAL_1_3);
optimizer->SetMessageConsumer([](spv_message_level_t level,
const char* source, const spv_position_t& position, const char* message) {
if (!filterSpvOptimizerMessage(level)) {
@@ -954,7 +961,7 @@ std::shared_ptr<Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
return optimizer;
}
std::shared_ptr<Optimizer> GLSLPostProcessor::createOptimizer(
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
MaterialBuilder::Optimization optimization, Config const& config) {
auto optimizer = createEmptyOptimizer();
@@ -993,7 +1000,7 @@ void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv)
}
void GLSLPostProcessor::fixupClipDistance(
SpirvBlob& spirv, Config const& config) const {
SpirvBlob& spirv, GLSLPostProcessor::Config const& config) const {
if (!config.usesClipDistance) {
return;
}
@@ -1033,7 +1040,7 @@ void GLSLPostProcessor::fixupClipDistance(
void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config const& config) {
auto RegisterPass = [&](Optimizer::PassToken&& pass,
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
MaterialBuilder::TargetApi apiFilter = MaterialBuilder::TargetApi::ALL) {
if (!(config.targetApi & apiFilter)) {
return;
@@ -1078,7 +1085,7 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
}
void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& config) {
auto RegisterPass = [&](Optimizer::PassToken&& pass,
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
MaterialBuilder::TargetApi apiFilter = MaterialBuilder::TargetApi::ALL) {
if (!(config.targetApi & apiFilter)) {
return;

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,8 @@
#include "MaterialVariants.h"
#include "shaders/ShaderGenerator.h"
#include <private/filament/EngineEnums.h>
#include <private/filament/Variant.h>
@@ -23,8 +25,16 @@
#include <filament/MaterialEnums.h>
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/Log.h>
#include <algorithm>
#include <vector>
#include <stddef.h>
#include <stdint.h>
namespace filamat {
std::vector<Variant> determineSurfaceVariants(
@@ -52,6 +62,58 @@ std::vector<Variant> determineSurfaceVariants(
if (fragmentVariant == variant) {
variants.emplace_back(variant, filament::backend::ShaderStage::FRAGMENT);
}
// Here we make sure that the combination of vertex and fragment variants have compatible
// PER_VIEW descriptor-set layouts. This could actually be a static/compile-time check
// because it is entirely decided in DescriptorSets.cpp. Unfortunately it's not possible
// to write this entirely as a constexpr.
if (UTILS_UNLIKELY(vertexVariant != fragmentVariant)) {
// fragment and vertex variants are different, we need to check the layouts are
// compatible.
using filament::ReflectionMode;
using filament::RefractionMode;
using filament::backend::ShaderStage;
// And we need to do that for all configurations of the "PER_VIEW" descriptor set
// layouts (there are eight).
// See ShaderGenerator::getPerViewDescriptorSetLayoutWithVariant.
for (auto reflection: {
ReflectionMode::SCREEN_SPACE,
ReflectionMode::DEFAULT }) {
for (auto refraction: {
RefractionMode::SCREEN_SPACE,
RefractionMode::CUBEMAP,
RefractionMode::NONE }) {
auto const vdsl = ShaderGenerator::getPerViewDescriptorSetLayoutWithVariant(
vertexVariant, userVariantFilter, isLit || shadowMultiplier,
reflection, refraction);
auto const fdsl = ShaderGenerator::getPerViewDescriptorSetLayoutWithVariant(
fragmentVariant, userVariantFilter, isLit || shadowMultiplier,
reflection, refraction);
// Check that all bindings present in the vertex shader DescriptorSetLayout
// are also present in the fragment shader DescriptorSetLayout.
for (auto const& r: vdsl.bindings) {
if (!hasShaderType(r.stageFlags, ShaderStage::VERTEX)) {
// ignore descriptors that are of the fragment stage only
continue;
}
auto const pos = std::find_if(fdsl.bindings.begin(), fdsl.bindings.end(),
[r](auto const& l) {
return l.count == r.count && l.type == r.type &&
l.binding == r.binding && l.flags == r.flags &&
l.stageFlags == r.stageFlags;
});
// A mismatch is fatal. The material is ill-formed. This typically
// mean a bug / inconsistency in DescriptorsSets.cpp
FILAMENT_CHECK_POSTCONDITION(pos != fdsl.bindings.end())
<< "Variant " << +k << " has mismatched descriptorset layouts";
}
}
}
}
}
return variants;
}
@@ -71,7 +133,7 @@ std::vector<Variant> determinePostProcessVariants() {
std::vector<Variant> determineComputeVariants() {
// TODO: should we have variants for compute shaders?
std::vector<Variant> variants;
filament::Variant const variant(0);
filament::Variant variant(0);
variants.emplace_back(variant, filament::backend::ShaderStage::COMPUTE);
return variants;
}

View File

View File

View File

@@ -39,7 +39,7 @@ namespace filamat {
MaterialUniformInterfaceBlockChunk::MaterialUniformInterfaceBlockChunk(
BufferInterfaceBlock const& uib) :
Chunk(MaterialUib),
Chunk(ChunkType::MaterialUib),
mUib(uib) {
}
@@ -60,7 +60,7 @@ void MaterialUniformInterfaceBlockChunk::flatten(Flattener& f) {
MaterialSamplerInterfaceBlockChunk::MaterialSamplerInterfaceBlockChunk(
SamplerInterfaceBlock const& sib) :
Chunk(MaterialSib),
Chunk(ChunkType::MaterialSib),
mSib(sib) {
}
@@ -81,7 +81,7 @@ void MaterialSamplerInterfaceBlockChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialSubpassInterfaceBlockChunk::MaterialSubpassInterfaceBlockChunk(SubpassInfo const& subpass) :
Chunk(MaterialSubpass),
Chunk(ChunkType::MaterialSubpass),
mSubpass(subpass) {
}
@@ -101,8 +101,8 @@ void MaterialSubpassInterfaceBlockChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialConstantParametersChunk::MaterialConstantParametersChunk(
FixedCapacityVector<MaterialConstant> constants)
: Chunk(MaterialConstants), mConstants(std::move(constants)) {}
utils::FixedCapacityVector<MaterialConstant> constants)
: Chunk(ChunkType::MaterialConstants), mConstants(std::move(constants)) {}
void MaterialConstantParametersChunk::flatten(Flattener& f) {
f.writeUint64(mConstants.size());
@@ -115,8 +115,8 @@ void MaterialConstantParametersChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialPushConstantParametersChunk::MaterialPushConstantParametersChunk(
CString const& structVarName, FixedCapacityVector<MaterialPushConstant> constants)
: Chunk(MaterialPushConstants),
CString const& structVarName, utils::FixedCapacityVector<MaterialPushConstant> constants)
: Chunk(ChunkType::MaterialPushConstants),
mStructVarName(structVarName),
mConstants(std::move(constants)) {}
@@ -133,7 +133,7 @@ void MaterialPushConstantParametersChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialBindingUniformInfoChunk::MaterialBindingUniformInfoChunk(Container list) noexcept
: Chunk(MaterialBindingUniformInfo),
: Chunk(ChunkType::MaterialBindingUniformInfo),
mBindingUniformInfo(std::move(list)) {
}
@@ -155,7 +155,7 @@ void MaterialBindingUniformInfoChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialAttributesInfoChunk::MaterialAttributesInfoChunk(Container list) noexcept
: Chunk(MaterialAttributeInfo),
: Chunk(ChunkType::MaterialAttributeInfo),
mAttributeInfo(std::move(list))
{
}
@@ -170,9 +170,11 @@ void MaterialAttributesInfoChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialDescriptorBindingsChuck::MaterialDescriptorBindingsChuck(Container const& sib) noexcept
: Chunk(MaterialDescriptorBindingsInfo),
mSamplerInterfaceBlock(sib) {
MaterialDescriptorBindingsChuck::MaterialDescriptorBindingsChuck(Container const& sib,
backend::DescriptorSetLayout const& perViewLayout) noexcept
: Chunk(ChunkType::MaterialDescriptorBindingsInfo),
mSamplerInterfaceBlock(sib),
mPerViewLayout(perViewLayout) {
}
void MaterialDescriptorBindingsChuck::flatten(Flattener& f) {
@@ -181,6 +183,13 @@ void MaterialDescriptorBindingsChuck::flatten(Flattener& f) {
using namespace backend;
// number of descriptor-sets
f.writeUint8(3);
// set
f.writeUint8(+DescriptorSetBindingPoints::PER_MATERIAL);
// samplers + 1 descriptor for the UBO
f.writeUint8(mSamplerInterfaceBlock.getSize() + 1);
@@ -201,13 +210,37 @@ void MaterialDescriptorBindingsChuck::flatten(Flattener& f) {
}
f.writeUint8(entry.binding);
}
// set
f.writeUint8(+DescriptorSetBindingPoints::PER_RENDERABLE);
f.writeUint8(descriptor_sets::getPerRenderableLayout().bindings.size());
for (auto const& entry: descriptor_sets::getPerRenderableLayout().bindings) {
auto const& name = descriptor_sets::getDescriptorName(
DescriptorSetBindingPoints::PER_RENDERABLE, entry.binding);
f.writeString({ name.data(), name.size() });
f.writeUint8(uint8_t(entry.type));
f.writeUint8(entry.binding);
}
// set
f.writeUint8(+DescriptorSetBindingPoints::PER_VIEW);
f.writeUint8(mPerViewLayout.bindings.size());
for (auto const& entry: mPerViewLayout.bindings) {
auto const& name = descriptor_sets::getDescriptorName(
DescriptorSetBindingPoints::PER_VIEW, entry.binding);
f.writeString({ name.data(), name.size() });
f.writeUint8(uint8_t(entry.type));
f.writeUint8(entry.binding);
}
}
// ------------------------------------------------------------------------------------------------
MaterialDescriptorSetLayoutChunk::MaterialDescriptorSetLayoutChunk(Container const& sib) noexcept
: Chunk(MaterialDescriptorSetLayoutInfo),
mSamplerInterfaceBlock(sib) {
MaterialDescriptorSetLayoutChunk::MaterialDescriptorSetLayoutChunk(Container const& sib,
backend::DescriptorSetLayout const& perViewLayout) noexcept
: Chunk(ChunkType::MaterialDescriptorSetLayoutInfo),
mSamplerInterfaceBlock(sib),
mPerViewLayout(perViewLayout) {
}
void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
@@ -238,6 +271,18 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
f.writeUint8(uint8_t(DescriptorFlags::NONE));
f.writeUint16(0);
}
// samplers + 1 descriptor for the UBO
f.writeUint8(mPerViewLayout.bindings.size());
// all the material's sampler descriptors
for (auto const& entry: mPerViewLayout.bindings) {
f.writeUint8(uint8_t(entry.type));
f.writeUint8(uint8_t(entry.stageFlags));
f.writeUint8(entry.binding);
f.writeUint8(uint8_t(entry.flags));
f.writeUint16(entry.count);
}
}
} // namespace filamat

View File

@@ -19,12 +19,16 @@
#include "Chunk.h"
#include <private/filament/EngineEnums.h>
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <tuple>
#include <utility>
#include <stdint.h>
@@ -41,10 +45,10 @@ namespace filamat {
class MaterialUniformInterfaceBlockChunk final : public Chunk {
public:
explicit MaterialUniformInterfaceBlockChunk(filament::BufferInterfaceBlock const& uib);
~MaterialUniformInterfaceBlockChunk() override = default;
~MaterialUniformInterfaceBlockChunk() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
filament::BufferInterfaceBlock const& mUib;
};
@@ -54,10 +58,10 @@ private:
class MaterialSamplerInterfaceBlockChunk final : public Chunk {
public:
explicit MaterialSamplerInterfaceBlockChunk(filament::SamplerInterfaceBlock const& sib);
~MaterialSamplerInterfaceBlockChunk() override = default;
~MaterialSamplerInterfaceBlockChunk() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
filament::SamplerInterfaceBlock const& mSib;
};
@@ -67,10 +71,10 @@ private:
class MaterialSubpassInterfaceBlockChunk final : public Chunk {
public:
explicit MaterialSubpassInterfaceBlockChunk(filament::SubpassInfo const& subpass);
~MaterialSubpassInterfaceBlockChunk() override = default;
~MaterialSubpassInterfaceBlockChunk() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
filament::SubpassInfo const& mSubpass;
};
@@ -80,41 +84,41 @@ private:
class MaterialConstantParametersChunk final : public Chunk {
public:
explicit MaterialConstantParametersChunk(
FixedCapacityVector<filament::MaterialConstant> constants);
~MaterialConstantParametersChunk() override = default;
utils::FixedCapacityVector<filament::MaterialConstant> constants);
~MaterialConstantParametersChunk() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
FixedCapacityVector<filament::MaterialConstant> mConstants;
utils::FixedCapacityVector<filament::MaterialConstant> mConstants;
};
// ------------------------------------------------------------------------------------------------
class MaterialPushConstantParametersChunk final : public Chunk {
public:
explicit MaterialPushConstantParametersChunk(CString const& structVarName,
FixedCapacityVector<filament::MaterialPushConstant> constants);
~MaterialPushConstantParametersChunk() override = default;
explicit MaterialPushConstantParametersChunk(utils::CString const& structVarName,
utils::FixedCapacityVector<filament::MaterialPushConstant> constants);
~MaterialPushConstantParametersChunk() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
CString mStructVarName;
FixedCapacityVector<filament::MaterialPushConstant> mConstants;
utils::CString mStructVarName;
utils::FixedCapacityVector<filament::MaterialPushConstant> mConstants;
};
// ------------------------------------------------------------------------------------------------
class MaterialBindingUniformInfoChunk final : public Chunk {
using Container = FixedCapacityVector<std::tuple<
uint8_t, CString, filament::backend::Program::UniformInfo>>;
uint8_t, utils::CString, filament::backend::Program::UniformInfo>>;
public:
explicit MaterialBindingUniformInfoChunk(Container list) noexcept;
~MaterialBindingUniformInfoChunk() override = default;
~MaterialBindingUniformInfoChunk() final = default;
private:
void flatten(Flattener &) override;
void flatten(Flattener &) final;
Container mBindingUniformInfo;
};
@@ -122,13 +126,13 @@ private:
// ------------------------------------------------------------------------------------------------
class MaterialAttributesInfoChunk final : public Chunk {
using Container = FixedCapacityVector<std::pair<CString, uint8_t>>;
using Container = FixedCapacityVector<std::pair<utils::CString, uint8_t>>;
public:
explicit MaterialAttributesInfoChunk(Container list) noexcept;
~MaterialAttributesInfoChunk() override = default;
~MaterialAttributesInfoChunk() final = default;
private:
void flatten(Flattener &) override;
void flatten(Flattener &) final;
Container mAttributeInfo;
};
@@ -138,13 +142,15 @@ private:
class MaterialDescriptorBindingsChuck final : public Chunk {
using Container = filament::SamplerInterfaceBlock;
public:
explicit MaterialDescriptorBindingsChuck(Container const& sib) noexcept;
~MaterialDescriptorBindingsChuck() override = default;
explicit MaterialDescriptorBindingsChuck(Container const& sib,
filament::backend::DescriptorSetLayout const& perViewLayout) noexcept;
~MaterialDescriptorBindingsChuck() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
Container const& mSamplerInterfaceBlock;
filament::backend::DescriptorSetLayout mPerViewLayout;
};
// ------------------------------------------------------------------------------------------------
@@ -152,13 +158,15 @@ private:
class MaterialDescriptorSetLayoutChunk final : public Chunk {
using Container = filament::SamplerInterfaceBlock;
public:
explicit MaterialDescriptorSetLayoutChunk(Container const& sib) noexcept;
~MaterialDescriptorSetLayoutChunk() override = default;
explicit MaterialDescriptorSetLayoutChunk(Container const& sib,
filament::backend::DescriptorSetLayout const& perViewLayout) noexcept;
~MaterialDescriptorSetLayoutChunk() final = default;
private:
void flatten(Flattener&) override;
void flatten(Flattener&) final;
Container const& mSamplerInterfaceBlock;
filament::backend::DescriptorSetLayout mPerViewLayout;
};
} // namespace filamat

View File

@@ -604,13 +604,27 @@ std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel shaderMode
if (featureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
auto const perViewDescriptorSetLayout = getPerViewDescriptorSetLayoutWithVariant(
variant, variantFilter,
material.isLit || material.hasShadowMultiplier,
material.reflectionMode, material.refractionMode);
// this is the list of samplers we need to filter
auto const list = SamplerInterfaceBlock::filterSamplerList(
SibGenerator::getPerViewSib(variant).getSamplerInfoList(),
descriptor_sets::getPerViewDescriptorSetLayoutWithVariant(
variant, mMaterialDomain, variantFilter,
material.isLit || material.hasShadowMultiplier,
material.reflectionMode, material.refractionMode));
auto list = SibGenerator::getPerViewSib(variant).getSamplerInfoList();
// remove all the samplers that are not included in the descriptor-set layout
list.erase(
std::remove_if(list.begin(), list.end(),
[&perViewDescriptorSetLayout](auto const& entry) {
auto pos = std::find_if(
perViewDescriptorSetLayout.bindings.begin(),
perViewDescriptorSetLayout.bindings.end(),
[&entry](const auto& item) {
return item.binding == entry.binding;
});
return pos == perViewDescriptorSetLayout.bindings.end();
}), list.end());
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_VIEW, list);
}
@@ -827,4 +841,22 @@ bool ShaderGenerator::hasStereo(
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
}
backend::DescriptorSetLayout ShaderGenerator::getPerViewDescriptorSetLayoutWithVariant(
filament::Variant variant,
UserVariantFilterMask variantFilter,
bool isLit,
ReflectionMode reflectionMode,
RefractionMode refractionMode) {
if (filament::Variant::isValidDepthVariant(variant)) {
return descriptor_sets::getDepthVariantLayout();
}
if (filament::Variant::isSSRVariant(variant)) {
return descriptor_sets::getSsrVariantLayout();
}
// We need to filter out all the descriptors not included in the "resolved" layout below
return descriptor_sets::getPerViewDescriptorSetLayout(
MaterialDomain::SURFACE, variantFilter,
isLit, reflectionMode, refractionMode);
}
} // namespace filament

View File

@@ -88,6 +88,13 @@ public:
MaterialBuilder::FeatureLevel featureLevel,
MaterialInfo const& material) noexcept;
static filament::backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
filament::Variant variant,
filament::UserVariantFilterMask variantFilter,
bool isLit,
filament::ReflectionMode reflectionMode,
filament::RefractionMode refractionMode);
private:
static void generateVertexDomainDefines(utils::io::sstream& out,
filament::VertexDomain domain) noexcept;

View File

@@ -50,6 +50,8 @@
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/NameComponentManager.h>
#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO
#include <utils/Systrace.h>
#include <tsl/robin_map.h>

View File

@@ -37,7 +37,10 @@
#include <utils/compiler.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO
#include <utils/Systrace.h>
#include <utils/Path.h>
#include <cgltf.h>

View File

@@ -21,6 +21,8 @@
#include "GltfEnums.h"
#include <utils/Log.h>
#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO
#include <utils/Systrace.h>
#define CGLTF_IMPLEMENTATION

View File

@@ -128,7 +128,6 @@ public:
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
CString& append(const CString& str) noexcept { return insert(length(), str); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());

View File

@@ -17,24 +17,23 @@
#ifndef TNT_UTILS_SYSTRACE_H
#define TNT_UTILS_SYSTRACE_H
#define SYSTRACE_TAG_NEVER (0)
#define SYSTRACE_TAG_ALWAYS (1<<0)
#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles
#define SYSTRACE_TAG_JOBSYSTEM (1<<2)
#define SYSTRACE_TAG_DISABLED (0)
#define SYSTRACE_TAG_FILAMENT (2) // don't change used in makefiles
#define SYSTRACE_TAG_JOBSYSTEM (3)
#define SYSTRACE_TAG_GLTFIO (4)
/*
* The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined
* before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used.
* The SYSTRACE_ macros use SYSTRACE_TAG as a category, which must be defined
* before this file is included.
*/
#ifndef SYSTRACE_TAG
#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS)
# error SYSTRACE_TAG must be set to SYSTRACE_TAG_{DISABLED|FILAMENT|JOBSYSTEM}
#endif
// Systrace on Apple platforms is fragile and adds overhead, should only be enabled in dev builds.
#ifndef FILAMENT_APPLE_SYSTRACE
#define FILAMENT_APPLE_SYSTRACE 0
# define FILAMENT_APPLE_SYSTRACE 0
#endif
#if defined(__ANDROID__)
@@ -44,7 +43,6 @@
#else
#define SYSTRACE_ENABLE()
#define SYSTRACE_DISABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_FRAME_ID(frame)
@@ -56,6 +54,6 @@
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#endif // ANDROID
#endif
#endif // TNT_UTILS_SYSTRACE_H

View File

@@ -17,226 +17,75 @@
#ifndef TNT_UTILS_ANDROID_SYSTRACE_H
#define TNT_UTILS_ANDROID_SYSTRACE_H
#include <atomic>
#include <perfetto/perfetto.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <utils/compiler.h>
PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(systrace,
perfetto::Category("filament"),
perfetto::Category("jobsystem"),
perfetto::Category("gltfio"));
// enable tracing
#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG)
PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(systrace);
// disable tracing
#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG)
#if SYSTRACE_TAG == SYSTRACE_TAG_FILAMENT
# define UTILS_PERFETTO_CATEGORY "filament"
#elif SYSTRACE_TAG == SYSTRACE_TAG_JOBSYSTEM
# define UTILS_PERFETTO_CATEGORY "jobsystem"
#elif SYSTRACE_TAG == SYSTRACE_TAG_GLTFIO
# define UTILS_PERFETTO_CATEGORY "gltfio"
#endif
#if SYSTRACE_TAG == SYSTRACE_TAG_DISABLED
/**
* Creates a Systrace context in the current scope. needed for calling all other systrace
* commands below.
*/
#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___trctx(SYSTRACE_TAG)
#define SYSTRACE_ENABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_FRAME_ID(frame)
#define SYSTRACE_NAME_BEGIN(name)
#define SYSTRACE_NAME_END()
#define SYSTRACE_CALL()
#define SYSTRACE_ASYNC_BEGIN(name, cookie)
#define SYSTRACE_ASYNC_END(name, cookie)
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#else
// SYSTRACE_NAME traces the beginning and end of the current scope. To trace
// the correct start and end times this macro should be declared first in the
// scope body.
// It also automatically creates a Systrace context
#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name)
#define SYSTRACE_ENABLE()
#define SYSTRACE_CONTEXT()
// Denotes that a new frame has started processing.
#define SYSTRACE_FRAME_ID(frame) \
{ /* scope for frame id trace */ \
char buf[64]; \
snprintf(buf, 64, "frame %u", frame); \
SYSTRACE_NAME(buf); \
}
#define SYSTRACE_CALL() \
auto constexpr FILAMENT_SYSTRACE_FUNCTION = perfetto::StaticString(__FUNCTION__); \
TRACE_EVENT(UTILS_PERFETTO_CATEGORY, FILAMENT_SYSTRACE_FUNCTION)
// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name.
#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__)
#define SYSTRACE_NAME(name) TRACE_EVENT(UTILS_PERFETTO_CATEGORY, nullptr, \
[&](perfetto::EventContext ctx) { \
ctx.event()->set_name(name); \
})
#define SYSTRACE_NAME_BEGIN(name) \
___trctx.traceBegin(SYSTRACE_TAG, name)
#define SYSTRACE_NAME_BEGIN(name) TRACE_EVENT_BEGIN(UTILS_PERFETTO_CATEGORY, nullptr, \
[&](perfetto::EventContext ctx) { \
ctx.event()->set_name(name); \
})
#define SYSTRACE_NAME_END() \
___trctx.traceEnd(SYSTRACE_TAG)
#define SYSTRACE_NAME_END() TRACE_EVENT_END(UTILS_PERFETTO_CATEGORY)
/**
* Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
* contexts, asynchronous events do not need to be nested. The name describes
* the event, and the cookie provides a unique identifier for distinguishing
* simultaneous events. The name and cookie used to begin an event must be
* used to end it.
*/
#define SYSTRACE_ASYNC_BEGIN(name, cookie) \
___trctx.asyncBegin(SYSTRACE_TAG, name, cookie)
TRACE_EVENT_BEGIN(UTILS_PERFETTO_CATEGORY, name, perfetto::Track(cookie))
/**
* Trace the end of an asynchronous event.
* This should have a corresponding SYSTRACE_ASYNC_BEGIN.
*/
#define SYSTRACE_ASYNC_END(name, cookie) \
___trctx.asyncEnd(SYSTRACE_TAG, name, cookie)
TRACE_EVENT_END(UTILS_PERFETTO_CATEGORY, perfetto::Track(cookie))
#define SYSTRACE_FRAME_ID(frame) \
TRACE_EVENT_INSTANT(UTILS_PERFETTO_CATEGORY, "frame", "id", frame)
/**
* Traces an integer counter value. name is used to identify the counter.
* This can be used to track how a value changes over time.
*/
#define SYSTRACE_VALUE32(name, val) \
___trctx.value(SYSTRACE_TAG, name, int32_t(val))
TRACE_COUNTER(UTILS_PERFETTO_CATEGORY, name, val)
#define SYSTRACE_VALUE64(name, val) \
___trctx.value(SYSTRACE_TAG, name, int64_t(val))
TRACE_COUNTER(UTILS_PERFETTO_CATEGORY, name, val)
// ------------------------------------------------------------------------------------------------
// No user serviceable code below...
// ------------------------------------------------------------------------------------------------
namespace utils {
namespace details {
class UTILS_PUBLIC Systrace {
public:
enum tags {
NEVER = SYSTRACE_TAG_NEVER,
ALWAYS = SYSTRACE_TAG_ALWAYS,
FILAMENT = SYSTRACE_TAG_FILAMENT,
JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM
// we could define more TAGS here, as we need them.
};
explicit Systrace(uint32_t tag) noexcept {
if (tag) init(tag);
}
static void enable(uint32_t tags) noexcept;
static void disable(uint32_t tags) noexcept;
inline void traceBegin(uint32_t tag, const char* name) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginSection(this, name);
}
}
inline void traceEnd(uint32_t tag) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endSection(this);
}
}
inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginAsyncSection(this, name, cookie);
}
}
inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endAsyncSection(this, name, cookie);
}
}
inline void value(uint32_t tag, const char* name, int32_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
inline void value(uint32_t tag, const char* name, int64_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
private:
friend class ScopedTrace;
// whether tracing is supported at all by the platform
using ATrace_isEnabled_t = bool (*)();
using ATrace_beginSection_t = void (*)(const char* sectionName);
using ATrace_endSection_t = void (*)();
using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue);
struct GlobalState {
bool isTracingAvailable;
std::atomic<uint32_t> isTracingEnabled;
int markerFd;
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
};
static GlobalState sGlobalState;
// per-instance versions for better performance
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
void init(uint32_t tag) noexcept;
// cached values for faster access, no need to be initialized
bool mIsTracingEnabled;
int mMarkerFd = -1;
pid_t mPid;
static void setup() noexcept;
static void init_once() noexcept;
static bool isTracingEnabled(uint32_t tag) noexcept;
static void begin_body(int fd, int pid, const char* name) noexcept;
static void end_body(int fd, int pid) noexcept;
static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept;
};
// ------------------------------------------------------------------------------------------------
class UTILS_PUBLIC ScopedTrace {
public:
// we don't inline this because it's relatively heavy due to a global check
ScopedTrace(uint32_t tag, const char* name) noexcept: mTrace(tag), mTag(tag) {
mTrace.traceBegin(tag, name);
}
inline ~ScopedTrace() noexcept {
mTrace.traceEnd(mTag);
}
private:
Systrace mTrace;
const uint32_t mTag;
};
} // namespace details
} // namespace utils
#endif // SYSTRACE_TAG == SYSTRACE_TAG_DISABLED
#endif // TNT_UTILS_ANDROID_SYSTRACE_H

View File

@@ -29,13 +29,25 @@
#include <utils/compiler.h>
#include <stack>
#if SYSTRACE_TAG == SYSTRACE_TAG_DISABLED
#define SYSTRACE_ENABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_FRAME_ID(frame)
#define SYSTRACE_NAME_BEGIN(name)
#define SYSTRACE_NAME_END()
#define SYSTRACE_CALL()
#define SYSTRACE_ASYNC_BEGIN(name, cookie)
#define SYSTRACE_ASYNC_END(name, cookie)
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#else
// enable tracing
#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG)
// disable tracing
#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG)
/**
* Creates a Systrace context in the current scope. needed for calling all other systrace
* commands below.
@@ -93,6 +105,8 @@ extern thread_local std::stack<const char*> ___tracerSections;
#define SYSTRACE_VALUE64(name, val) \
___tracer.value(SYSTRACE_TAG, name, int64_t(val))
#endif // SYSTRACE_TAG == SYSTRACE_TAG_DISABLED
// ------------------------------------------------------------------------------------------------
// No user serviceable code below...
// ------------------------------------------------------------------------------------------------
@@ -118,50 +132,40 @@ namespace utils {
namespace details {
class Systrace {
public:
enum tags {
NEVER = SYSTRACE_TAG_NEVER,
ALWAYS = SYSTRACE_TAG_ALWAYS,
FILAMENT = SYSTRACE_TAG_FILAMENT,
JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM
// we could define more TAGS here, as we need them.
};
public:
explicit Systrace(uint32_t tag) noexcept {
if (tag) init(tag);
}
static void enable(uint32_t tags) noexcept;
static void disable(uint32_t tags) noexcept;
static void enable(uint32_t tag) noexcept;
inline void traceBegin(uint32_t tag, const char* name) noexcept {
void traceBegin(uint32_t tag, const char* name) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
APPLE_SIGNPOST_EMIT(sGlobalState.systraceLog, OS_SIGNPOST_INTERVAL_BEGIN,
OS_SIGNPOST_ID_EXCLUSIVE, name, name)
}
}
inline void traceEnd(uint32_t tag, const char* name) noexcept {
void traceEnd(uint32_t tag, const char* name) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
APPLE_SIGNPOST_EMIT(sGlobalState.systraceLog, OS_SIGNPOST_INTERVAL_END,
OS_SIGNPOST_ID_EXCLUSIVE, name, "")
}
}
inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
// TODO
}
}
inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
// TODO
}
}
inline void value(uint32_t tag, const char* name, int32_t value) noexcept {
void value(uint32_t tag, const char* name, int32_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
char buf[64];
snprintf(buf, 64, "%s - %d", name, value);
@@ -170,7 +174,7 @@ class Systrace {
}
}
inline void value(uint32_t tag, const char* name, int64_t value) noexcept {
void value(uint32_t tag, const char* name, int64_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
char buf[64];
snprintf(buf, 64, "%s - %lld", name, value);
@@ -179,16 +183,16 @@ class Systrace {
}
}
inline void frameId(uint32_t tag, uint32_t frame) noexcept {
void frameId(uint32_t tag, uint32_t frame) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
char buf[64]; \
snprintf(buf, 64, "frame %u", frame); \
char buf[64];
snprintf(buf, 64, "frame %u", frame);
APPLE_SIGNPOST_EMIT(sGlobalState.frameIdLog, OS_SIGNPOST_EVENT,
OS_SIGNPOST_ID_EXCLUSIVE, "frame", buf)
}
}
private:
private:
friend class ScopedTrace;
struct GlobalState {
@@ -213,25 +217,25 @@ class Systrace {
// ------------------------------------------------------------------------------------------------
class ScopedTrace {
public:
public:
// we don't inline this because it's relatively heavy due to a global check
ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mName(name), mTag(tag) {
mTrace.traceBegin(tag, name);
}
inline ~ScopedTrace() noexcept {
~ScopedTrace() noexcept {
mTrace.traceEnd(mTag, mName);
}
inline void value(uint32_t tag, const char* name, int32_t v) noexcept {
void value(uint32_t tag, const char* name, int32_t v) noexcept {
mTrace.value(tag, name, v);
}
inline void value(uint32_t tag, const char* name, int64_t v) noexcept {
void value(uint32_t tag, const char* name, int64_t v) noexcept {
mTrace.value(tag, name, v);
}
private:
private:
Systrace mTrace;
const char* mName;
const uint32_t mTag;

View File

@@ -15,10 +15,8 @@
*/
// Note: The overhead of SYSTRACE_TAG_JOBSYSTEM is not negligible especially with parallel_for().
#ifndef SYSTRACE_TAG
#define SYSTRACE_TAG SYSTRACE_TAG_DISABLED
//#define SYSTRACE_TAG SYSTRACE_TAG_JOBSYSTEM
#define SYSTRACE_TAG SYSTRACE_TAG_NEVER
#endif
// when SYSTRACE_TAG_JOBSYSTEM is used, enables even heavier systraces
#define HEAVY_SYSTRACE 0

View File

@@ -14,206 +14,26 @@
* limitations under the License.
*/
#include <utils/Systrace.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/android/Systrace.h>
#include <cinttypes>
#include <perfetto/perfetto.h>
#include <string.h>
PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(systrace);
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <dlfcn.h>
namespace {
namespace utils {
namespace details {
static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
template <typename T>
static void loadSymbol(T*& pfn, const char *symbol) noexcept {
pfn = (T*)dlsym(RTLD_DEFAULT, symbol);
}
Systrace::GlobalState Systrace::sGlobalState = {};
void Systrace::init_once() noexcept {
GlobalState& s = sGlobalState;
s.markerFd = -1;
// API 23
loadSymbol(s.ATrace_isEnabled, "ATrace_isEnabled");
loadSymbol(s.ATrace_beginSection, "ATrace_beginSection");
loadSymbol(s.ATrace_endSection, "ATrace_endSection");
// API 29
loadSymbol(s.ATrace_beginAsyncSection, "ATrace_beginAsyncSection");
loadSymbol(s.ATrace_endAsyncSection, "ATrace_endAsyncSection");
loadSymbol(s.ATrace_setCounter, "ATrace_setCounter");
const bool hasBasicAtrace = s.ATrace_isEnabled &&
s.ATrace_beginSection &&
s.ATrace_endSection;
const bool hasFullATrace = hasBasicAtrace &&
s.ATrace_beginAsyncSection &&
s.ATrace_endAsyncSection &&
s.ATrace_setCounter;
if (!hasFullATrace) {
s.markerFd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
class SystraceStaticInitialization {
public:
SystraceStaticInitialization() {
perfetto::TracingInitArgs args;
args.backends |= perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
systrace::TrackEvent::Register();
}
};
if (hasBasicAtrace && !hasFullATrace) {
// no-op if we don't have all these
s.ATrace_beginAsyncSection = [](const char* sectionName, int32_t cookie){};
s.ATrace_endAsyncSection = [](const char* sectionName, int32_t cookie){};
s.ATrace_setCounter = [](const char* sectionName, int64_t counterValue){};
}
UTILS_UNUSED SystraceStaticInitialization sSystraceStaticInitialization{};
const bool hasLegacySystrace = s.markerFd != -1;
if (hasLegacySystrace && !hasFullATrace) {
// use legacy
s.beginSection = [](Systrace* that, const char* name) {
begin_body(that->mMarkerFd, that->mPid, name);
};
s.endSection = [](Systrace* that) {
end_body(that->mMarkerFd, that->mPid);
};
s.beginAsyncSection = [](Systrace* that, const char* name, int32_t cookie) {
async_begin_body(that->mMarkerFd, that->mPid, name, cookie);
};
s.endAsyncSection = [](Systrace* that, const char* name, int32_t cookie) {
async_end_body(that->mMarkerFd, that->mPid, name, cookie);
};
s.setCounter = [](Systrace* that, const char* name, int64_t value) {
int64_body(that->mMarkerFd, that->mPid, name, value);
};
} else if (hasBasicAtrace) {
// we have at least basic ATrace
s.beginSection = [](Systrace* that, const char* name) {
that->ATrace_beginSection(name);
};
s.endSection = [](Systrace* that) {
that->ATrace_endSection();
};
s.beginAsyncSection = [](Systrace* that, const char* name, int32_t cookie) {
that->ATrace_beginAsyncSection(name, cookie);
};
s.endAsyncSection = [](Systrace* that, const char* name, int32_t cookie) {
that->ATrace_endAsyncSection(name, cookie);
};
s.setCounter = [](Systrace* that, const char* name, int64_t value) {
that->ATrace_setCounter(name, value);
};
}
s.isTracingAvailable = hasLegacySystrace || hasFullATrace || hasBasicAtrace;
}
void Systrace::setup() noexcept {
pthread_once(&atrace_once_control, init_once);
}
void Systrace::enable(uint32_t tags) noexcept {
setup();
if (UTILS_LIKELY(sGlobalState.isTracingAvailable)) {
sGlobalState.isTracingEnabled.fetch_or(tags, std::memory_order_relaxed);
}
}
void Systrace::disable(uint32_t tags) noexcept {
sGlobalState.isTracingEnabled.fetch_and(~tags, std::memory_order_relaxed);
}
// unfortunately, this generates quite a bit of code because reading a global is not
// trivial. For this reason, we do not inline this method.
bool Systrace::isTracingEnabled(uint32_t tag) noexcept {
if (tag) {
setup();
return bool((sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) | SYSTRACE_TAG_ALWAYS) & tag);
}
return false;
}
// ------------------------------------------------------------------------------------------------
void Systrace::init(uint32_t tag) noexcept {
// must be called first
mIsTracingEnabled = isTracingEnabled(tag);
// cache static variables for better efficiency
GlobalState& s = sGlobalState;
ATrace_isEnabled = s.ATrace_isEnabled;
ATrace_beginSection = s.ATrace_beginSection;
ATrace_endSection = s.ATrace_endSection;
ATrace_beginAsyncSection = s.ATrace_beginAsyncSection;
ATrace_endAsyncSection = s.ATrace_endAsyncSection;
ATrace_setCounter = s.ATrace_setCounter;
beginSection = s.beginSection;
endSection = s.endSection;
beginAsyncSection = s.beginAsyncSection;
endAsyncSection = s.endAsyncSection;
setCounter = s.setCounter;
mMarkerFd = s.markerFd;
mPid = getpid();
}
// ------------------------------------------------------------------------------------------------
/**
* Maximum size of a message that can be logged to the trace buffer.
* Note this message includes a tag, the pid, and the string given as the name.
* Names should be kept short to get the most use of the trace buffer.
*/
#define ATRACE_MESSAGE_LENGTH 512
#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
char buf[ATRACE_MESSAGE_LENGTH]; \
int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
name, value); \
if (len >= (int) sizeof(buf)) { \
/* Given the sizeof(buf), and all of the current format buffers, \
* it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
/* Truncate the name to make the message fit. */ \
len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
name_len, name, value); \
} \
write(fd, buf, len); \
}
void Systrace::begin_body(int fd, int pid, const char* name) noexcept {
char buf[ATRACE_MESSAGE_LENGTH];
ssize_t len = snprintf(buf, sizeof(buf), "B|%d|%s", pid, name);
if (len >= sizeof(buf)) {
len = sizeof(buf) - 1;
}
write(fd, buf, size_t(len));
}
void Systrace::end_body(int fd, int pid) noexcept {
const char END_TAG = 'E';
write(fd, &END_TAG, 1);
}
void Systrace::async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept {
WRITE_MSG("S|%d|", "|%" PRId32, pid, name, cookie);
}
void Systrace::async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept {
WRITE_MSG("F|%d|", "|%" PRId32, pid, name, cookie);
}
void Systrace::int64_body(int fd, int pid, const char* name, int64_t value) noexcept {
WRITE_MSG("C|%d|", "|%" PRId64, pid, name, value);
}
} // namespace details
} // namespace utils

View File

@@ -14,11 +14,17 @@
* limitations under the License.
*/
#include <utils/Systrace.h>
#include <utils/Log.h>
#include <utils/darwin/Systrace.h>
#ifndef FILAMENT_APPLE_SYSTRACE
# define FILAMENT_APPLE_SYSTRACE 0
#endif
#if FILAMENT_APPLE_SYSTRACE
#include <atomic>
#include <stack>
#include <stdint.h>
#include <pthread.h>
static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
@@ -41,21 +47,24 @@ void Systrace::setup() noexcept {
pthread_once(&atrace_once_control, init_once);
}
void Systrace::enable(uint32_t tags) noexcept {
void Systrace::enable(uint32_t tag) noexcept {
setup();
sGlobalState.isTracingEnabled.fetch_or(tags, std::memory_order_relaxed);
uint32_t const mask = 1 << tag;
sGlobalState.isTracingEnabled.fetch_or(mask, std::memory_order_relaxed);
}
void Systrace::disable(uint32_t tags) noexcept {
sGlobalState.isTracingEnabled.fetch_and(~tags, std::memory_order_relaxed);
void Systrace::disable(uint32_t tag) noexcept {
uint32_t const mask = 1 << tag;
sGlobalState.isTracingEnabled.fetch_and(~mask, std::memory_order_relaxed);
}
// unfortunately, this generates quite a bit of code because reading a global is not
// Unfortunately, this generates quite a bit of code because reading a global is not
// trivial. For this reason, we do not inline this method.
bool Systrace::isTracingEnabled(uint32_t tag) noexcept {
if (tag) {
setup();
return bool((sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) | SYSTRACE_TAG_ALWAYS) & tag);
uint32_t const mask = 1 << tag;
return bool(sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) & mask);
}
return false;
}