Compare commits

..

6 Commits

Author SHA1 Message Date
Syed Idris Shah
4ff62402c2 wip: render primitive 2025-04-17 14:41:50 -04:00
Syed Idris Shah
595a313686 WIP: Update VertexBuffer Info for webgpu 2025-04-17 14:41:50 -04:00
Powei Feng
21dd1319df Release Filament 1.59.2 2025-04-16 15:47:19 -07:00
bridgewaterrobbie
444ac8d6a6 Handle WebGPUDescriptorSetLayout creation 2025-04-15 18:51:26 -04:00
Syed Idris Shah
aac1e7dc4c Move adapter and device creation to the begining 2025-04-15 18:51:26 -04:00
Matthew Hoffman
5b8a1e5e58 Add a macro for skipping tests based on backend/OS. (#8602)
BUGS=[398198557]
2025-04-15 17:03:48 -05:00
23 changed files with 514 additions and 43 deletions

View File

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

View File

@@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.59.3
## v1.59.2
- Fix build/compile errors when upgrading to MacOS 15.4

View File

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

View File

@@ -507,8 +507,10 @@ if (APPLE OR LINUX)
test/Arguments.cpp
test/ImageExpectations.cpp
test/Lifetimes.cpp
test/PlatformRunner.cpp
test/Shader.cpp
test/SharedShaders.cpp
test/Skip.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp

View File

@@ -1139,7 +1139,6 @@ struct ExternalSamplerDatum {
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
std::string label;
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed

View File

@@ -464,13 +464,21 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {}
uint8_t attributeCount, AttributeArray attributes) {
constructHandle<WGPUVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {}
Handle<HwVertexBufferInfo> vbih) {
auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vbih);
constructHandle<WGPUVertexBuffer>(vbh, mDevice, vertexCount,vertexBufferInfo->bufferCount, vbih);
}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {}
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
constructHandle<WGPUIndexBuffer>(ibh, mDevice, elementSize, indexCount);
}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {}
@@ -503,7 +511,18 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType
uint32_t depth, TextureUsage usage) {}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
assert_invariant(mDevice);
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* indexBuffer = handleCast<WGPUIndexBuffer>(ibh);
// auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vertexBuffer->vbih);
// renderPrimitive->setBuffers(vertexBufferInfo, vertexBuffer, indexBuffer);
renderPrimitive->vertexBuffer = vertexBuffer;
renderPrimitive->indexBuffer = indexBuffer;
renderPrimitive->type = pt;
}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
@@ -715,6 +734,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
@@ -828,6 +848,70 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
}
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
// VulkanCommandBuffer* commands = mCurrentRenderPass.commandBuffer;
// VkCommandBuffer cmdbuffer = commands->buffer();
// auto prim = resource_ptr<VulkanRenderPrimitive>::cast(&mResourceManager, rph);
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
// commands->acquire(prim);
// This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
(void) vbi;
// mRenderPassEncoder.SetVertexBuffer(uint32_t slot, Buffer const& buffer = nullptr, uint64_t offset = 0, uint64_t size = kWholeSize);
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->buffer, renderPrimitive->indexBuffer->indexFormat);
// uint32_t const bufferCount = vbi->getAttributeCount();
// VkDeviceSize const* offsets = vbi->getOffsets();
// VkBuffer const* buffers = prim->vertexBuffer->getVkBuffers();
//
// // Next bind the vertex buffers and index buffer. One potential performance improvement is to
// // avoid rebinding these if they are already bound, but since we do not (yet) support subranges
// // it would be rare for a client to make consecutive draw calls with the same render primitive.
// vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
// vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->buffer.getGpuBuffer(), 0,
// prim->indexBuffer->indexType);
// METAL
// if (UTILS_UNLIKELY(mContext->currentRenderPassAbandoned)) {
// return;
// }
// FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
// << "bindRenderPrimitive() without a valid command encoder.";
//
// // Bind the user vertex buffers.
// MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
// size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
// size_t maxBufferIndex = 0;
//
// MetalRenderPrimitive const* const primitive = handle_cast<MetalRenderPrimitive>(rph);
// MetalVertexBufferInfo const* const vbi =
// handle_cast<MetalVertexBufferInfo>(primitive->vertexBuffer->vbih);
//
// mContext->currentRenderPrimitive = rph;
//
// auto vb = primitive->vertexBuffer;
// for (auto m : vbi->bufferMapping) {
// assert_invariant(
// m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
// m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
// size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
// vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
// maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
// }
//
// const auto bufferCount = maxBufferIndex + 1;
// MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
// USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, vertexBuffers,
// vertexBufferOffsets, bufferCount);
//
// // Bind the zero buffer, used for missing vertex attributes.
// static const char bytes[16] = { 0 };
// [mContext->currentRenderPassEncoder setVertexBytes:bytes
// length:16
// atIndex:ZERO_VERTEX_BUFFER_BINDING];
}
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {

View File

@@ -112,7 +112,7 @@ private:
template<typename D, typename B>
void destructHandle(Handle<B>& handle) noexcept {
auto* p = mHandleAllocator.handle_cast<D*>(handle);
return mHandleAllocator.deallocate(handle, p);
mHandleAllocator.deallocate(handle, p);
}
};

