Compare commits

...

24 Commits

Author SHA1 Message Date
Andy Hovingh
be9396b2d6 Merge branch 'main' into ebridgewater/RequiredFeatures 2025-05-07 13:44:41 -05:00
Juan David Caldas
de7bcd2df7 explicitly set bindings 2025-05-07 14:39:32 -04:00
bridgewaterrobbie
6adf140fb4 Simple and possibly incomplete pipeline hashing 2025-05-07 14:39:32 -04:00
Syed Idris Shah
b94c802076 Dawn change to enable writeBuffer for size != multiple of 4 2025-05-07 14:39:32 -04:00
bridgewaterrobbie
b231de0b5b WebGPU: HelloTraingle hacks
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
2025-05-07 14:39:32 -04:00
bridgewaterrobbie
fca200d549 Use variant layout label if available 2025-05-07 14:39:32 -04:00
bridgewaterrobbie
ba1d3f6c76 Minimal labels implementation for descriptorset layout 2025-05-07 14:39:32 -04:00
bridgewaterrobbie
7734fd4ad9 Add CString Append function 2025-05-07 13:34:00 -04:00
Mathias Agopian
7e6839f535 remove dependence on per-view descset layout from filamat
I've been going back and forth on whether we information about the
per-view descriptor set layout should be written in the material
file.

In this change, we remove that dependency. By definition the 
"per view" descriptor-set layout should only depend on view parameters
and obviously, all materials must be compatible.

In practice, a material does affect the layout so a reconciliation 
needs to happen somewhere. It's easier to maintain to have all this
logic in one place (in Filament) instead of split between material
generation and filament. 


All this change really does is to remove the information about the
per-view layout from the material file, and move it to Material in
filament, where it is hardcoded (before it was hardcoded in filamat),
but because both sides needed to match there was shared code.

**Material recompilation needed**
2025-05-06 11:10:06 -07:00
Mathias Agopian
3d78322058 clang-tidy cleanup 2025-05-06 11:10:06 -07:00
Mathias Agopian
3e1ea7cdfd clean-up our handling of descriptor-sets a bit more
the main aim of this PR is to consolidate how we access the
"per view" descriptor set to a single place.

some validation code is moved into DescriptorSets.cpp, so we get a
more centralized idea of what we do with the descriptors.

