Compare commits

..

14 Commits

Author SHA1 Message Date
Syed Idris Shah
5d59459f90 Dawn change to enable writeBuffer for size != multiple of 4 2025-05-06 14:07:05 -04:00
bridgewaterrobbie
4249ff6cfb WebGPU: HelloTraingle hacks
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
2025-05-06 14:07:05 -04:00
bridgewaterrobbie
1076433fc0 Use variant layout label if available 2025-05-06 14:06:53 -04:00
bridgewaterrobbie
3bd0039ee9 Minimal labels implementation for descriptorset layout 2025-05-06 13:22:33 -04:00
Konrad Piascik
67b9c72442 Fix windows compile errors 2025-05-06 12:24:42 -04:00
Andy Hovingh
c1a8d73384 webgpu: legacy draw call should call draw2 2025-05-06 12:24:42 -04:00
Andy Hovingh
2d98ac878e webgpu: sets bind groups in the render pass 2025-05-06 12:24:42 -04:00
Andy Hovingh
35ab5f768d webgpu: depth texture and attachment 2025-05-06 12:24:42 -04:00
bridgewaterrobbie
705b3c39f4 WebGPU: Add Draw call, with temporary hack to achieve spinning triangle
Co-authored-by: Juan Caldas <juancaldas@google.com>
2025-05-06 12:24:42 -04:00
bridgewaterrobbie
56231db326 WebGPU: Set spec constants via string mapping
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
Co-authored-by: Juan Caldas <juancaldas@google.com>
2025-05-06 12:24:42 -04:00
bridgewaterrobbie
e97f7ef628 WebGPU: Render Target Implementation
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
Co-authored-by: Syed Idris Shah <idrisshah@google.com>
2025-05-06 12:24:42 -04:00
bridgewaterrobbie
4570c2e710 WebGPU: Sampler, Texture and Descriptor Set setup
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
2025-05-06 12:24:42 -04:00
bridgewaterrobbie
4759237e66 Separate out required and optional features for webgpu. Panic if any required features are not available 2025-05-06 12:24:42 -04:00
Matthew Hoffman
543b93939a Optional CMake flag for enabling ASAN for backend and its tests. (#8696) 2025-05-05 12:35:21 -05:00
33 changed files with 1317 additions and 619 deletions

View File

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

View File

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

View File

@@ -5,6 +5,18 @@ 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
# ==================================================================================================
@@ -472,6 +484,7 @@ 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)
@@ -482,6 +495,8 @@ 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}>
@@ -551,6 +566,8 @@ 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
@@ -589,6 +606,7 @@ 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)
@@ -598,6 +616,8 @@ 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

