Compare commits

...

12 Commits

Author SHA1 Message Date
bridgewaterrobbie
31f96c9065 Gemini tinkering 3 2025-05-22 11:09:26 -04:00
bridgewaterrobbie
1305303f2e Fix typo in getVertexFormat 2025-05-20 18:13:19 -04:00
bridgewaterrobbie
8532511107 Add enforced check for compressed texture block width/height offset 2025-05-20 16:10:17 -04:00
bridgewaterrobbie
b762ccb78c Use the aspect to skip drawing for depth only. TODO is why this is even coming up? 2025-05-20 16:10:17 -04:00
bridgewaterrobbie
3296eb153c If necessary, apply padding to uploaded texture. 2025-05-20 16:10:17 -04:00
bridgewaterrobbie
ae6d1028f7 Initial update3DImage implementation for webgpu 2025-05-20 16:10:17 -04:00
bridgewaterrobbie
92579b6307 Expose bytes per pixel function separately from computeDataSize 2025-05-20 16:10:17 -04:00
Benjamin Doherty
315b53c339 Release Filament 1.60.1 2025-05-20 12:42:33 -07:00
Andy Hovingh
c55d459986 webgpu: samples/hellopbr.cpp CLI allows for selecting webgpu backend api 2025-05-20 14:40:48 -05:00
Andy Hovingh
7876695545 webgpu: add CLI to texturedquad.cpp sample to select backend + updated WebGPU warning message. 2025-05-20 14:40:48 -05:00
Matthew Hoffman
e1a1464c5d Add helpers for creating RenderPipelineParams for backend tests (#8751)
* Add helpers for creating RenderPipelineParams for backend tests

BUGS=[398199600]

* Move all backend tests over to use cyan as the clear color
2025-05-20 19:17:12 +00:00
bridgewaterrobbie
67ed5141bf Avoid using paramater name type which is reserved in WGSL and thus can't be compiled for WebGPU 2025-05-20 14:09:23 -04:00
55 changed files with 532 additions and 256 deletions

View File

@@ -7,5 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.60.0'
implementation 'com.google.android.filament:filament-android:1.60.1'
}
```
@@ -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.60.0'
pod 'Filament', '~> 1.60.1'
```
## Documentation

View File

@@ -7,6 +7,12 @@ 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.61.0
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]
- samples/texturedquad.cpp now has CLI to select backend api
- samples/hellopbr.cpp CLI now allows for selecting webgpu
## v1.60.1

View File

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

View File