View File

@@ -27,18 +27,126 @@ wgpu::Buffer createIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
.mappedAtCreation = false };
return device.CreateBuffer(&descriptor);
}
wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool normalized, bool integer) {
using ElementType = filament::backend::ElementType;
// using VertexFormat = wgpu::VertexFormat;
if (normalized) {
switch (type) {
// Single Component Types
case ElementType::BYTE: return wgpu::VertexFormat::Snorm8;
case ElementType::UBYTE: return wgpu::VertexFormat::Unorm8;
case ElementType::SHORT: return wgpu::VertexFormat::Snorm16;
case ElementType::USHORT: return wgpu::VertexFormat::Unorm16;
// Two Component Types
case ElementType::BYTE2: return wgpu::VertexFormat::Snorm8x2;
case ElementType::UBYTE2: return wgpu::VertexFormat::Unorm8x2;
case ElementType::SHORT2: return wgpu::VertexFormat::Snorm16x2;
case ElementType::USHORT2: return wgpu::VertexFormat::Unorm16x2;
// Three Component Types
// There is no vertex format type for 3 byte data in webgpu. Use
// 4 byte signed normalized type and ignore the last byte.
// TODO: This is to be verified.
case ElementType::BYTE3: return wgpu::VertexFormat::Snorm8x4; // NOT MINSPEC
case ElementType::UBYTE3: return wgpu::VertexFormat::Unorm8x4; // NOT MINSPEC
case ElementType::SHORT3: return wgpu::VertexFormat::Snorm16x4; // NOT MINSPEC
case ElementType::USHORT3: return wgpu::VertexFormat::Unorm16x4; // NOT MINSPEC
// Four Component Types
case ElementType::BYTE4: return wgpu::VertexFormat::Snorm8x4;
case ElementType::UBYTE4: return wgpu::VertexFormat::Unorm8x4;
case ElementType::SHORT4: return wgpu::VertexFormat::Snorm16x4;
case ElementType::USHORT4: return wgpu::VertexFormat::Unorm8x4;
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
return wgpu::VertexFormat::Float32x3;
}
}
switch (type) {
// Single Component Types
// There is no direct alternative for SSCALED in webgpu. Convert them to Float32 directly.
// This will result in increased memory on the cpu side.
// TODO: Is Float16 acceptable instead with some potential accuracy errors?
case ElementType::BYTE: return integer ? wgpu::VertexFormat::Sint8 : wgpu::VertexFormat::Float32;
case ElementType::UBYTE: return integer ? wgpu::VertexFormat::Uint8 : wgpu::VertexFormat::Float32;
case ElementType::SHORT: return integer ? wgpu::VertexFormat::Sint16 : wgpu::VertexFormat::Float32;
case ElementType::USHORT: return integer ? wgpu::VertexFormat::Uint16 : wgpu::VertexFormat::Float32;
case ElementType::HALF: return wgpu::VertexFormat::Float16;
case ElementType::INT: return wgpu::VertexFormat::Sint32;
case ElementType::UINT: return wgpu::VertexFormat::Uint32;
case ElementType::FLOAT: return wgpu::VertexFormat::Float32;
// Two Component Types
case ElementType::BYTE2: return integer ? wgpu::VertexFormat::Sint8x2 : wgpu::VertexFormat::Float32x2;
case ElementType::UBYTE2: return integer ? wgpu::VertexFormat::Uint8x2 : wgpu::VertexFormat::Float32x2;
case ElementType::SHORT2: return integer ? wgpu::VertexFormat::Sint16x2 : wgpu::VertexFormat::Float32x2;
case ElementType::USHORT2: return integer ? wgpu::VertexFormat::Uint16x2 : wgpu::VertexFormat::Float32x2;
case ElementType::HALF2: return wgpu::VertexFormat::Float16x2;
case ElementType::FLOAT2: return wgpu::VertexFormat::Float32x2;
// Three Component Types
case ElementType::BYTE3: return wgpu::VertexFormat::Sint8x4; // NOT MINSPEC
case ElementType::UBYTE3: return wgpu::VertexFormat::Uint8x4; // NOT MINSPEC
case ElementType::SHORT3: return wgpu::VertexFormat::Sint16x4; // NOT MINSPEC
case ElementType::USHORT3: return wgpu::VertexFormat::Uint16x4; // NOT MINSPEC
case ElementType::HALF3: return wgpu::VertexFormat::Float16x4; // NOT MINSPEC
case ElementType::FLOAT3: return wgpu::VertexFormat::Float32x3;
// Four Component Types
case ElementType::BYTE4: return integer ? wgpu::VertexFormat::Sint8x4 : wgpu::VertexFormat::Float32x4;
case ElementType::UBYTE4: return integer ? wgpu::VertexFormat::Uint8x4 : wgpu::VertexFormat::Float32x4;
case ElementType::SHORT4: return integer ? wgpu::VertexFormat::Sint16x4 : wgpu::VertexFormat::Float32x4;
case ElementType::USHORT4: return integer ? wgpu::VertexFormat::Uint16x4 : wgpu::VertexFormat::Float32x4;
case ElementType::HALF4: return wgpu::VertexFormat::Float16x4;
case ElementType::FLOAT4: return wgpu::VertexFormat::Float32x4;
}
FILAMENT_CHECK_POSTCONDITION(false) << "Vertex format should always be defined.";
return wgpu::VertexFormat::Float32x3;
}
} // namespace
namespace filament::backend {
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
mVertexBufferLayout(bufferCount),
mAttributes(bufferCount) {
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute attrib = attributes[attribIndex];
// Ignore the attributes which are not bind to vertex buffers.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
continue;
}
assert_invariant(attrib.buffer < bufferCount);
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
mAttributes[attrib.buffer].push_back({
.format = vertexFormat,
.offset = attrib.offset,
.shaderLocation = attribIndex,
});
mVertexBufferLayout[attrib.buffer] = {
.arrayStride = attrib.stride,
};
}
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++) {
mVertexBufferLayout[bufferIndex] = {
.attributeCount = mAttributes[bufferIndex].size(),
.attributes = mAttributes[bufferIndex].data(),
};
}
}
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
uint32_t indexCount)
: buffer(createIndexBuffer(device, elementSize, indexCount)) {}
: buffer(createIndexBuffer(device, elementSize, indexCount)),
indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih)
Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vextexCount),
vbih(vbih),
buffers(bufferCount) {
@@ -62,17 +170,17 @@ WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byt
bufferObjectBinding(bindingType) {}
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
wgpu::ShaderStage ret = wgpu::ShaderStage::None;
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
if (any(ShaderStageFlags::VERTEX & fFlags)) {
ret |= wgpu::ShaderStage::Vertex;
retStages |= wgpu::ShaderStage::Vertex;
}
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
ret |= wgpu::ShaderStage::Fragment;
retStages |= wgpu::ShaderStage::Fragment;
}
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
ret |= wgpu::ShaderStage::Compute;
retStages |= wgpu::ShaderStage::Compute;
}
return ret;
return retStages;
}
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
@@ -142,7 +250,7 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
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_"+ layout.label + std::to_string(++layoutNum) },
.label{ "layout_" + std::to_string(++layoutNum) },
.entryCount = wEntries.size(),
.entries = wEntries.data()
};
@@ -150,4 +258,10 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
mLayout = device->CreateBindGroupLayout(&layoutDescriptor);
}
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
void WGPURenderPrimitive::setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer) {
}
}// namespace filament::backend