@@ -39,6 +39,7 @@
#include <stddef.h>
#include <stdint.h>
#include <utils/StaticString.h>
/**
* Types and enums used by filament's driver.
@@ -1139,6 +1140,7 @@ struct ExternalSamplerDatum {
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
std::variant<utils::StaticString, utils::CString, std::monostate> label;
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed

View File

@@ -149,13 +149,6 @@ public:
* - PlatformEGLAndroid
*/
bool assertNativeWindowIsValid = false;
/**
* The action to take if a Drawable cannot be acquired. If true, the
* frame is aborted instead of panic. This is only supported for:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
};
Platform() noexcept;

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

@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
return MetalDriverFactory::create(this, driverConfig);
}

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

@@ -27,6 +27,7 @@
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <sstream>
#include <string_view>
#include <vector>
@@ -125,21 +126,64 @@ namespace {
return module;
}
// This is a 1 to 1 mapping of the ReservedSpecializationConstants enum in EngineEnums.h
// The _hack is a workaround until https://issues.chromium.org/issues/42250586 is resolved
// This workaround is the same one being used on the generateSpecializationConstant() function
wgpu::StringView getSpecConstantStringId(uint32_t id) {
switch (id) {
case 0:
return "0";// BACKEND_FEATURE_LEVEL_hack
case 1:
return "1";// CONFIG_MAX_INSTANCES_hack
case 2:
return "2";// ONFIG_STATIC_TEXTURE_TARGET_WORKAROUND_hack
case 3:
return "3";// CONFIG_SRGB_SWAPCHAIN_EMULATION_hack
case 4:
return "4";// CONFIG_FROXEL_BUFFER_HEIGHT_hack
case 5:
return "5";// CONFIG_POWER_VR_SHADER_WORKAROUNDS_hack
case 6:
return "6";// CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP_hack
case 7:
return "7";// CONFIG_DEBUG_FROXEL_VISUALIZATION_hack
case 8:
return "8";// CONFIG_STEREO_EYE_COUNT_hack
case 9:
return "9";// CONFIG_SH_BANDS_COUNT_hack
case 10:
return "10";// CONFIG_SHADOW_SAMPLING_METHOD_hack
default:
PANIC_POSTCONDITION("Unknown/unhandled spec constant key/id: %d", id);
}
}
std::vector<wgpu::ConstantEntry> convertConstants(
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
constantsInfo) {
std::vector<wgpu::ConstantEntry> constants(constantsInfo.size());
for (size_t i = 0; i < constantsInfo.size(); i++) {
filament::backend::Program::SpecializationConstant const& specConstant = constantsInfo[i];
wgpu::ConstantEntry& constantEntry = constants[i];
constantEntry.key = wgpu::StringView(std::to_string(specConstant.id));
if (auto* v = std::get_if<int32_t>(&specConstant.value)) {
constantEntry.value = static_cast<double>(*v);
} else if (auto* f = std::get_if<float>(&specConstant.value)) {
constantEntry.value = static_cast<double>(*f);
} else if (auto* b = std::get_if<bool>(&specConstant.value)) {
constantEntry.value = *b ? 0.0 : 1.0;
std::vector<wgpu::ConstantEntry> constants;
constants.reserve(constantsInfo.size());
for (filament::backend::Program::SpecializationConstant const& constant: constantsInfo) {
// CONFIG_MAX_INSTANCES (1) and CONFIG_FROXEL_BUFFER_HEIGHT (4) will not be present
// as constant overrides in the generated WGSL, because WGSL doesn't support specialization
// constants as an array length
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
// CONFIG_SRGB_SWAPCHAIN_EMULATION (3) is being skipped all together since it's only
// included for the case of mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0, which should
// not be possible for WebGPU
if (constant.id == 1 || constant.id == 3 || constant.id == 4) {
continue;
}
double value = 0.0;
if (auto* v = std::get_if<int32_t>(&constant.value)) {
value = static_cast<double>(*v);
} else if (auto* f = std::get_if<float>(&constant.value)) {
value = static_cast<double>(*f);
} else if (auto* b = std::get_if<bool>(&constant.value)) {
value = *b ? 0.0 : 1.0;
}
constants.push_back(
wgpu::ConstantEntry{ .key = getSpecConstantStringId(constant.id), .value = value });
}
return constants;
}

View File

@@ -389,9 +389,7 @@ Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WGPUTexture>(); }
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
return allocHandle<WGPUProgram>();
@@ -410,7 +408,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
@@ -438,7 +436,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
@@ -450,15 +448,15 @@ Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcep
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
@@ -470,6 +468,8 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
mDevice, flags);
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."
@@ -512,30 +512,46 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {}
TextureUsage usage) {
constructHandle<WGPUTexture>(th, target, levels, format, samples, w, h, depth, usage, mDevice);
}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {}
uint8_t baseLevel, uint8_t levelCount) {
auto source = handleCast<WGPUTexture>(srch);
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {}
backend::TextureSwizzle a) {
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {}
Platform::ExternalImageHandleRef externalImage) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {}
void* externalImage) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {}
void* image, uint32_t plane) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage usage) {}
uint32_t depth, TextureUsage usage) {
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
@@ -575,7 +591,7 @@ 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->getLayoutSize());
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getBindGroupEntries());
}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
@@ -612,7 +628,7 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool WebGPUDriver::isTextureFormatSupported(TextureFormat format) {
return true;
return WGPUTexture::fToWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
}
bool WebGPUDriver::isTextureSwizzleSupported() {
@@ -760,8 +776,31 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
}
}
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
assert_invariant(mCommandEncoder);
auto* renderTarget = handleCast<WGPURenderTarget>(rth);
// if (renderTarget == mDefaultRenderTarget) {
// FWGPU_LOGW << "Default render target"
// << utils::io::endl;
// } else {
// FWGPU_LOGW << "Non Default render target"
// << utils::io::endl;
// }
wgpu::RenderPassDescriptor renderPassDescriptor2;
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
.view = mSwapChain->getDepthTextureView(),
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0
};
renderTarget->setUpRenderPassAttachments(renderPassDescriptor2, mTextureView, params);
renderPassDescriptor2.depthStencilAttachment = &depthStencilAttachment;
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
@@ -786,7 +825,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.timestampWrites = nullptr,
};
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor2);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
}
@@ -914,7 +953,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat());
// TODO: uncomment once we have a valid pipeline to set
// mRenderPassEncoder.SetPipeline(pipeline);
mRenderPassEncoder.SetPipeline(pipeline);
}
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -934,10 +973,18 @@ 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);
// 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);
}
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
uint32_t indexCount, uint32_t instanceCount) {
draw2(indexOffset, indexCount, instanceCount);
}
void WebGPUDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
@@ -973,34 +1020,156 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding, Handle<HwTexture> th, SamplerParams params) {
/*
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
auto texture = handleCast<WGPUTexture>(th);
// TODO very high odds badd assumptions are in here about handling HwTexture. Revisit with more
// understanding. Right now assuming there is a wgpu::TextureView filled in
if (!bindGroup->getIsLocked()) {
// Dawn will cache duplicate samplers, so we don't strictly need to maintain a cache.
// Making a cache might save us minor perf by reducing param translation
auto sampler = makeSampler(params);
// TODO making assumptions that size and offset mean the same thing here.
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
.textureView = texture->texView };
.textureView = texture->getTexView() };
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
.sampler = texture->sampler };
.sampler = sampler };
bindGroup->addEntry(sEntry.binding, std::move(sEntry));
}
//TODO Just the setup, this function stilll needs the rest of logic implemented
*/
}
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
// TODO: presume we need this, use it. Probably Encoder::SetBindGroup
auto wbg = bindGroup->lockAndReturn(mDevice);
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
const auto wbg = bindGroup->lockAndReturn(mDevice);
assert_invariant(mRenderPassEncoder);
// TODO is this how we should be getting the dynamic offsets?
// should we add offsets for unused entries or is the input already have them?
// this implementation assumes unused entries are not provided, and adds dummy values.
// The count also includes unused entities, as not doing so produces errors
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
uint32_t const* const dynamicOffsetsWithUnused = bindGroup->setDynamicOffsets(offsets.data());
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, dynamicOffsetsWithUnused);
}
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
wgpu::SamplerDescriptor desc;
desc.label = "TODO";
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
switch (params.filterMag) {
case SamplerMagFilter::NEAREST: {
desc.magFilter = wgpu::FilterMode::Nearest;
break;
}
case SamplerMagFilter::LINEAR: {
desc.magFilter = wgpu::FilterMode::Linear;
break;
}
}
switch (params.filterMin) {
case SamplerMinFilter::NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
// suffice
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
// suffice
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
}
switch (params.compareFunc) {
case SamplerCompareFunc::LE: {
desc.compare = wgpu::CompareFunction::LessEqual;
break;
}
case SamplerCompareFunc::GE: {
desc.compare = wgpu::CompareFunction::GreaterEqual;
break;
}
case SamplerCompareFunc::L: {
desc.compare = wgpu::CompareFunction::Less;
break;
}
case SamplerCompareFunc::G: {
desc.compare = wgpu::CompareFunction::Greater;
break;
}
case SamplerCompareFunc::E: {
desc.compare = wgpu::CompareFunction::Equal;
break;
}
case SamplerCompareFunc::NE: {
desc.compare = wgpu::CompareFunction::NotEqual;
break;
}
case SamplerCompareFunc::A: {
desc.compare = wgpu::CompareFunction::Always;
break;
}
case SamplerCompareFunc::N: {
desc.compare = wgpu::CompareFunction::Never;
break;
}
}
desc.maxAnisotropy = 1u << params.anisotropyLog2;
// Unused: Filament's compareMode, WGPU lodMinClamp/lodMaxClamp
//TODO Once we can properly map to descriptorsetlayout use the sampler.
return mDevice.CreateSampler(/*&desc*/);
}
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
switch (fWrapMode) {
case SamplerWrapMode::CLAMP_TO_EDGE: {
return wgpu::AddressMode::ClampToEdge;
break;
}
case SamplerWrapMode::REPEAT: {
return wgpu::AddressMode::Repeat;
break;
}
case SamplerWrapMode::MIRRORED_REPEAT: {
return wgpu::AddressMode::MirrorRepeat;
break;
}
}
return wgpu::AddressMode::Undefined;
}
} // namespace filament

View File