@@ -204,23 +204,15 @@ public:
}, new T(std::forward<T>(functor))
};
}
// --------------------------------------------------------------------------------------------
/**
* Computes the size in bytes needed to fit an image of given dimensions and format
* Computes the size in bytes for a pixel of given dimensions and format
*
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param stride Stride of a row in pixels
* @param height Height of the image in rows
* @param alignment Alignment in bytes of pixel rows
* @return The buffer size needed to fit this image in bytes
* @return The size of the specified pixel in bytes
*/
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
size_t stride, size_t height, size_t alignment) noexcept {
assert_invariant(alignment);
static constexpr size_t computePixelSize(PixelDataFormat format, PixelDataType type) noexcept {
if (type == PixelDataType::COMPRESSED) {
return 0;
}
@@ -242,7 +234,7 @@ public:
case PixelDataFormat::RGB_INTEGER:
n = 3;
break;
case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm)
case PixelDataFormat::UNUSED:// shouldn't happen (used to be rgbm)
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
n = 4;
@@ -251,7 +243,7 @@ public:
size_t bpp = n;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::COMPRESSED:// Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
@@ -282,16 +274,35 @@ public:
bpp = 2;
break;
}
return bpp;
}
// --------------------------------------------------------------------------------------------
/**
* Computes the size in bytes needed to fit an image of given dimensions and format
*
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param stride Stride of a row in pixels
* @param height Height of the image in rows
* @param alignment Alignment in bytes of pixel rows
* @return The buffer size needed to fit this image in bytes
*/
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
size_t stride, size_t height, size_t alignment) noexcept {
assert_invariant(alignment);
size_t bpp = computePixelSize(format, type);
size_t const bpr = bpp * stride;
size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
return bprAligned * height;
}
//! left coordinate in pixels
uint32_t left = 0;
uint32_t left = 0;
//! top coordinate in pixels
uint32_t top = 0;
uint32_t top = 0;
union {
struct {
//! stride in pixels

View File

@@ -26,7 +26,7 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
#include <private/backend/BackendUtils.h>
#include <math/mat3.h>
#include <utils/CString.h>
#include <utils/Panic.h>
@@ -39,6 +39,7 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
@@ -353,6 +354,17 @@ void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
}
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
if (rth) {
WGPURenderTarget* rt = handleCast<WGPURenderTarget>(rth);
if (rt == mDefaultRenderTarget) {
mDefaultRenderTarget = nullptr;
}
// WGPURenderTarget destructor is trivial.
// The HwTexture handles stored within WGPURenderTarget (via MRT, TargetBufferInfo)
// are not owned by WGPURenderTarget, so they are not destroyed here.
// They are destroyed via WebGPUDriver::destroyTexture.
destructHandle<WGPURenderTarget>(rth);
}
}
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
@@ -472,9 +484,9 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
mDevice, flags);
assert_invariant(mSwapChain);
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
FWGPU_LOGW << "WebGPU support is highly experimental, in development, and tested for only a "
"small set of simple samples (e.g. hellotriangle and texturedquad), thus issues "
"are likely to be encountered at this stage."
<< utils::io::endl;
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
@@ -579,7 +591,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {}
TargetBufferInfo depth, TargetBufferInfo stencil) {
// The `targets` flags indicate which of the `color`, `depth`, `stencil` TargetBufferInfo
// are actually active for this render target.
// We'll pass all TargetBufferInfo to WGPURenderTarget; it will use them if their handles are valid.
// Ensure that textures intended for use as attachments were created with
// wgpu::TextureUsage::RenderAttachment. This check should ideally be in createTextureR
// or validated here if possible.
// The `layerCount` parameter might be for creating array textures that this RT targets.
// Individual attachments (color[i].layer, depth.layer, stencil.layer) specify which layer
// of an array texture to bind. For now, we assume textures are pre-configured.
constructHandle<WGPURenderTarget>(rth, width, height, samples, color, depth, stencil);
}
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
@@ -752,11 +778,72 @@ void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
vertexBuffer->buffers[index] = bufferObject->getBuffer();
}
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& data) {
scheduleDestroy(std::move(data));
void WebGPUDriver::update3DImage(Handle<HwTexture> th, uint32_t level, uint32_t xoffset,
uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p) {
PixelBufferDescriptor* data = &p;
PixelBufferDescriptor reshapedData;
if (reshape(p, reshapedData)) {
data = &reshapedData;
}
auto texture = handleCast<WGPUTexture>(th);
// TODO: Writing to a depth texture is illegal and errors. I'm not sure why Filament is trying
// to do so, but early returning is working?
if(texture->getAspect() == wgpu::TextureAspect::DepthOnly){
scheduleDestroy(std::move(p));
return;
}
size_t blockWidth = texture->getBlockWidth();
size_t blockHeight = texture->getBlockHeight();
// WebGPU specification requires that for compressed textures, the x and y offsets
// must be a multiple of the compressed texture format's block width and height.
// See: https://www.w3.org/TR/webgpu/#abstract-opdef-validating-gputexelcopytextureinfo
if (blockWidth > 1 || blockHeight > 1) {
FILAMENT_CHECK_PRECONDITION(xoffset % blockWidth == 0)
<< "xoffset must be aligned to blockwidth, but offset is " << blockWidth
<< "and offset is " << xoffset;
FILAMENT_CHECK_PRECONDITION(yoffset % blockHeight == 0)
<< "yoffset must be aligned to blockHeight, but offset is " << blockHeight
<< "and offset is " << yoffset;
}
auto copyInfo = wgpu::TexelCopyTextureInfo{ .texture = texture->getTexture(),
.mipLevel = level,
.origin = { .x = xoffset, .y = yoffset, .z = zoffset },
.aspect = texture->getAspect() };
uint32_t bytesPerRow = static_cast<uint32_t>(
PixelBufferDescriptor::computePixelSize(data->format, data->type) * width);
auto extent = wgpu::Extent3D{ .width = width, .height = height, .depthOrArrayLayers = depth };
const uint8_t* dataBuff = static_cast<const uint8_t*>(data->buffer);
size_t dataSize = data->size;
std::unique_ptr<uint8_t[]> paddedBuffer;
if (bytesPerRow % 256 != 0) {
uint32_t padding = 256 - (bytesPerRow % 256);
uint32_t paddedBytesPerRow = bytesPerRow + padding;
size_t paddedBufferSize = static_cast<size_t>(paddedBytesPerRow) * height * depth;
paddedBuffer = std::make_unique<uint8_t[]>(paddedBufferSize);
uint8_t* dest = paddedBuffer.get();
for (uint32_t z = 0; z < depth; ++z) {
for (uint32_t y = 0; y < height; ++y) {
std::memcpy(dest, dataBuff, bytesPerRow);
dest += paddedBytesPerRow;
dataBuff += bytesPerRow;
}
}
dataBuff = paddedBuffer.get();
dataSize = paddedBufferSize;
bytesPerRow = paddedBytesPerRow;
}
auto layout = wgpu::TexelCopyBufferLayout{ .bytesPerRow = bytesPerRow, .rowsPerImage = height };
mQueue.WriteTexture(&copyInfo, dataBuff, dataSize, &layout, &extent);
scheduleDestroy(std::move(p));
}
void WebGPUDriver::setupExternalImage(void* image) {
@@ -783,35 +870,74 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
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 renderPassDescriptor;
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(renderPassDescriptor, mTextureView, params);
renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment;
assert_invariant(mTextureView);
wgpu::RenderPassDescriptor renderPassDescriptor{};
wgpu::TextureView defaultColorView = nullptr;
wgpu::TextureView defaultDepthStencilView = nullptr;
std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
uint32_t customColorViewCount = 0;
wgpu::TextureView customDepthView = nullptr;
wgpu::TextureView customStencilView = nullptr;
if (renderTarget->isDefaultRenderTarget()) {
assert_invariant(mSwapChain && mTextureView);
defaultColorView = mTextureView;
defaultDepthStencilView = mSwapChain->getDepthTextureView();
} else {
// Resolve views for custom render target
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
if (colorInfos[i].handle) {
auto* hwTexture = handleCast<WGPUTexture>(colorInfos[i].handle);
if (hwTexture) {
// TODO: Consider colorInfos[i].level and colorInfos[i].layer for view creation
// if WGPUTexture::getTexView() isn't sufficient or needs parameters.
customColorViews[customColorViewCount++] = hwTexture->getTexView();
}
}
}
const auto& depthInfo = renderTarget->getDepthAttachmentInfo();
if (depthInfo.handle) {
auto* hwTexture = handleCast<WGPUTexture>(depthInfo.handle);
if (hwTexture) {
customDepthView = hwTexture->getTexView();
}
}
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
if (stencilInfo.handle) {
// If depth and stencil use the same texture handle, this will re-cast but that's fine.
auto* hwTexture = handleCast<WGPUTexture>(stencilInfo.handle);
if (hwTexture) {
customStencilView = hwTexture->getTexView();
}
}
}
renderTarget->setUpRenderPassAttachments(renderPassDescriptor,
params,
defaultColorView,
defaultDepthStencilView,
customColorViews.data(),
customColorViewCount,
customDepthView,
customStencilView);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
// Ensure viewport dimensions are positive
uint32_t viewportWidth = params.viewport.width > 0 ? params.viewport.width : 1;
uint32_t viewportHeight = params.viewport.height > 0 ? params.viewport.height : 1;
mRenderPassEncoder.SetViewport(
static_cast<float>(params.viewport.left),
static_cast<float>(params.viewport.bottom),
static_cast<float>(viewportWidth),
static_cast<float>(viewportHeight),
params.depthRange.near,
params.depthRange.far);
}
void WebGPUDriver::endRenderPass(int /* dummy */) {

View File

@@ -20,7 +20,7 @@
#include <utils/BitmaskEnum.h>
#include <utils/Panic.h>
#include <private/backend/BackendUtils.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
@@ -68,7 +68,7 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
case ElementType::BYTE4: return VertexFormat::Snorm8x4;
case ElementType::UBYTE4: return VertexFormat::Unorm8x4;
case ElementType::SHORT4: return VertexFormat::Snorm16x4;
case ElementType::USHORT4: return VertexFormat::Unorm8x4;
case ElementType::USHORT4: return VertexFormat::Unorm16x4;
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
return VertexFormat::Float32x3;
@@ -502,6 +502,8 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
mUsage = fToWGPUTextureUsage(usage);
mFormat = fToWGPUTextureFormat(format);
mAspect = fToWGPUTextureViewAspect(usage, format);
mBlockWidth = filament::backend::getBlockWidth(format);
mBlockHeight = filament::backend::getBlockHeight(format);
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(target),
.usage = mUsage,
@@ -548,6 +550,10 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
mTexture = src->mTexture;
mAspect = src->mAspect;
mBlockWidth = src->mBlockWidth;
mBlockHeight = src->mBlockHeight;
mTexView = makeTextureView(baseLevel, levelCount, target);
}
@@ -917,58 +923,147 @@ wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const u
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;
WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
const MRT& colorAttachmentsMRT,
const Attachment& depthAttachmentInfo,
const Attachment& stencilAttachmentInfo)
: HwRenderTarget(width, height),
defaultRenderTarget(false),
samples(samples),
mColorAttachments(colorAttachmentsMRT),
mDepthAttachment(depthAttachmentInfo),
mStencilAttachment(stencilAttachmentInfo) {
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
}
// Static helper to map MRT index to TargetBufferFlags
TargetBufferFlags WGPURenderTarget::getTargetBufferFlagsAt(int mrtIndex) {
if (mrtIndex < 0 || mrtIndex >= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT) {
return TargetBufferFlags::NONE;
}
// This mapping assumes TargetBufferFlags::COLOR_0, COLOR_1 etc. are contiguous
return static_cast<TargetBufferFlags>(
static_cast<uint32_t>(TargetBufferFlags::COLOR0) << mrtIndex);
}
// Corrected getLoadOperation
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)) {
TargetBufferFlags bufferToOperateOn) {
if (any(params.flags.clear & bufferToOperateOn)) {
return wgpu::LoadOp::Clear;
}
if (any(params.flags.discardStart & bufferToOperateOn)) {
return wgpu::LoadOp::Clear; // Or wgpu::LoadOp::Undefined if clear is not desired on discard
}
return wgpu::LoadOp::Load;
}
// Corrected getStoreOperation
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
TargetBufferFlags buffer) {
const auto discardEndFlags = params.flags.discardEnd;
if (any(discardEndFlags & buffer)) {
TargetBufferFlags bufferToOperateOn) {
if (any(params.flags.discardEnd & bufferToOperateOn)) {
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);
// }
void WGPURenderTarget::setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params,
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureView const* customColorTextureViews,
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView) {
mColorAttachmentDescriptors.clear();
mHasDepthStencilAttachment = false;
if (defaultRenderTarget) {
assert_invariant(defaultColorTextureView);
wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = defaultColorTextureView;
colorAttDesc.resolveTarget = nullptr;
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
if (defaultDepthStencilTextureView) {
mDepthStencilAttachmentDescriptor = {
.view = defaultDepthStencilTextureView,
.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,
};
mHasDepthStencilAttachment = true;
}
} else { // Custom Render Target
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
if (customColorTextureViews[i]) {
wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = customColorTextureViews[i];
colorAttDesc.resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
}
}
wgpu::TextureView combinedDsView = customDepthTextureView ? customDepthTextureView : customStencilTextureView;
if (combinedDsView) {
mDepthStencilAttachmentDescriptor = {};
mDepthStencilAttachmentDescriptor.view = combinedDsView;
if (customDepthTextureView) {
mDepthStencilAttachmentDescriptor.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue = static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
} else {
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.depthReadOnly = true;
}
if (customStencilTextureView) {
mDepthStencilAttachmentDescriptor.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilClearValue = params.clearStencil;
mDepthStencilAttachmentDescriptor.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
} else {
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilReadOnly = true;
}
mHasDepthStencilAttachment = true;
}
}
descriptor.colorAttachments = colorAttachments.data();
descriptor.colorAttachmentCount = colorAttachments.size();
descriptor.depthStencilAttachment = nullptr;
descriptor.timestampWrites = nullptr;
descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
descriptor.colorAttachments = mColorAttachmentDescriptors.data();
descriptor.depthStencilAttachment = mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
// descriptor.sampleCount was removed from the core spec. If your webgpu.h still has it,
// and your Dawn version expects it, you might need to set it here based on this->samples.
// e.g., descriptor.sampleCount = this->samples;
}
math::uint2 WGPURenderTarget::getAttachmentSize() const noexcept {
if (!defaultRenderTarget) {
return {width, height};
}
// For default RT, size is dynamic and usually obtained from the swapchain.
// The caller (WebGPUDriver::beginRenderPass) should know the current swapchain size.
return {0,0};
}
}// namespace filament::backend