View File

@@ -24,6 +24,7 @@
#include <backend/Handle.h>
#include <utils/FixedCapacityVector.h>
// #include <utils/StructureOfArrays.h>
#include <webgpu/webgpu_cpp.h>
@@ -32,23 +33,40 @@
namespace filament::backend {
struct WGPUBufferObject;
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
// VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout
// is equal to bufferCount. Each VertexBufferLayout can contain multiple VertexAttribute. Bind index
// of vertex buffer is implicitly calculated by the position of VertexBufferLayout in an array.
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {}
AttributeArray attributes;
AttributeArray const& attributes);
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const {
return mVertexBufferLayout.data();
}
inline uint32_t getVertexBufferLayoutSize() const {
return mVertexBufferLayout.size();
}
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
return mAttributes[index].data();
}
inline uint32_t getVertexAttributeSize(uint32_t index) const {
return mAttributes[index].size();
}
private:
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout;
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes;
};
struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih);
Handle<HwVertexBufferInfo> vbih);
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
Handle<WGPUVertexBufferInfo> vbih;
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> buffers;
};
@@ -57,9 +75,10 @@ struct WGPUIndexBuffer : public HwIndexBuffer {
uint32_t indexCount);
wgpu::Buffer buffer;
wgpu::IndexFormat indexFormat;
};
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
// TODO: Currently WGPUBufferObject is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUBufferObject : HwBufferObject {
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
@@ -67,6 +86,7 @@ struct WGPUBufferObject : HwBufferObject {
wgpu::Buffer buffer;
const BufferObjectBinding bufferObjectBinding;
};
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout {
public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const* device);