@@ -56,7 +56,8 @@ private:
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
template<typename GPUBufferObject>
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
uint32_t byteOffset) {

View File

@@ -16,7 +16,16 @@
#include "WebGPUHandles.h"
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstdint>
#include <utility>
#include <vector>
namespace {
constexpr wgpu::BufferUsage getBufferObjectUsage(
@@ -110,6 +119,44 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
}
}
wgpu::StringView getUserTextureLabel(filament::backend::SamplerType target) {
// TODO will be helpful to get more useful info than this
using filament::backend::SamplerType;
switch (target) {
case SamplerType::SAMPLER_2D:
return "a_2D_user_texture";
case SamplerType::SAMPLER_2D_ARRAY:
return "a_2D_array_user_texture";
case SamplerType::SAMPLER_CUBEMAP:
return "a_cube_map_user_texture";
case SamplerType::SAMPLER_EXTERNAL:
return "an_external_user_texture";
case SamplerType::SAMPLER_3D:
return "a_3D_user_texture";
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return "a_cube_mape_array_user_texture";
}
}
wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target) {
// TODO will be helpful to get more useful info than this
using filament::backend::SamplerType;
switch (target) {
case SamplerType::SAMPLER_2D:
return "a_2D_user_texture_view";
case SamplerType::SAMPLER_2D_ARRAY:
return "a_2D_array_user_texture_view";
case SamplerType::SAMPLER_CUBEMAP:
return "a_cube_map_user_texture_view";
case SamplerType::SAMPLER_EXTERNAL:
return "an_external_user_texture_view";
case SamplerType::SAMPLER_3D:
return "a_3D_user_texture_view";
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return "a_cube_mape_array_user_texture_view";
}
}
}// namespace
namespace filament::backend {
@@ -137,7 +184,7 @@ WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attribut
mAttributes[attrib.buffer].push_back({
.format = vertexFormat,
.offset = attrib.offset,
.shaderLocation = static_cast<uint32_t>(mAttributes[attrib.buffer].size()),
.shaderLocation = attribIndex,
});
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
@@ -192,11 +239,20 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
wgpu::Device const& device) {
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;
uint samplerCount =
unsigned int samplerCount =
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
return fEntry.type == DescriptorType::SAMPLER ||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
@@ -205,86 +261,204 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
wEntries.reserve(layout.bindings.size() + samplerCount);
mBindGroupEntries.reserve(wEntries.capacity());
for (auto fEntry: layout.bindings) {
auto& wEntry = wEntries.emplace_back();
auto& entryInfo = mBindGroupEntries.emplace_back();
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
wEntry.binding = fEntry.binding * 2;
entryInfo.binding = wEntry.binding;
switch (fEntry.type) {
// TODO Metal treats these the same. Is this fine?
case DescriptorType::SAMPLER_EXTERNAL:
case DescriptorType::SAMPLER: {
// Sampler binding is 2n+1 due to split.
auto& samplerEntry = wEntries.emplace_back();
auto& samplerEntryInfo = mBindGroupEntries.emplace_back();
samplerEntry.binding = fEntry.binding * 2 + 1;
samplerEntryInfo.binding = samplerEntry.binding;
samplerEntryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER;
samplerEntry.visibility = wEntry.visibility;
// We are simply hoping that undefined and defaults suffices here.
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
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)) {
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
} else {
wEntry.texture.viewDimension =
wgpu::TextureViewDimension::e2D;// Example default
}
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW;
break;
}
case DescriptorType::UNIFORM_BUFFER: {
wEntry.buffer.hasDynamicOffset =
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
entryInfo.hasDynamicOffset = wEntry.buffer.hasDynamicOffset;
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
// TODO: Ideally we fill minBindingSize
break;
}
case DescriptorType::INPUT_ATTACHMENT: {
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
PANIC_POSTCONDITION("Input Attachment is not supported");
break;
}
case DescriptorType::SHADER_STORAGE_BUFFER: {
// TODO: Vulkan does not support this, can we?
PANIC_POSTCONDITION("Shader storage is not supported");
break;
}
}
// Currently flags are only used to specify dynamic offset.
// UNUSED
// fEntry.count
// fEntry.count is unused currently
}
std::string label = "layout_" + baseLabel + std::to_string(++layoutNum) ;
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value.
.label{ "layout_" + std::to_string(++layoutNum) },
.label{label.c_str()}, // Use .c_str() if label needs to be const char*
.entryCount = wEntries.size(),
.entries = wEntries.data()
};
// TODO Do we need to defer this until we have more info on textures and samplers??
mLayoutSize = wEntries.size();
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
}
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
WebGPUDescriptorSet::WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize)
: mLayout(layout),
entries(layoutSize, wgpu::BindGroupEntry{}) {
// Establish the size of entries based on the layout. This should be reliable and efficient.
wgpu::Buffer WebGPUDescriptorSet::sDummyUniformBuffer = nullptr;
wgpu::Texture WebGPUDescriptorSet::sDummyTexture = nullptr;
wgpu::TextureView WebGPUDescriptorSet::sDummyTextureView = nullptr;
wgpu::Sampler WebGPUDescriptorSet::sDummySampler = nullptr;
void WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(wgpu::Device const& device,
wgpu::TextureFormat aColorFormat) {
if (!sDummyUniformBuffer) {
wgpu::BufferDescriptor bufferDescriptor{
.label = "dummy_uniform_not_to_be_used",
.usage = wgpu::BufferUsage::Uniform,
.size = 4
};
sDummyUniformBuffer = device.CreateBuffer(&bufferDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
<< "Failed to create dummy uniform buffer?";
}
if (!sDummyTexture || !sDummyTextureView) {
wgpu::TextureDescriptor textureDescriptor{
.label = "dummy_texture_not_to_be_used",
.usage = wgpu::TextureUsage::TextureBinding,
.dimension = wgpu::TextureDimension::e2D,
.size = wgpu::Extent3D{ .width = 4, .height = 4, .depthOrArrayLayers = 1 },
.format = aColorFormat,
};
if (!sDummyTexture) {
sDummyTexture = device.CreateTexture(&textureDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy texture?";
}
if (!sDummyTextureView) {
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = "dummy_texture_view_not_to_be_used"
};
sDummyTextureView = sDummyTexture.CreateView(&textureViewDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
<< "Failed to create dummy texture view?";
}
}
if (!sDummySampler) {
wgpu::SamplerDescriptor samplerDescriptor{
.label = "dummy_sampler_not_to_be_used"
};
sDummySampler = device.CreateSampler(&samplerDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy sampler?";
}
}
std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedByBinding(
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&
bindGroupEntries) {
assert_invariant(WebGPUDescriptorSet::sDummyUniformBuffer &&
"Dummy uniform buffer must have been created before "
"creating dummy bind group entries.");
assert_invariant(
WebGPUDescriptorSet::sDummyTexture &&
"Dummy texture must have been created before creating dummy bind group entries.");
assert_invariant(
WebGPUDescriptorSet::sDummyTextureView &&
"Dummy texture view must have been created before creating dummy bind group entries.");
assert_invariant(
WebGPUDescriptorSet::sDummySampler &&
"Dummy sampler must have been created before creating dummy bind group entries.");
using filament::backend::WebGPUDescriptorSetLayout;
std::vector<wgpu::BindGroupEntry> entries;
entries.reserve(bindGroupEntries.size());
for (auto const& entryInfo: bindGroupEntries) {
auto& entry = entries.emplace_back();
entry.binding = entryInfo.binding;
switch (entryInfo.type) {
case WebGPUDescriptorSetLayout::BindGroupEntryType::UNIFORM_BUFFER:
entry.buffer = WebGPUDescriptorSet::sDummyUniformBuffer;
break;
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW:
entry.textureView = WebGPUDescriptorSet::sDummyTextureView;
break;
case WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER:
entry.sampler = WebGPUDescriptorSet::sDummySampler;
break;
}
}
std::sort(entries.begin(), entries.end(),
[](wgpu::BindGroupEntry const& a, wgpu::BindGroupEntry const& b) {
return a.binding < b.binding;
});
return entries;
}
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
: 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());
for (size_t i = 0; i < mEntryIndexByBinding.size(); i++) {
mEntryIndexByBinding[i] = INVALID_INDEX;
}
for (size_t index = 0; index < mEntriesSortedByBinding.size(); index++) {
wgpu::BindGroupEntry const& entry = mEntriesSortedByBinding[index];
assert_invariant(entry.binding < mEntryIndexByBinding.size());
mEntryIndexByBinding[entry.binding] = static_cast<uint8_t>(index);
}
for (auto const& entry : bindGroupEntries) {
if (entry.hasDynamicOffset) {
assert_invariant(entry.binding < mEntriesByBindingWithDynamicOffsets.size());
mEntriesByBindingWithDynamicOffsets[entry.binding] = true;
}
}
mDynamicOffsets.reserve(mEntriesSortedByBinding.size());
}
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
mBindGroup = nullptr;
mLayout = nullptr;
}
WebGPUDescriptorSet::~WebGPUDescriptorSet() {}
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
if (mBindGroup) {
return mBindGroup;
}
// TODO label? Should we just copy layout label?
wgpu::BindGroupDescriptor desc{ .layout = mLayout,
.entryCount = entries.size(),
.entries = entries.data() };
wgpu::BindGroupDescriptor desc{
.layout = mLayout,
.entryCount = mEntriesSortedByBinding.size(),
.entries = mEntriesSortedByBinding.data()
};
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;
}
void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
void WebGPUDescriptorSet::addEntry(unsigned int index, wgpu::BindGroupEntry&& entry) {
if (mBindGroup) {
// We will keep getting hits from future updates, but shouldn't do anything
// Filament guarantees this won't change after things have locked.
@@ -292,6 +466,490 @@ void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
}
// TODO: Putting some level of trust that Filament is not going to reuse indexes or go past the
// layout index for efficiency. Add guards if wrong.
entries[index] = std::move(entry);
FILAMENT_CHECK_POSTCONDITION(index < mEntryIndexByBinding.size())
<< "impossible/invalid index for a descriptor/binding (our of range or >= "
"MAX_DESCRIPTOR_COUNT) "
<< index;
uint8_t entryIndex = mEntryIndexByBinding[index];
FILAMENT_CHECK_POSTCONDITION(
entryIndex != INVALID_INDEX && entryIndex < mEntriesSortedByBinding.size())
<< "Invalid binding " << index;
entry.binding = index;
mEntriesSortedByBinding[entryIndex] = std::move(entry);
mEntriesByBindingAdded[index] = true;
}
uint32_t const* WebGPUDescriptorSet::setDynamicOffsets(uint32_t const* offsets) {
// mDynamicOffsets already reserves enough memory for the number of entries in the set
mDynamicOffsets.clear();
// this implementation copies the offsets to mDynamicOffsets, but also adds values for
// unused entries TODO: is this necessary?
size_t inputIndex = 0;
size_t outputIndex = 0;
for (auto const& entry : mEntriesSortedByBinding) {
if (mEntriesByBindingWithDynamicOffsets[entry.binding]) {
if (mEntriesByBindingAdded[entry.binding]) {
mDynamicOffsets[outputIndex++] = offsets[inputIndex++];
} else {
mDynamicOffsets[outputIndex++] = 0; // dummy offset, as it was never added
}
}
}
return mDynamicOffsets.data();
}
size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
return mEntriesByBindingWithDynamicOffsets.count();
}
// From createTextureR
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device const& device) noexcept {
assert_invariant(
samples == 1 ||
samples == 4 &&
"An invalid number of samples were requested, as WGPU requires the sample "
"count to either be 1 (no multisampling) or 4, at least as of April 2025 of "
"the spec. See https://www.w3.org/TR/webgpu/#texture-creation or "
"https://gpuweb.github.io/gpuweb/#multisample-state");
// First, the texture aspect, starting with the defaults/basic configuration
mUsage = fToWGPUTextureUsage(usage);
mFormat = fToWGPUTextureFormat(format);
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(target),
.usage = mUsage,
.dimension = target == SamplerType::SAMPLER_3D ? wgpu::TextureDimension::e3D
: wgpu::TextureDimension::e2D,
.size = { .width = width, .height = height, .depthOrArrayLayers = depth },
.format = mFormat,
.mipLevelCount = levels,
.sampleCount = samples,
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from
// Filament
.viewFormatCount = 0,
.viewFormats = nullptr,
};
// adjust for specific cases
switch (target) {
case SamplerType::SAMPLER_2D:
mArrayLayerCount = 1;
break;
case SamplerType::SAMPLER_2D_ARRAY:
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
break;
case SamplerType::SAMPLER_CUBEMAP:
textureDescriptor.size.depthOrArrayLayers = 6;
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
break;
case SamplerType::SAMPLER_EXTERNAL:
case SamplerType::SAMPLER_3D:
mArrayLayerCount = 1;
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
textureDescriptor.size.depthOrArrayLayers = depth * 6;
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
break;
}
assert_invariant(textureDescriptor.format != wgpu::TextureFormat::Undefined &&
"Could not find appropriate WebGPU format");
mTexture = device.CreateTexture(&textureDescriptor);
FILAMENT_CHECK_POSTCONDITION(mTexture)
<< "Failed to create texture for " << textureDescriptor.label;
// Second, the texture view aspect
mTexView = makeTextureView(0, levels, target);
}
// From createTextureViewR
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
mTexture = src->mTexture;
mTexView = makeTextureView(baseLevel, levelCount, target);
}
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
// TODO Validate assumptions, revisit if issues.
if (any(TextureUsage::BLIT_SRC & fUsage)) {
retUsage |= wgpu::TextureUsage::CopySrc;
}
if (any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
retUsage |= wgpu::TextureUsage::CopyDst;
}
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
retUsage |= wgpu::TextureUsage::TextureBinding;
}
// WGPU Render attachment covers either color or stencil situation dependant
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
// issues, investigate here.
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
TextureUsage::DEPTH_ATTACHMENT) &
fUsage)) {
retUsage |= wgpu::TextureUsage::RenderAttachment;
}
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
// benefit though
const bool useTransientAttachment =
// Usage consists of attachment flags only.
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
// Depth resolve cannot use transient attachment because it uses a custom shader.
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
// restriction.
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
// and above to be able to support stencil resolve (along with depth).
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
if (useTransientAttachment) {
retUsage |= wgpu::TextureUsage::TransientAttachment;
}
// NOTE: Unused wgpu flags:
// StorageBinding
// StorageAttachment
// NOTE: Unused Filament flags:
// SUBPASS_INPUT VK goes to input attachment which we don't support right now
// PROTECTED
return retUsage;
}
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
switch (fUsage) {
case filament::backend::TextureFormat::R8:
return wgpu::TextureFormat::R8Unorm;
case filament::backend::TextureFormat::R8_SNORM:
return wgpu::TextureFormat::R8Snorm;
case filament::backend::TextureFormat::R8UI:
return wgpu::TextureFormat::R8Uint;
case filament::backend::TextureFormat::R8I:
return wgpu::TextureFormat::R8Sint;
case filament::backend::TextureFormat::STENCIL8:
return wgpu::TextureFormat::Stencil8;
case filament::backend::TextureFormat::R16F:
return wgpu::TextureFormat::R16Float;
case filament::backend::TextureFormat::R16UI:
return wgpu::TextureFormat::R16Uint;
case filament::backend::TextureFormat::R16I:
return wgpu::TextureFormat::R16Sint;
case filament::backend::TextureFormat::RG8:
return wgpu::TextureFormat::RG8Unorm;
case filament::backend::TextureFormat::RG8_SNORM:
return wgpu::TextureFormat::RG8Snorm;
case filament::backend::TextureFormat::RG8UI:
return wgpu::TextureFormat::RG8Uint;
case filament::backend::TextureFormat::RG8I:
return wgpu::TextureFormat::RG8Sint;
case filament::backend::TextureFormat::R32F:
return wgpu::TextureFormat::R32Float;
case filament::backend::TextureFormat::R32UI:
return wgpu::TextureFormat::R32Uint;
case filament::backend::TextureFormat::R32I:
return wgpu::TextureFormat::R32Sint;
case filament::backend::TextureFormat::RG16F:
return wgpu::TextureFormat::RG16Float;
case filament::backend::TextureFormat::RG16UI:
return wgpu::TextureFormat::RG16Uint;
case filament::backend::TextureFormat::RG16I:
return wgpu::TextureFormat::RG16Sint;
case filament::backend::TextureFormat::RGBA8:
return wgpu::TextureFormat::RGBA8Unorm;
case filament::backend::TextureFormat::SRGB8_A8:
return wgpu::TextureFormat::RGBA8UnormSrgb;
case filament::backend::TextureFormat::RGBA8_SNORM:
return wgpu::TextureFormat::RGBA8Snorm;
case filament::backend::TextureFormat::RGBA8UI:
return wgpu::TextureFormat::RGBA8Uint;
case filament::backend::TextureFormat::RGBA8I:
return wgpu::TextureFormat::RGBA8Sint;
case filament::backend::TextureFormat::DEPTH16:
return wgpu::TextureFormat::Depth16Unorm;
case filament::backend::TextureFormat::DEPTH24:
return wgpu::TextureFormat::Depth24Plus;
case filament::backend::TextureFormat::DEPTH32F:
return wgpu::TextureFormat::Depth32Float;
case filament::backend::TextureFormat::DEPTH24_STENCIL8:
return wgpu::TextureFormat::Depth24PlusStencil8;
case filament::backend::TextureFormat::DEPTH32F_STENCIL8:
return wgpu::TextureFormat::Depth32FloatStencil8;
case filament::backend::TextureFormat::RG32F:
return wgpu::TextureFormat::RG32Float;
case filament::backend::TextureFormat::RG32UI:
return wgpu::TextureFormat::RG32Uint;
case filament::backend::TextureFormat::RG32I:
return wgpu::TextureFormat::RG32Sint;
case filament::backend::TextureFormat::RGBA16F:
return wgpu::TextureFormat::RGBA16Float;
case filament::backend::TextureFormat::RGBA16UI:
return wgpu::TextureFormat::RGBA16Uint;
case filament::backend::TextureFormat::RGBA16I:
return wgpu::TextureFormat::RGBA16Sint;
case filament::backend::TextureFormat::RGBA32F:
return wgpu::TextureFormat::RGBA32Float;
case filament::backend::TextureFormat::RGBA32UI:
return wgpu::TextureFormat::RGBA32Uint;
case filament::backend::TextureFormat::RGBA32I:
return wgpu::TextureFormat::RGBA32Sint;
case filament::backend::TextureFormat::EAC_R11:
return wgpu::TextureFormat::EACR11Unorm;
case filament::backend::TextureFormat::EAC_R11_SIGNED:
return wgpu::TextureFormat::EACR11Snorm;
case filament::backend::TextureFormat::EAC_RG11:
return wgpu::TextureFormat::EACRG11Unorm;
case filament::backend::TextureFormat::EAC_RG11_SIGNED:
return wgpu::TextureFormat::EACRG11Snorm;
case filament::backend::TextureFormat::ETC2_RGB8:
return wgpu::TextureFormat::ETC2RGB8Unorm;
case filament::backend::TextureFormat::ETC2_SRGB8:
return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
case filament::backend::TextureFormat::ETC2_RGB8_A1:
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
case filament::backend::TextureFormat::ETC2_SRGB8_A1:
return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
case filament::backend::TextureFormat::ETC2_EAC_RGBA8:
return wgpu::TextureFormat::ETC2RGBA8Unorm;
case filament::backend::TextureFormat::ETC2_EAC_SRGBA8:
return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_4x4:
return wgpu::TextureFormat::ASTC4x4Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
return wgpu::TextureFormat::ASTC4x4UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_5x4:
return wgpu::TextureFormat::ASTC5x4Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
return wgpu::TextureFormat::ASTC5x4UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_5x5:
return wgpu::TextureFormat::ASTC5x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
return wgpu::TextureFormat::ASTC5x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_6x5:
return wgpu::TextureFormat::ASTC6x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
return wgpu::TextureFormat::ASTC6x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_6x6:
return wgpu::TextureFormat::ASTC6x6Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
return wgpu::TextureFormat::ASTC6x6UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_8x5:
return wgpu::TextureFormat::ASTC8x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
return wgpu::TextureFormat::ASTC8x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_8x6:
return wgpu::TextureFormat::ASTC8x6Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
return wgpu::TextureFormat::ASTC8x6UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_8x8:
return wgpu::TextureFormat::ASTC8x8Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
return wgpu::TextureFormat::ASTC8x8UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x5:
return wgpu::TextureFormat::ASTC10x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
return wgpu::TextureFormat::ASTC10x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x6:
return wgpu::TextureFormat::ASTC10x6Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
return wgpu::TextureFormat::ASTC10x6UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x8:
return wgpu::TextureFormat::ASTC10x8Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
return wgpu::TextureFormat::ASTC10x8UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x10:
return wgpu::TextureFormat::ASTC10x10Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
return wgpu::TextureFormat::ASTC10x10UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_12x10:
return wgpu::TextureFormat::ASTC12x10Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
return wgpu::TextureFormat::ASTC12x10UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_12x12:
return wgpu::TextureFormat::ASTC12x12Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return wgpu::TextureFormat::ASTC12x12UnormSrgb;
case filament::backend::TextureFormat::RED_RGTC1:
return wgpu::TextureFormat::BC4RUnorm;
case filament::backend::TextureFormat::SIGNED_RED_RGTC1:
return wgpu::TextureFormat::BC4RSnorm;
case filament::backend::TextureFormat::RED_GREEN_RGTC2:
return wgpu::TextureFormat::BC5RGUnorm;
case filament::backend::TextureFormat::SIGNED_RED_GREEN_RGTC2:
return wgpu::TextureFormat::BC5RGSnorm;
case filament::backend::TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
return wgpu::TextureFormat::BC6HRGBUfloat;
case filament::backend::TextureFormat::RGB_BPTC_SIGNED_FLOAT:
return wgpu::TextureFormat::BC6HRGBFloat;
case filament::backend::TextureFormat::RGBA_BPTC_UNORM:
return wgpu::TextureFormat::BC7RGBAUnorm;
case filament::backend::TextureFormat::SRGB_ALPHA_BPTC_UNORM:
return wgpu::TextureFormat::BC7RGBAUnormSrgb;
case filament::backend::TextureFormat::RGB565:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and discard the alpha and lower precision.
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGB9_E5:
return wgpu::TextureFormat::RGB9E5Ufloat;
case filament::backend::TextureFormat::RGB5_A1:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and handle the packing/unpacking in shaders.
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGBA4:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and handle the packing/unpacking in shaders.
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGB8:
// No direct sRGB equivalent in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Unorm;
case filament::backend::TextureFormat::SRGB8:
// No direct sRGB equivalent in wgpu without alpha.
return wgpu::TextureFormat::RGBA8UnormSrgb;
case filament::backend::TextureFormat::RGB8_SNORM:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Snorm;
case filament::backend::TextureFormat::RGB8UI:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Uint;
case filament::backend::TextureFormat::RGB8I:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Sint;
case filament::backend::TextureFormat::R11F_G11F_B10F:
return wgpu::TextureFormat::RG11B10Ufloat;
case filament::backend::TextureFormat::UNUSED:
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGB10_A2:
return wgpu::TextureFormat::RGB10A2Unorm;
case filament::backend::TextureFormat::RGB16F:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA16Float;
case filament::backend::TextureFormat::RGB16UI:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA16Uint;
case filament::backend::TextureFormat::RGB16I:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA16Sint;
case filament::backend::TextureFormat::RGB32F:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA32Float;
case filament::backend::TextureFormat::RGB32UI:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA32Uint;
case filament::backend::TextureFormat::RGB32I:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA32Sint;
case filament::backend::TextureFormat::DXT1_RGB:
return wgpu::TextureFormat::BC1RGBAUnorm;
case filament::backend::TextureFormat::DXT1_RGBA:
return wgpu::TextureFormat::BC1RGBAUnorm;
case filament::backend::TextureFormat::DXT3_RGBA:
return wgpu::TextureFormat::BC2RGBAUnorm;
case filament::backend::TextureFormat::DXT5_RGBA:
return wgpu::TextureFormat::BC3RGBAUnorm;
case filament::backend::TextureFormat::DXT1_SRGB:
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
case filament::backend::TextureFormat::DXT1_SRGBA:
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
case filament::backend::TextureFormat::DXT3_SRGBA:
return wgpu::TextureFormat::BC2RGBAUnormSrgb;
case filament::backend::TextureFormat::DXT5_SRGBA:
return wgpu::TextureFormat::BC3RGBAUnormSrgb;
}
}
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
SamplerType target) {
// starting with the defaults/basic configuration
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = getUserTextureViewLabel(target),
.format = mFormat,
// dimension depends on target and is set below
.baseMipLevel = baseLevel,
.mipLevelCount = levelCount,
// baseArrayLayer is required, making a guess
.baseArrayLayer = 0,
.arrayLayerCount = mArrayLayerCount,
// Have not found an analog to aspect in other drivers, but ALL should be unrestrictive.
// TODO Can we make this better?
.aspect = wgpu::TextureAspect::All,
.usage = mUsage
};
// adjust for specific cases
switch (target) {
case SamplerType::SAMPLER_2D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
break;
case SamplerType::SAMPLER_2D_ARRAY:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray;
break;
case SamplerType::SAMPLER_CUBEMAP:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube;
break;
case SamplerType::SAMPLER_EXTERNAL:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
break;
case SamplerType::SAMPLER_3D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e3D;
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::CubeArray;
break;
}
wgpu::TextureView textureView = mTexture.CreateView(&textureViewDescriptor);
FILAMENT_CHECK_POSTCONDITION(textureView)
<< "Failed to create texture view " << textureViewDescriptor.label;
return textureView;
}
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
auto result = color[index];
if (index == 0 && defaultRenderTarget) {
}
return result;
}
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
TargetBufferFlags buffer) {
auto clearFlags = params.flags.clear;
auto discardStartFlags = params.flags.discardStart;
if (any(clearFlags & buffer)) {
return wgpu::LoadOp::Clear;
} else if (any(discardStartFlags & buffer)) {
return wgpu::LoadOp::Clear;
}
return wgpu::LoadOp::Load;
}
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
TargetBufferFlags buffer) {
const auto discardEndFlags = params.flags.discardEnd;
if (any(discardEndFlags & buffer)) {
return wgpu::StoreOp::Discard;
}
return wgpu::StoreOp::Store;
}
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params) {
// auto discardFlags = params.flags.discardEnd;
// (void) discardFlags;
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
colorAttachments.clear();
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) {
// auto attachment = getDrawColorAttachment(i);
// if (attachment) {
wgpu::RenderPassColorAttachment colorAttachment;
colorAttachment.view = textureView;
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i));
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a };
colorAttachments.emplace_back(colorAttachment);
// }
}
descriptor.colorAttachments = colorAttachments.data();
descriptor.colorAttachmentCount = colorAttachments.size();
descriptor.depthStencilAttachment = nullptr;
descriptor.timestampWrites = nullptr;
}
}// namespace filament::backend