View File

@@ -161,6 +161,9 @@ public:
wgpu::Device const& device) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
wgpu::TextureAspect getAspect() const { return mAspect; }
size_t getBlockWidth() const { return mBlockWidth; }
size_t getBlockHeight() const { return mBlockHeight; }
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
[[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
@@ -184,6 +187,8 @@ private:
uint32_t mArrayLayerCount = 1;
wgpu::TextureView mTexView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
size_t mBlockWidth;
size_t mBlockHeight;
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
@@ -192,57 +197,66 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPUIndexBuffer* indexBuffer = nullptr;
};
class WGPURenderTarget : public HwRenderTarget {
public:
class Attachment {
public:
friend class WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->getTexture()),
mWGPUTexture(gpuTexture) {}
operator bool() const {
return mWGPUTexture != nullptr;
}
uint8_t level = 0;
uint16_t layer = 0;
private:
wgpu::Texture texture = nullptr;
WGPUTexture* mWGPUTexture = nullptr;
};
using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
const MRT& colorAttachments,
const Attachment& depthAttachment,
const Attachment& stencilAttachment);
// Default constructor for the default render target
WGPURenderTarget()
: HwRenderTarget(0, 0),
defaultRenderTarget(true) {}
defaultRenderTarget(true),
samples(1) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params);
// Updated signature: takes resolved views for custom RTs, and default views for default RT
void setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params,
// For default render target:
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
// For custom render targets:
wgpu::TextureView const* customColorTextureViews, // Array of views
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView);
math::uint2 getAttachmentSize() noexcept;
math::uint2 getAttachmentSize() const noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
// Accessors for the driver to get stored attachment info
const MRT& getColorAttachmentInfos() const { return mColorAttachments; }
const Attachment& getDepthAttachmentInfo() const { return mDepthAttachment; }
const Attachment& getStencilAttachmentInfo() const { return mStencilAttachment; }
// Static helpers for load/store operations
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
private:
// Helper to map MRT index to TargetBufferFlags
static TargetBufferFlags getTargetBufferFlagsAt(int mrtIndex);
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
};
MRT mColorAttachments{};
Attachment mDepthAttachment{};
Attachment mStencilAttachment{};
// Cached descriptors for the render pass
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
bool mHasDepthStencilAttachment = false;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H