also factorize in one place the filtering of sampler list by layouts.
2025-05-06 11:10:06 -07:00
Mathias Agopian
6d413a4faf add missing depth formats to getMetalFormat()
this prevented to upload depth data into a depth texture
2025-05-06 11:08:34 -07:00
Powei Feng
af09c517b6 Revert "Use the Perfetto SDK instead of ATRACE" (#8701)
This reverts commit ca3ff7e08e.
2025-05-06 17:41:21 +00: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
bridgewaterrobbie
1661085705 Separate out required and optional features for webgpu. Panic if any required features are not available 2025-04-30 16:04:18 -04:00
45 changed files with 2167 additions and 857 deletions

View File

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

View File

@@ -5,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

@@ -30,6 +30,7 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/StaticString.h>
#include <math/vec4.h>
@@ -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

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

View File

@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
Profiler profiler;
if constexpr (SYSTRACE_TAG) {
if (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 constexpr (SYSTRACE_TAG) {
if (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled
profiler.stop();

View File

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

View File

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

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <utils/Hash.h>
#include "webgpu/WebGPUDriver.h"
#include "WebGPUPipelineCreation.h"
@@ -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);
}
@@ -877,6 +916,14 @@ void WebGPUDriver::blit(
}
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
// TODO Investigate implications of this hash more closely. Vulkan has a whole class
// VulkanPipelineCache to handle this, may be missing nuance
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
auto hash = pipleineStateHasher(pipelineState);
if(mPipelineMap.find(hash) != mPipelineMap.end()){
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
return;
}
const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr &&
@@ -913,8 +960,8 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat());
// TODO: uncomment once we have a valid pipeline to set
// mRenderPassEncoder.SetPipeline(pipeline);
mPipelineMap[hash] = pipeline;
mRenderPassEncoder.SetPipeline(pipeline);
}
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -934,10 +981,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 +1028,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) {
@@ -92,6 +93,8 @@ private:
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
/*
* Driver interface
*/

View File

@@ -16,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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -50,8 +50,6 @@
#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,10 +37,7 @@
#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,8 +21,6 @@
#include "GltfEnums.h"
#include <utils/Log.h>
#define SYSTRACE_TAG SYSTRACE_TAG_GLTFIO
#include <utils/Systrace.h>
#define CGLTF_IMPLEMENTATION

View File

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

View File

@@ -17,23 +17,24 @@
#ifndef TNT_UTILS_SYSTRACE_H
#define TNT_UTILS_SYSTRACE_H
#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)
#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)
/*
* The SYSTRACE_ macros use SYSTRACE_TAG as a category, which must be defined
* before this file is included.
* 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.
*/
#ifndef SYSTRACE_TAG
# error SYSTRACE_TAG must be set to SYSTRACE_TAG_{DISABLED|FILAMENT|JOBSYSTEM}
#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS)
#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__)
@@ -43,6 +44,7 @@
#else
#define SYSTRACE_ENABLE()
#define SYSTRACE_DISABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_FRAME_ID(frame)
@@ -54,6 +56,6 @@
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#endif
#endif // ANDROID
#endif // TNT_UTILS_SYSTRACE_H

View File

@@ -17,75 +17,226 @@
#ifndef TNT_UTILS_ANDROID_SYSTRACE_H
#define TNT_UTILS_ANDROID_SYSTRACE_H
#include <perfetto/perfetto.h>
#include <atomic>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(systrace,
perfetto::Category("filament"),
perfetto::Category("jobsystem"),
perfetto::Category("gltfio"));
#include <utils/compiler.h>
PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(systrace);
// enable tracing
#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(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
// disable tracing
#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG)
#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)
/**
* 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)
#else
#define SYSTRACE_ENABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_CALL() \
auto constexpr FILAMENT_SYSTRACE_FUNCTION = perfetto::StaticString(__FUNCTION__); \
TRACE_EVENT(UTILS_PERFETTO_CATEGORY, FILAMENT_SYSTRACE_FUNCTION)
#define SYSTRACE_NAME(name) TRACE_EVENT(UTILS_PERFETTO_CATEGORY, nullptr, \
[&](perfetto::EventContext ctx) { \
ctx.event()->set_name(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() TRACE_EVENT_END(UTILS_PERFETTO_CATEGORY)
#define SYSTRACE_ASYNC_BEGIN(name, cookie) \
TRACE_EVENT_BEGIN(UTILS_PERFETTO_CATEGORY, name, perfetto::Track(cookie))
#define SYSTRACE_ASYNC_END(name, cookie) \
TRACE_EVENT_END(UTILS_PERFETTO_CATEGORY, perfetto::Track(cookie))
// 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)
// Denotes that a new frame has started processing.
#define SYSTRACE_FRAME_ID(frame) \
TRACE_EVENT_INSTANT(UTILS_PERFETTO_CATEGORY, "frame", "id", frame)
{ /* scope for frame id trace */ \
char buf[64]; \
snprintf(buf, 64, "frame %u", frame); \
SYSTRACE_NAME(buf); \
}
// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name.
#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__)
#define SYSTRACE_NAME_BEGIN(name) \
___trctx.traceBegin(SYSTRACE_TAG, name)
#define SYSTRACE_NAME_END() \
___trctx.traceEnd(SYSTRACE_TAG)
/**
* 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 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)
/**
* 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) \
TRACE_COUNTER(UTILS_PERFETTO_CATEGORY, name, val)
___trctx.value(SYSTRACE_TAG, name, int32_t(val))
#define SYSTRACE_VALUE64(name, val) \
TRACE_COUNTER(UTILS_PERFETTO_CATEGORY, name, val)
___trctx.value(SYSTRACE_TAG, name, int64_t(val))
#endif // SYSTRACE_TAG == SYSTRACE_TAG_DISABLED
// ------------------------------------------------------------------------------------------------
// 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 // TNT_UTILS_ANDROID_SYSTRACE_H

View File

@@ -29,25 +29,13 @@
#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.
@@ -105,8 +93,6 @@ 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...
// ------------------------------------------------------------------------------------------------
@@ -132,40 +118,50 @@ namespace utils {
namespace details {
class Systrace {
public:
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 tag) noexcept;
static void enable(uint32_t tags) noexcept;
static void disable(uint32_t tags) noexcept;
void traceBegin(uint32_t tag, const char* name) noexcept {
inline 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)
}
}
void traceEnd(uint32_t tag, const char* name) noexcept {
inline 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, "")
}
}
void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
// TODO
}
}
void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
// TODO
}
}
void value(uint32_t tag, const char* name, int32_t value) noexcept {
inline 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);
@@ -174,7 +170,7 @@ public:
}
}
void value(uint32_t tag, const char* name, int64_t value) noexcept {
inline 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);
@@ -183,16 +179,16 @@ public:
}
}
void frameId(uint32_t tag, uint32_t frame) noexcept {
inline 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 {
@@ -217,25 +213,25 @@ private:
// ------------------------------------------------------------------------------------------------
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);
}
~ScopedTrace() noexcept {
inline ~ScopedTrace() noexcept {
mTrace.traceEnd(mTag, mName);
}
void value(uint32_t tag, const char* name, int32_t v) noexcept {
inline void value(uint32_t tag, const char* name, int32_t v) noexcept {
mTrace.value(tag, name, v);
}
void value(uint32_t tag, const char* name, int64_t v) noexcept {
inline 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,8 +15,10 @@
*/
// Note: The overhead of SYSTRACE_TAG_JOBSYSTEM is not negligible especially with parallel_for().
#define SYSTRACE_TAG SYSTRACE_TAG_DISABLED
#ifndef SYSTRACE_TAG
//#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,26 +14,206 @@
* limitations under the License.
*/
#include <utils/compiler.h>
#include <utils/android/Systrace.h>
#include <utils/Systrace.h>
#include <utils/Log.h>
#include <perfetto/perfetto.h>
#include <cinttypes>
PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(systrace);
#include <string.h>
namespace {
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <dlfcn.h>
class SystraceStaticInitialization {
public:
SystraceStaticInitialization() {
perfetto::TracingInitArgs args;
args.backends |= perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
systrace::TrackEvent::Register();
}
};
namespace utils {
namespace details {
UTILS_UNUSED SystraceStaticInitialization sSystraceStaticInitialization{};
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);
}
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){};
}
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,17 +14,11 @@
* limitations under the License.
*/
#include <utils/darwin/Systrace.h>
#ifndef FILAMENT_APPLE_SYSTRACE
# define FILAMENT_APPLE_SYSTRACE 0
#endif
#include <utils/Systrace.h>
#include <utils/Log.h>
#if FILAMENT_APPLE_SYSTRACE
#include <atomic>
#include <stack>
#include <stdint.h>
#include <pthread.h>
static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
@@ -47,24 +41,21 @@ void Systrace::setup() noexcept {
pthread_once(&atrace_once_control, init_once);
}
void Systrace::enable(uint32_t tag) noexcept {
void Systrace::enable(uint32_t tags) noexcept {
setup();
uint32_t const mask = 1 << tag;
sGlobalState.isTracingEnabled.fetch_or(mask, std::memory_order_relaxed);
sGlobalState.isTracingEnabled.fetch_or(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);
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
// 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();
uint32_t const mask = 1 << tag;
return bool(sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) & mask);
return bool((sGlobalState.isTracingEnabled.load(std::memory_order_relaxed) | SYSTRACE_TAG_ALWAYS) & tag);
}
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),