View File

@@ -43,10 +43,12 @@ using namespace image;
namespace test {
Backend BackendTest::sBackend = Backend::NOOP;
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
bool BackendTest::sIsMobilePlatform = false;
void BackendTest::init(Backend backend, bool isMobilePlatform) {
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
sBackend = backend;
sOperatingSystem = operatingSystem;
sIsMobilePlatform = isMobilePlatform;
}
@@ -199,6 +201,18 @@ void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, si
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
}
bool BackendTest::matchesEnvironment(Backend backend) {
return sBackend == backend;
}
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
return sOperatingSystem == operatingSystem;
}
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) {
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend);
}
class Environment : public ::testing::Environment {
public:
virtual void SetUp() override {
@@ -210,8 +224,8 @@ public:
}
};
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]) {
BackendTest::init(backend, isMobile);
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) {
BackendTest::init(backend, operatingSystem, isMobile);
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new Environment);
}

View File

@@ -31,9 +31,10 @@ namespace test {
class BackendTest : public ::testing::Test {
public:
static void init(Backend backend, bool isMobilePlatform);
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform);
static Backend sBackend;
static OperatingSystem sOperatingSystem;
static bool sIsMobilePlatform;
protected:
@@ -71,6 +72,10 @@ protected:
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; }
static bool matchesEnvironment(Backend backend);
static bool matchesEnvironment(OperatingSystem operatingSystem);
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
private:
filament::backend::Driver* driver = nullptr;

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PlatformRunner.h"
namespace utils {
template<>
CString to_string<test::Backend>(test::Backend backend) noexcept {
switch (backend) {
case test::Backend::OPENGL: {
return "OpenGL";
}
case test::Backend::VULKAN: {
return "Vulkan";
}
case test::Backend::METAL: {
return "Metal";
}
case test::Backend::WEBGPU: {
return "WebGPU";
}
case test::Backend::NOOP:
default: {
return "No-op";
}
}
}
template<>
CString to_string(test::OperatingSystem os) noexcept {
switch (os) {
case test::OperatingSystem::LINUX: {
return "Linux";
}
case test::OperatingSystem::APPLE: {
return "Apple";
}
case test::OperatingSystem::OTHER:
default: {
return "Other";
}
}
}
} // namespace utils