View File

@@ -32,6 +32,7 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND
using namespace filament;
using namespace filament::backend;
using namespace filament::math;
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
@@ -114,16 +115,27 @@ PipelineState BackendTest::getColorWritePipelineState() {
return result;
}
void BackendTest::fullViewport(RenderPassParams& params) {
fullViewport(params.viewport);
filament::backend::Viewport BackendTest::getFullViewport() const {
const NativeView& view = getNativeView();
return Viewport {
.left = 0,
.bottom = 0,
.width = static_cast<uint32_t>(view.width),
.height = static_cast<uint32_t>(view.height)
};
}
void BackendTest::fullViewport(Viewport& viewport) {
const NativeView& view = getNativeView();
viewport.left = 0;
viewport.bottom = 0;
viewport.width = view.width;
viewport.height = view.height;
filament::backend::RenderPassParams BackendTest::getClearColorRenderPass(float4 color) {
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.clearColor = color;
return params;
}
filament::backend::RenderPassParams BackendTest::getNoClearRenderPass() {
return RenderPassParams{};
}
void BackendTest::renderTriangle(
@@ -131,14 +143,9 @@ void BackendTest::renderTriangle(
Handle<filament::backend::HwRenderTarget> renderTarget,
Handle<filament::backend::HwSwapChain> swapChain,
Handle<filament::backend::HwProgram> program) {
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = {0.f, 0.f, 1.f, 1.f};
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = 512;
params.viewport.height = 512;
renderTriangle(pipelineLayout, renderTarget, swapChain, program, params);
}

View File

@@ -54,9 +54,12 @@ protected:
static filament::backend::PipelineState getColorWritePipelineState();
// Helper methods to set the viewport to the full extent of the swap chain.
static void fullViewport(filament::backend::RenderPassParams& params);
static void fullViewport(filament::backend::Viewport& viewport);
// Gets the full back buffer's viewport
filament::backend::Viewport getFullViewport() const;
// If color is unset this defaults to using opaque cyan
static filament::backend::RenderPassParams getClearColorRenderPass(
filament::math::float4 color = filament::math::float4(0, 1, 1, 1));
static filament::backend::RenderPassParams getNoClearRenderPass();
void renderTriangle(
filament::backend::PipelineLayout const& pipelineLayout,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -151,3 +151,6 @@ if __name__ == "__main__":
results.show_images(file_prefix)
else:
results.batch_move(args.batch)
print("--------------------------------------------------------")
print("REMEMBER TO RESYNC CMAKE AND UPDATE HASHES IN TEST FILES")
print("--------------------------------------------------------")

View File

@@ -305,11 +305,7 @@ TEST_F(BlitTest, ColorResolve) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.clearColor = float4(1, 1, 0, 1);
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = kSrcTexWidth;
params.viewport.height = kSrcTexHeight;
@@ -337,7 +333,7 @@ TEST_F(BlitTest, ColorResolve) {
SamplerMagFilter::NEAREST);
EXPECT_IMAGE(dstRenderTarget, getExpectations(),
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 531759687));
}
TEST_F(BlitTest, Blit2DTextureArray) {

View File

@@ -79,12 +79,8 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
// Create a uniform buffer.
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use
@@ -157,6 +153,9 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"All values including alpha are written as 0, see b/417254943");
constexpr int kTexWidth = 512;
constexpr int kTexHeight = 512;
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -177,9 +176,9 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
// Create a render target.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
TextureFormat::RGBA8, 1, kTexWidth, kTexHeight, 1, TextureUsage::COLOR_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{ colorTexture }}, {}, {}));
TargetBufferFlags::COLOR0, kTexWidth, kTexHeight, 1, 0, {{ colorTexture }}, {}, {}));
// Upload uniforms for the first triangle.
// Upload the uniform, but with an offset to accommodate the padding in the shader's
@@ -190,13 +189,9 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
});
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512;
RenderPassParams params = getClearColorRenderPass();
params.viewport.height = kTexWidth;
params.viewport.width = kTexHeight;
renderTriangle({ { shader.getDescriptorSetLayout() } }, renderTarget, swapChain,
shader.getProgram(), params);
@@ -221,7 +216,7 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
EXPECT_IMAGE(renderTarget, getExpectations(),
ScreenshotParams(512, 512, "BufferObjectUpdateWithOffset", 91322442));
ScreenshotParams(kTexWidth, kTexHeight, "BufferObjectUpdateWithOffset", 2320747245));
api.flush();
api.commit(swapChain);