View File

@@ -27,6 +27,8 @@
#include <webgpu/webgpu_cpp.h>
#include <array>
#include <bitset>
#include <cstdint>
#include <vector>
@@ -98,80 +100,124 @@ struct WGPUBufferObject : HwBufferObject {
wgpu::Buffer buffer = nullptr;
const BufferObjectBinding bufferObjectBinding;
};
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
public:
enum class BindGroupEntryType : uint8_t {
UNIFORM_BUFFER,
TEXTURE_VIEW,
SAMPLER
};
struct BindGroupEntryInfo final {
uint8_t binding = 0;
BindGroupEntryType type = BindGroupEntryType::UNIFORM_BUFFER;
bool hasDynamicOffset = false;
};
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
~WebGPUDescriptorSetLayout();
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
[[nodiscard]] uint getLayoutSize() const { return mLayoutSize; }
[[nodiscard]] std::vector<BindGroupEntryInfo> const& getBindGroupEntries() const {
return mBindGroupEntries;
}
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);
uint mLayoutSize;
std::vector<BindGroupEntryInfo> mBindGroupEntries;
wgpu::BindGroupLayout mLayout;
};
class WebGPUDescriptorSet final : public HwDescriptorSet {
public:
WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize);
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
wgpu::TextureFormat aColorFormat);
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
~WebGPUDescriptorSet();
wgpu::BindGroup lockAndReturn(wgpu::Device const& device);
void addEntry(uint index, wgpu::BindGroupEntry&& entry);
wgpu::BindGroup lockAndReturn(wgpu::Device const&);
void addEntry(unsigned int index, wgpu::BindGroupEntry&& entry);
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
private:
static wgpu::Buffer sDummyUniformBuffer;
static wgpu::Texture sDummyTexture;
static wgpu::TextureView sDummyTextureView;
static wgpu::Sampler sDummySampler;
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);
// 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;
std::vector<wgpu::BindGroupEntry> entries;
wgpu::BindGroup mBindGroup;
wgpu::BindGroupLayout mLayout = nullptr;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets {};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded {};
std::vector<uint32_t> mDynamicOffsets;
wgpu::BindGroup mBindGroup = nullptr;
};
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUTexture : public HwTexture {
class WGPUTexture : public HwTexture {
public:
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device const& device) noexcept;
// constructors for creating texture views
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
wgpu::Texture texture = nullptr;
// TODO: Adding this but not yet setting it up. Filament "Textures" are combined image samplers,
// rep both.
wgpu::Sampler sampler = nullptr;
//TODO: Not sure all the ways HwTexture is used. Overloading like this might be entirely wrong.
wgpu::TextureView texView = nullptr;
const wgpu::Texture& getTexture() const { return mTexture; }
const wgpu::TextureView& getTexView() const { return mTexView; }
// Public to allow checking for support of a texture format
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
private:
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
SamplerType target);
// CreateTextureR has info for a texture and sampler. Texture Views are needed for binding,
// along with a sampler Current plan: Inherit the sampler and Texture to always exist (It is a
// ref counted pointer) when making views. View is optional
wgpu::Texture mTexture = nullptr;
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
uint32_t mArrayLayerCount = 1;
wgpu::TextureView mTexView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPURenderPrimitive() {}
void setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
WGPUVertexBuffer* vertexBuffer = nullptr;
WGPUIndexBuffer* indexBuffer = nullptr;
};
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPURenderTarget : public HwRenderTarget {
class WGPURenderTarget : public HwRenderTarget {
public:
class Attachment {
public:
friend struct WGPURenderTarget;
friend class WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->texture),
texture(gpuTexture->getTexture()),
mWGPUTexture(gpuTexture) {}
operator bool() const {
return mWGPUTexture != nullptr;
}
uint8_t level = 0;
uint16_t layer = 0;
@@ -187,8 +233,8 @@ struct WGPURenderTarget : public HwRenderTarget {
: HwRenderTarget(0, 0),
defaultRenderTarget(true) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
const RenderPassParams& params);
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params);
math::uint2 getAttachmentSize() noexcept;
@@ -198,15 +244,15 @@ struct WGPURenderTarget : public HwRenderTarget {
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
private:
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments {};
};
}// namespace filament::backend