View File

@@ -19,6 +19,7 @@
#include <stdint.h>
#include <stddef.h>
#include "utils/CString.h"
namespace test {
@@ -34,6 +35,15 @@ enum class Backend : uint8_t {
NOOP = 5,
};
enum class OperatingSystem: uint8_t {
OTHER = 1,
// Also represents android phones.
LINUX = 2,
// Also represents iOS phones.
APPLE = 3,
// TODO: When tests support windows add it here.
};
struct NativeView {
void* ptr = nullptr;
size_t width = 0, height = 0;
@@ -51,9 +61,10 @@ NativeView getNativeView();
* No tests will be run yet.
*
* @param backend The backend to run the tests on.
* @param operatingSystem The operating system the tests are being run on.
* @param isMobile True if the platform is a mobile platform (iOS or Android).
*/
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]);
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]);
/**
* Test runners should call runTests when they are ready for tests to be run.
@@ -68,6 +79,6 @@ int runTests();
*/
Backend parseArgumentsForBackend(int argc, char* argv[]);
}
} // namespace test
#endif

View File

@@ -56,7 +56,7 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
mProgram = cleanup.add(api.createProgram(std::move(prog)));
mDescriptorSetLayout = cleanup.add(
api.createDescriptorSetLayout(DescriptorSetLayout{ "kLayouts", kLayouts }));
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
}
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Skip.h"
#include <sstream>
namespace test {
SkipEnvironment::SkipEnvironment(test::Backend backend) : backend(backend) {}
SkipEnvironment::SkipEnvironment(test::OperatingSystem os) : os(os) {}
SkipEnvironment::SkipEnvironment(test::OperatingSystem os, test::Backend backend)
: backend(backend),
os(os) {}
bool SkipEnvironment::matches() {
bool backendMatches = !backend.has_value() || *backend == BackendTest::sBackend;
bool osMatches = !os.has_value() || *os == BackendTest::sOperatingSystem;
bool isMobileMatches = !isMobile.has_value() || *isMobile == BackendTest::sIsMobilePlatform;
return backendMatches && osMatches && isMobileMatches;
}
std::string SkipEnvironment::describe() {
std::stringstream result;
if (matches()) {
result << "environment matches because " << describe_actual_environment() << ".";
} else {
result << "environment does not match because " << describe_requirements() << " but "
<< describe_actual_environment() << ".";
}
return result.str();
}
std::string SkipEnvironment::describe_actual_environment() {
bool resultWritten = false;
std::stringstream reality;
if (backend.has_value()) {
reality << "backend was " << utils::to_string(BackendTest::sBackend).c_str();
resultWritten = true;
}
if (os.has_value()) {
if (resultWritten) {
reality << ", and ";
}
reality << "operating system was "
<< utils::to_string(BackendTest::sOperatingSystem).c_str();
resultWritten = true;
}
if (isMobile.has_value()) {
if (resultWritten) {
reality << ", and ";
}
reality << "device " << (BackendTest::sIsMobilePlatform ? "was" : "was not") << " mobile";
resultWritten = true;
}
return reality.str();
}
std::string SkipEnvironment::describe_requirements() {
bool resultWritten = false;
std::stringstream requirement;
if (backend.has_value()) {
requirement << "backend needs to be " << utils::to_string(*backend).c_str();
resultWritten = true;
}
if (os.has_value()) {
if (resultWritten) {
requirement << ", and ";
}
requirement << "operating system needs to be " << utils::to_string(*os).c_str();
resultWritten = true;
}
if (isMobile.has_value() && BackendTest::sIsMobilePlatform != isMobile) {
if (resultWritten) {
requirement << ", and ";
}
requirement << "device needs to " << (*isMobile ? "be" : "not be") << " mobile";
resultWritten = true;
}
return requirement.str();
}
} // namespace test

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_SKIP_H
#define TNT_SKIP_H
#include <gtest/gtest.h>
#include "BackendTest.h"
// skipEnvironment must be a test::SkipEnvironment
#define SKIP_IF(skipEnvironment) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
GTEST_SKIP() << "Skipping test as the " << skip.describe(); \
} \
} while (false)
namespace test {
struct SkipEnvironment {
SkipEnvironment(const SkipEnvironment&) = default;
explicit SkipEnvironment(test::Backend backend);
explicit SkipEnvironment(test::OperatingSystem os);
SkipEnvironment(test::OperatingSystem os, test::Backend backend);
std::optional<test::Backend> backend;
std::optional<test::OperatingSystem> os;
std::optional<bool> isMobile;
bool matches();
// Describes the current state of either matching or mismatching.
std::string describe();
// Describe all the non-null requirements.
std::string describe_requirements();
// Describes the environment's status for all the attributes that are non-null.
std::string describe_actual_environment();
};
} // namespace test
#endif// TNT_SKIP_H