View File

@@ -161,12 +161,11 @@ TEST_F(BackendTest, FeedbackLoops) {
for (int frame = 0; frame < kNumFrames; frame++) {
// Prep for rendering.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = getNoClearRenderPass();
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);

View File

@@ -215,7 +215,7 @@ TEST_F(LoadImageTest, UpdateImage2D) {
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
// All of these test cases should result in the same rendered image, and thus the same hash.
static const uint32_t expectedHash = 3644679986;
static const uint32_t expectedHash = 1875922935;
struct TestCase {
const char* name;
@@ -417,7 +417,7 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
defaultRenderTarget, swapChain, shader.getProgram());
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "UpdateImageSRGB", 359858623));
ScreenshotParams(512, 512, "UpdateImageSRGB", 3300305265));
api.commit(swapChain);
api.endFrame(0);
@@ -476,7 +476,7 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
defaultRenderTarget, swapChain, shader.getProgram());
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "UpdateImageMipLevel", 3644679986));
ScreenshotParams(512, 512, "UpdateImageMipLevel", 1875922935));
api.commit(swapChain);
api.endFrame(0);
@@ -550,7 +550,7 @@ TEST_F(LoadImageTest, UpdateImage3D) {
defaultRenderTarget, swapChain, shader.getProgram());
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "UpdateImage3D", 3644679986));
ScreenshotParams(512, 512, "UpdateImage3D", 1875922935));
}
api.stopCapture();