View File

@@ -235,6 +235,7 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
fragmentState.constantCount = program.constants.size(),
fragmentState.constants = program.constants.data(),
fragmentState.targetCount = 1; // TODO need to get this from the render target
fragmentState.targets = colorTargets.data();
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
auto& colorTarget = colorTargets[targetIndex];

View File

@@ -105,7 +105,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
}
#endif
constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
[[nodiscard]] constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
const std::array expectedColorFormats =
useSRGBColorSpace ?
@@ -123,7 +123,7 @@ constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
return *firstFoundColorFormat;
}
constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
[[nodiscard]] constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
bool needStencil) {
if (needStencil) {
if (depth32FloatStencil8Enabled) {
@@ -137,7 +137,7 @@ constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled
}
}
constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
[[nodiscard]] constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
wgpu::PresentMode const* availablePresentModes) {
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
@@ -151,7 +151,7 @@ constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
return desiredPresentMode;
}
constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
[[nodiscard]] constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
wgpu::CompositeAlphaMode const* availableAlphaModes) {
bool autoAvailable = false;
bool inheritAvailable = false;
@@ -222,13 +222,58 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
config.alphaMode = selectAlphaMode(capabilities.alphaModeCount, capabilities.alphaModes);
}
[[nodiscard]] wgpu::Texture createDepthTexture(wgpu::Device const& device,
wgpu::Extent2D const& extent, wgpu::TextureFormat depthFormat) {
wgpu::TextureDescriptor descriptor{ .label = "depth_texture",
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
.dimension = wgpu::TextureDimension::e2D,
.size = { .width = extent.width, .height = extent.height, .depthOrArrayLayers = 1 },
.format = depthFormat,
.mipLevelCount = 1,
.sampleCount = 1,
.viewFormatCount = 1,
.viewFormats = &depthFormat
};
wgpu::Texture depthTexture = device.CreateTexture(&descriptor);
FILAMENT_CHECK_POSTCONDITION(depthTexture) << "Failed to create depth texture with width "
<< extent.width << " and height " << extent.height;
return depthTexture;
}
[[nodiscard]] wgpu::TextureView createDepthTextureView(wgpu::Texture const& depthTexture,
wgpu::TextureFormat depthFormat, bool needStencil) {
wgpu::TextureViewDescriptor descriptor{
.label = "depth_texture_view",
.format = depthFormat,
.dimension = wgpu::TextureViewDimension::e2D,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.arrayLayerCount = 1,
.aspect = wgpu::TextureAspect::DepthOnly,
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment
};
if (needStencil) {
descriptor.aspect = wgpu::TextureAspect::All;
}
wgpu::TextureView depthTextureView = depthTexture.CreateView(&descriptor);
FILAMENT_CHECK_POSTCONDITION(depthTextureView) << "Failed to create depth texture view";
return depthTextureView;
}
}// namespace
namespace filament::backend {
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags)
: mSurface(surface) {
: mDevice(device),
mSurface(surface),
mNeedStencil((flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0),
mDepthFormat(selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
mNeedStencil)),
mDepthTexture(createDepthTexture(device, surfaceSize, mDepthFormat)),
mDepthTextureView(createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil)) {
wgpu::SurfaceCapabilities capabilities = {};
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
@@ -238,19 +283,14 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const&
#endif
}
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
const bool needStencil = (flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
mDepthFormat = selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
needStencil);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig, mDepthFormat);
#endif
mSurface.Configure(&mConfig);
}
WebGPUSwapChain::~WebGPUSwapChain() {
mSurface.Unconfigure();
}
WebGPUSwapChain::~WebGPUSwapChain() { mSurface.Unconfigure(); }
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
@@ -267,6 +307,8 @@ void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
// TODO we may need to ensure no surface texture is in flight when we do this. some
// synchronization may be necessary
mSurface.Configure(&mConfig);
mDepthTexture = createDepthTexture(mDevice, currentSurfaceSize, mDepthFormat);
mDepthTextureView = createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil);
}
}