View File

@@ -51,6 +51,6 @@ int main(int argc, char* argv[]) {
return 1;
}
test::initTests(backend, false, argc, argv);
test::initTests(backend, test::OperatingSystem::LINUX, false, argc, argv);
return test::runTests();
}

View File

@@ -98,7 +98,7 @@ test::NativeView getNativeView() {
int main(int argc, char* argv[]) {
auto backend = test::parseArgumentsForBackend(argc, argv);
test::initTests(backend, false, argc, argv);
test::initTests(backend, test::OperatingSystem::APPLE, false, argc, argv);
AppDelegate* delegate = [AppDelegate new];
delegate.backend = backend;
NSApplication* app = [NSApplication sharedApplication];

View File

@@ -17,6 +17,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Skip.h"
using namespace filament;
using namespace filament::backend;
@@ -24,6 +25,8 @@ using namespace filament::backend;
namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -81,6 +84,8 @@ TEST_F(BackendTest, FrameScheduledCallback) {
}
TEST_F(BackendTest, FrameCompletedCallback) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
Cleanup cleanup(api);

View File

@@ -18,6 +18,7 @@
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -78,6 +79,8 @@ void main() {
})";
TEST_F(BackendTest, PushConstants) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
api.startCapture(0);

View File

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

View File

@@ -33,11 +33,11 @@ namespace filament::descriptor_sets {
using namespace backend;
static DescriptorSetLayout const postProcessDescriptorSetLayout{"postProcess", {
static DescriptorSetLayout const postProcessDescriptorSetLayout{{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
}};
static DescriptorSetLayout const depthVariantDescriptorSetLayout{"depthVariant",{
static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
}};
@@ -46,14 +46,14 @@ static DescriptorSetLayout const depthVariantDescriptorSetLayout{"depthVariant",
// 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{"ssrVariant", {
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 DescriptorSetLayout perViewDescriptorSetLayout = {"perView", {
static DescriptorSetLayout perViewDescriptorSetLayout = {{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
@@ -68,7 +68,7 @@ static DescriptorSetLayout perViewDescriptorSetLayout = {"perView", {
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
}};
static DescriptorSetLayout perRenderableDescriptorSetLayout = {"preRenderable",{
static DescriptorSetLayout perRenderableDescriptorSetLayout = {{
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },

View File

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