View File

@@ -104,12 +104,9 @@ TEST_F(BackendTest, MRT) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = {0.f, 1.f, 0.f, 1.f};
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
api.startCapture(0);

View File

@@ -138,13 +138,12 @@ TEST_F(BackendTest, TextureViewLod) {
TargetBufferFlags::COLOR, 32, 32, 1, 0,
{texture, 2 /* level */, 0 /* layer */}, {}, {}));
{
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state = getColorWritePipelineState();
whiteShader.addProgramToPipelineState(state);
RenderPassParams params = getNoClearRenderPass();
params.viewport = getFullViewport();
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
@@ -153,15 +152,12 @@ TEST_F(BackendTest, TextureViewLod) {
backend::Handle<HwRenderTarget> defaultRenderTarget =
cleanup.add(api.createDefaultRenderTarget(0));
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = {0.f, 0.f, 0.5f, 1.f};
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state = getColorWritePipelineState();
texturedShader.addProgramToPipelineState(state);
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
DescriptorSetHandle descriptorSet13 = texturedShader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet13, 0, texture13, {
.filterMag = SamplerMagFilter::NEAREST,

View File

@@ -81,12 +81,9 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
api.startCapture(0);

View File

@@ -130,12 +130,8 @@ TEST_F(BackendTest, PushConstants) {
TrianglePrimitive triangle(api);
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0;
params.viewport = { 0, 0, 512, 512 };
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
PipelineState ps = {};
ps.program = program;
@@ -183,7 +179,8 @@ TEST_F(BackendTest, PushConstants) {
api.endRenderPass();
EXPECT_IMAGE(renderTarget, getExpectations(),
ScreenshotParams(512, 512, "pushConstants", 1957275826));
ScreenshotParams(params.viewport.width, params.viewport.height, "pushConstants",
3575588741));
api.commit(swapChain);
api.endFrame(0);

View File

@@ -279,14 +279,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
TrianglePrimitive const triangle(api);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = t.getRenderTargetSize();
RenderPassParams params = getClearColorRenderPass(math::float4(0, 0, 1, 1));
params.viewport.width = t.getRenderTargetSize();
params.viewport.height = t.getRenderTargetSize();
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
@@ -399,14 +394,10 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = renderTargetSize;
RenderPassParams params = getClearColorRenderPass(math::float4(0, 0, 1, 1));
params.viewport.width = renderTargetSize;
params.viewport.height = renderTargetSize;
void* buffer = calloc(1, renderTargetSize * renderTargetSize * 4);

View File

@@ -75,12 +75,9 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
@@ -175,12 +172,9 @@ TEST_F(BackendTest, RenderExternalImage) {
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
RenderPassParams params = {};
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
RenderPassParams params = getClearColorRenderPass();
params.viewport = getFullViewport();
api.startCapture(0);
api.makeCurrent(swapChain, swapChain);
@@ -198,7 +192,7 @@ TEST_F(BackendTest, RenderExternalImage) {
api.commit(swapChain);
api.endFrame(0);
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "RenderExternalImage", 267229901));
ScreenshotParams(512, 512, "RenderExternalImage", 1206264951));
api.stopCapture(0);
api.finish();

View File

@@ -114,12 +114,8 @@ TEST_F(BackendTest, ScissorViewportRegion) {
TrianglePrimitive triangle(api);
// Render a white triangle over blue.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0;
RenderPassParams params = getClearColorRenderPass();
params.viewport = srcRect;
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = getColorWritePipelineState();
shader.addProgramToPipelineState(ps);
@@ -133,7 +129,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
api.endRenderPass();
EXPECT_IMAGE(fullRenderTarget, getExpectations(),
ScreenshotParams(kSrcTexWidth >> 1, kSrcTexHeight >> 1, "scissor", 0xAB3D1C53));
ScreenshotParams(kSrcTexWidth >> 1, kSrcTexHeight >> 1, "scissor", 15842520));
api.commit(swapChain);
api.endFrame(0);
@@ -194,12 +190,8 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
TrianglePrimitive triangle(api);
// Render a white triangle over blue.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0;
RenderPassParams params = getClearColorRenderPass();
params.viewport = bottomLeftViewport;
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = getColorWritePipelineState();
shader.addProgramToPipelineState(ps);
@@ -221,7 +213,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
api.endRenderPass();
EXPECT_IMAGE(renderTarget, getExpectations(),
ScreenshotParams(512, 512, "ScissorViewportEdgeCases", 0x6BF00F31));
ScreenshotParams(512, 512, "ScissorViewportEdgeCases", 2199186852));
api.commit(swapChain);
api.endFrame(0);

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.60.0"
spec.version = "1.60.1"
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.60.0/filament-v1.60.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.60.1/filament-v1.60.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -32,6 +32,7 @@
#include <getopt/getopt.h>
#include <iostream>
#include <string>// for printing usage/help
#include "generated/resources/resources.h"
#include "generated/resources/monkey.h"
@@ -63,7 +64,8 @@ static void printUsage(char* name) {
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, or metal\n"
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
" NOTE that webgpu is not yet functional (it is in development).\n"
);
const std::string from("EXEC");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
@@ -95,8 +97,11 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
app->config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
app->config.backend = Engine::Backend::METAL;
} else if (arg == "webgpu") {
app->config.backend = Engine::Backend::WEBGPU;
} else {
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
std::cerr << "Unrecognized backend. Must be "
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
exit(1);
}
break;

View File

@@ -35,6 +35,7 @@
#include <cmath>
#include <iostream>
#include <string>// for printing usage/help
#include "generated/resources/resources.h"
@@ -78,10 +79,6 @@ static void printUsage(char* name) {
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
" (note: webgpu is a no-op for now, printing backend\n"
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
" set at build time, includes the \n"
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
);
const std::string from("HELLOTRIANGLE");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {

View File

@@ -34,9 +34,12 @@
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <getopt/getopt.h>
#include <stb_image.h>
#include <iostream> // for cerr
#include <string> // for printing usage/help
#include "generated/resources/resources.h"
@@ -76,9 +79,63 @@ static constexpr uint16_t QUAD_INDICES[6] = {
3, 2, 1,
};
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
std::string usage("TEXTUREDQUAD renders a textured quad moving back and forth in a loop\n"
"Usage:\n"
" TEXTUREDQUAD [options]\n"
"Options:\n"
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, metal, or webgpu\n");
const std::string from("TEXTUREDQUAD");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
usage.replace(pos, from.length(), exec_name);
}
std::cout << usage;
}
static int handleCommandLineArguments(int argc, char* argv[], Config& config) {
static constexpr const char* OPTSTR = "ha:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ nullptr, 0, nullptr, 0 }
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printUsage(argv[0]);
exit(0);
case 'a':
if (arg == "opengl") {
config.backend = Engine::Backend::OPENGL;
} else if (arg == "vulkan") {
config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
config.backend = Engine::Backend::METAL;
} else if (arg == "webgpu") {
config.backend = Engine::Backend::WEBGPU;
} else {
std::cerr << "Unrecognized backend. Must be "
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
exit(1);
}
break;
}
}
return optind;
}
int main(int argc, char** argv) {
Config config;
config.title = "texturedquad";
handleCommandLineArguments(argc, argv, config);
App app;
auto setup = [&app](Engine* engine, View* view, Scene* scene) {

View File

@@ -155,15 +155,15 @@ Light getLight(const uint lightIndex) {
light.channels = int(channels);
light.contactShadows = bool(typeShadow & 0x10u);
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
light.type = (typeShadow & 0x1u);
light.lightType = (typeShadow & 0x1u);
#if defined(VARIANT_HAS_SHADOWING)
light.shadowIndex = int((typeShadow >> 8u) & 0xFFu);
light.castsShadows = bool(channels & 0x10000u);
if (light.type == LIGHT_TYPE_SPOT) {
if (light.lightType == LIGHT_TYPE_SPOT) {
light.zLight = dot(shadowUniforms.shadows[light.shadowIndex].lightFromWorldZ, vec4(worldPosition, 1.0));
}
#endif
if (light.type == LIGHT_TYPE_SPOT) {
if (light.lightType == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
}
#endif
@@ -211,7 +211,7 @@ void evaluatePunctualLights(const MaterialInputs material,
if (light.NoL > 0.0) {
if (light.castsShadows) {
int shadowIndex = light.shadowIndex;
if (light.type == LIGHT_TYPE_POINT) {
if (light.lightType == LIGHT_TYPE_POINT) {
// point-light shadows are sampled from a direction
highp vec3 r = getWorldPosition() - light.worldPosition;
int face = getPointLightFace(r);

View File

@@ -8,7 +8,7 @@ struct Light {
float zLight;
bool castsShadows;
bool contactShadows;
uint type;
uint lightType;
int shadowIndex;
int channels;
};

View File

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