View File

@@ -38,14 +38,20 @@ public:
[[nodiscard]] wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
void present();
private:
void setExtent(wgpu::Extent2D const&);
wgpu::Device mDevice = nullptr;
wgpu::Surface mSurface = {};
wgpu::SurfaceConfiguration mConfig = {};
bool mNeedStencil = false;
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
wgpu::Texture mDepthTexture = nullptr;
wgpu::TextureView mDepthTextureView = nullptr;
};
} // namespace filament::backend

View File

@@ -99,22 +99,45 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
// TODO consider passing limits
constexpr std::array desiredFeatures = {
wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8,
wgpu::FeatureName::CoreFeaturesAndLimits };
std::vector<wgpu::FeatureName> requiredFeatures;
requiredFeatures.reserve(desiredFeatures.size());
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
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, desiredFeatures.begin(),
desiredFeatures.end(), std::back_inserter(requiredFeatures));
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));
wgpu::DeviceDescriptor deviceDescriptor{};
deviceDescriptor.label = "graphics_device";
deviceDescriptor.defaultQueue.label = "default_queue";
deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
deviceDescriptor.requiredFeatures = requiredFeatures.data();
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
deviceDescriptor.requiredFeatures = enabledFeatures.data();
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
wgpu::StringView message) {

View File

@@ -317,15 +317,6 @@ public:
*/
size_t metalUploadBufferSizeBytes = 512 * 1024;
/**
* The action to take if a Drawable cannot be acquired.
*
* Each frame rendered requires a CAMetalDrawable texture, which is
* presented on-screen at the completion of each frame. These are
* limited and provided round-robin style by the system.
*/
bool metalDisablePanicOnDrawableFailure = false;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.

View File

@@ -138,7 +138,6 @@ Engine* FEngine::create(Builder const& builder) {
.forceGLES2Context = instance->getConfig().forceGLES2Context,
.stereoscopicType = instance->getConfig().stereoscopicType,
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
};
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
@@ -734,7 +733,6 @@ int FEngine::loop() {
.forceGLES2Context = mConfig.forceGLES2Context,
.stereoscopicType = mConfig.stereoscopicType,
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);

View File

@@ -1140,6 +1140,7 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* const parser) {
UTILS_UNUSED_IN_RELEASE bool success;
success = parser->getDescriptorBindings(&mProgramDescriptorBindings);
assert_invariant(success);
@@ -1147,6 +1148,22 @@ void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* con
success = parser->getDescriptorSetLayout(&descriptorSetLayout);
assert_invariant(success);
// Setup Labels for debugging
utils::CString namestr;
utils::StaticString perViewStr = "_perView";
utils::StaticString singleStr = "_single";
parser->getName(&namestr);
utils::CString singleLabel(namestr.c_str(), namestr.length() + singleStr.length());
memccpy(singleLabel.c_str() + namestr.length(), singleStr.c_str(), '\0', singleStr.length());
utils::CString perViewLabel(namestr.c_str(), namestr.length() + perViewStr.length());
memccpy(perViewLabel.c_str() + namestr.length(), perViewStr.c_str(), '\0', perViewStr.length());
descriptorSetLayout[0].label = namestr;
descriptorSetLayout[1].label = perViewLabel;
mDescriptorSetLayout = {
engine.getDescriptorSetLayoutFactory(),
engine.getDriverApi(), std::move(descriptorSetLayout[0]) };

View File

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

View File

@@ -33,11 +33,13 @@ namespace filament::descriptor_sets {
using namespace backend;
static DescriptorSetLayout const postProcessDescriptorSetLayout{{
static DescriptorSetLayout const postProcessDescriptorSetLayout{
utils::StaticString("postProcess"),{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
}};
static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
static DescriptorSetLayout const depthVariantDescriptorSetLayout{
utils::StaticString("depthVariant"),{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
}};
@@ -46,14 +48,16 @@ static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
// 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 DescriptorSetLayout const ssrVariantDescriptorSetLayout{{
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{
utils::StaticString("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 DescriptorSetLayout perViewDescriptorSetLayout = {{
static DescriptorSetLayout perViewDescriptorSetLayout = {
utils::StaticString("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,7 +72,8 @@ static DescriptorSetLayout perViewDescriptorSetLayout = {{
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
}};
static DescriptorSetLayout perRenderableDescriptorSetLayout = {{
static DescriptorSetLayout perRenderableDescriptorSetLayout = {
utils::StaticString("perRenderable"),{
{ 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 },

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

@@ -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;
}

View File

@@ -229,10 +229,10 @@ MaybeError ValidateWriteBuffer(const DeviceBase* device,
uint64_t size) {
DAWN_TRY(device->ValidateObject(buffer));
DAWN_INVALID_IF(bufferOffset % 4 != 0, "BufferOffset (%u) is not a multiple of 4.",
bufferOffset);
DAWN_INVALID_IF(size % 4 != 0, "Size (%u) is not a multiple of 4.", size);
// DAWN_INVALID_IF(bufferOffset % 4 != 0, "BufferOffset (%u) is not a multiple of 4.",
// bufferOffset);
//
// DAWN_INVALID_IF(size % 4 != 0, "Size (%u) is not a multiple of 4.", size);
uint64_t bufferSize = buffer->GetSize();
DAWN_INVALID_IF(bufferOffset > bufferSize || size > (bufferSize - bufferOffset),

View File

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