Compare commits
1 Commits
pf/renderd
...
ajh/textur
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a13ef02db |
@@ -290,7 +290,9 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUSwapChain.cpp
|
||||
src/webgpu/WebGPUSwapChain.h
|
||||
src/webgpu/WebGPUTexture.cpp
|
||||
src/webgpu/WebGPUTexture.h
|
||||
src/webgpu/WebGPUTextureHelpers.h
|
||||
src/webgpu/WebGPUTextureSwizzler.cpp
|
||||
src/webgpu/WebGPUTextureSwizzler.h
|
||||
src/webgpu/WebGPUVertexBuffer.cpp
|
||||
src/webgpu/WebGPUVertexBuffer.h
|
||||
src/webgpu/WebGPUVertexBufferInfo.cpp
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "WebGPUStrings.h"
|
||||
#include "WebGPUSwapChain.h"
|
||||
#include "WebGPUTexture.h"
|
||||
#include "WebGPUTextureSwizzler.h"
|
||||
#include "WebGPUVertexBuffer.h"
|
||||
#include "WebGPUVertexBufferInfo.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
@@ -77,6 +78,7 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
||||
mPipelineCache{ mDevice },
|
||||
mRenderPassMipmapGenerator{ mDevice },
|
||||
mSpdComputePassMipmapGenerator{ mDevice },
|
||||
mTextureSwizzler{ mDevice },
|
||||
mHandleAllocator{ "Handles", driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags } {
|
||||
mDevice.GetLimits(&mDeviceLimits);
|
||||
@@ -407,10 +409,24 @@ void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
|
||||
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
|
||||
const backend::TextureSwizzle a) {
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle red,
|
||||
const backend::TextureSwizzle green, const backend::TextureSwizzle blue,
|
||||
const backend::TextureSwizzle alpha) {
|
||||
auto source{ handleCast<WebGPUTexture>(sourceTextureHandle) };
|
||||
auto destination{ handleCast<WebGPUTexture>(textureHandle) };
|
||||
// TODO fix this. This does not work. The "destination" texture at this point is "junk", meaning
|
||||
// we need to create it here. Also, we should NOT do the swizzling at this point either,
|
||||
// as we should track the swizzle parameters such that we can have swizzles of swizzles
|
||||
// (they can be stacked), and we should only do the actual swizzle action below when it
|
||||
// time to use the texture. As a result, we are abandoning this custom implementation
|
||||
// in favor of using Dawn's texture sizzle view when we can (otherwise tell Filament above
|
||||
// that we can't swizzle textures).
|
||||
PANIC_POSTCONDITION("The texture swizzling implementation is not complete. We need to support "
|
||||
"swizzle stacking and lazy swizzle computation at the appropriate places "
|
||||
"fot this implementation to be viable. Most likely, we should use Dawn's "
|
||||
"texture swizzle view or nothing.");
|
||||
mTextureSwizzler.swizzle(mQueue, source->getTexture(), destination->getTexture(), red, green,
|
||||
blue, alpha);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
@@ -554,7 +570,7 @@ bool WebGPUDriver::isTextureFormatSupported(const TextureFormat format) {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "webgpu/WebGPUPipelineCache.h"
|
||||
#include "webgpu/WebGPUPipelineLayoutCache.h"
|
||||
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
|
||||
#include "webgpu/WebGPUTextureSwizzler.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
@@ -85,6 +86,7 @@ private:
|
||||
WebGPURenderPassMipmapGenerator mRenderPassMipmapGenerator;
|
||||
spd::MipmapGenerator mSpdComputePassMipmapGenerator;
|
||||
WebGPUMsaaTextureResolver mMsaaTextureResolver{};
|
||||
WebGPUTextureSwizzler mTextureSwizzler;
|
||||
|
||||
struct DescriptorSetBindingInfo{
|
||||
wgpu::BindGroup bindGroup;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "WebGPUMsaaTextureResolver.h"
|
||||
|
||||
#include "WebGPUTexture.h"
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "WebGPUPipelineCache.h"
|
||||
|
||||
#include "WebGPUConstants.h"
|
||||
#include "WebGPUTexture.h"
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
#include "WebGPUVertexBufferInfo.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "WebGPURenderPassMipmapGenerator.h"
|
||||
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
@@ -297,132 +299,6 @@ WebGPURenderPassMipmapGenerator::getCompatibilityFor(const wgpu::TextureFormat f
|
||||
return { .compatible = true, .reason = "" };
|
||||
}
|
||||
|
||||
constexpr WebGPURenderPassMipmapGenerator::ScalarSampleType
|
||||
WebGPURenderPassMipmapGenerator::getScalarSampleTypeFrom(const wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::R8Uint:
|
||||
case wgpu::TextureFormat::R16Uint:
|
||||
case wgpu::TextureFormat::R32Uint:
|
||||
case wgpu::TextureFormat::Stencil8:
|
||||
case wgpu::TextureFormat::RG8Uint:
|
||||
case wgpu::TextureFormat::RG16Uint:
|
||||
case wgpu::TextureFormat::RGBA8Uint:
|
||||
case wgpu::TextureFormat::RGB10A2Uint:
|
||||
case wgpu::TextureFormat::RG32Uint:
|
||||
case wgpu::TextureFormat::RGBA16Uint:
|
||||
case wgpu::TextureFormat::RGBA32Uint:
|
||||
return ScalarSampleType::U32;
|
||||
case wgpu::TextureFormat::R8Sint:
|
||||
case wgpu::TextureFormat::R16Sint:
|
||||
case wgpu::TextureFormat::R32Sint:
|
||||
case wgpu::TextureFormat::RG8Sint:
|
||||
case wgpu::TextureFormat::RG16Sint:
|
||||
case wgpu::TextureFormat::RGBA8Sint:
|
||||
case wgpu::TextureFormat::RG32Sint:
|
||||
case wgpu::TextureFormat::RGBA16Sint:
|
||||
case wgpu::TextureFormat::RGBA32Sint:
|
||||
return ScalarSampleType::I32;
|
||||
case wgpu::TextureFormat::R8Snorm:
|
||||
case wgpu::TextureFormat::R8Unorm:
|
||||
case wgpu::TextureFormat::R16Float:
|
||||
case wgpu::TextureFormat::RG16Float:
|
||||
case wgpu::TextureFormat::RG8Unorm:
|
||||
case wgpu::TextureFormat::RG8Snorm:
|
||||
case wgpu::TextureFormat::R32Float:
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
||||
case wgpu::TextureFormat::RGBA8Snorm:
|
||||
case wgpu::TextureFormat::BGRA8Unorm:
|
||||
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
||||
case wgpu::TextureFormat::RGB10A2Unorm:
|
||||
case wgpu::TextureFormat::RG11B10Ufloat:
|
||||
case wgpu::TextureFormat::RGB9E5Ufloat:
|
||||
case wgpu::TextureFormat::RG32Float:
|
||||
case wgpu::TextureFormat::RGBA16Float:
|
||||
case wgpu::TextureFormat::RGBA32Float:
|
||||
case wgpu::TextureFormat::BC1RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC2RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC3RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC4RUnorm:
|
||||
case wgpu::TextureFormat::BC4RSnorm:
|
||||
case wgpu::TextureFormat::BC5RGUnorm:
|
||||
case wgpu::TextureFormat::BC5RGSnorm:
|
||||
case wgpu::TextureFormat::BC6HRGBUfloat:
|
||||
case wgpu::TextureFormat::BC6HRGBFloat:
|
||||
case wgpu::TextureFormat::BC7RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGB8Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGB8UnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGB8A1Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGBA8Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb:
|
||||
case wgpu::TextureFormat::EACR11Unorm:
|
||||
case wgpu::TextureFormat::EACR11Snorm:
|
||||
case wgpu::TextureFormat::EACRG11Unorm:
|
||||
case wgpu::TextureFormat::EACRG11Snorm:
|
||||
case wgpu::TextureFormat::ASTC4x4Unorm:
|
||||
case wgpu::TextureFormat::ASTC4x4UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC5x4Unorm:
|
||||
case wgpu::TextureFormat::ASTC5x4UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC5x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC5x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC6x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC6x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC6x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC6x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x8Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x8UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x8Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x8UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x10Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x10UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC12x10Unorm:
|
||||
case wgpu::TextureFormat::ASTC12x10UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC12x12Unorm:
|
||||
case wgpu::TextureFormat::ASTC12x12UnormSrgb:
|
||||
case wgpu::TextureFormat::R16Unorm:
|
||||
case wgpu::TextureFormat::RG16Unorm:
|
||||
case wgpu::TextureFormat::RGBA16Unorm:
|
||||
case wgpu::TextureFormat::R16Snorm:
|
||||
case wgpu::TextureFormat::RG16Snorm:
|
||||
case wgpu::TextureFormat::RGBA16Snorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar444Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm:
|
||||
return ScalarSampleType::F32;
|
||||
case wgpu::TextureFormat::Depth16Unorm:
|
||||
case wgpu::TextureFormat::Depth24Plus:
|
||||
case wgpu::TextureFormat::Depth24PlusStencil8:
|
||||
case wgpu::TextureFormat::Depth32Float:
|
||||
case wgpu::TextureFormat::Depth32FloatStencil8:
|
||||
PANIC_POSTCONDITION("A depth texture format requires special sampler treatment and "
|
||||
"does not generally support linear filtering (needed for mipmap "
|
||||
"generation). Thus no applicable scalar sample type for %d",
|
||||
format);
|
||||
break;
|
||||
case wgpu::TextureFormat::Undefined:
|
||||
case wgpu::TextureFormat::External:
|
||||
PANIC_POSTCONDITION("No scalar sample type for texture format %d", format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPURenderPassMipmapGenerator::generateMipmaps(wgpu::Queue const& queue,
|
||||
wgpu::Texture const& texture) {
|
||||
const uint32_t mipLevelCount{ texture.GetMipLevelCount() };
|
||||
@@ -436,7 +312,7 @@ void WebGPURenderPassMipmapGenerator::generateMipmaps(wgpu::Queue const& queue,
|
||||
const wgpu::CommandEncoder commandEncoder{ mDevice.CreateCommandEncoder(
|
||||
&commandEncoderDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(commandEncoder)
|
||||
<< "Failed to create command encoder for layer for render pass mipmap generation?";
|
||||
<< "Failed to create command encoder for layer for render pass mipmap generation?";
|
||||
const uint32_t layerCount{ texture.GetDepthOrArrayLayers() };
|
||||
for (uint32_t layer = 0; layer < layerCount; layer++) {
|
||||
for (uint32_t mipLevel = 1; mipLevel < mipLevelCount; mipLevel++) {
|
||||
@@ -449,7 +325,7 @@ void WebGPURenderPassMipmapGenerator::generateMipmaps(wgpu::Queue const& queue,
|
||||
};
|
||||
const wgpu::CommandBuffer commandBuffer{ commandEncoder.Finish(&commandBufferDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(commandBuffer)
|
||||
<< "Failed to create command buffer for layer for render pass mipmap generation?";
|
||||
<< "Failed to create command buffer for layer for render pass mipmap generation?";
|
||||
queue.Submit(1, &commandBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,15 +42,6 @@ public:
|
||||
void generateMipmaps(wgpu::Queue const&, wgpu::Texture const&);
|
||||
|
||||
private:
|
||||
enum class ScalarSampleType : uint8_t {
|
||||
F32,
|
||||
I32,
|
||||
U32,
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr static ScalarSampleType getScalarSampleTypeFrom(
|
||||
wgpu::TextureFormat format);
|
||||
|
||||
[[nodiscard]] wgpu::RenderPipeline& getOrCreatePipelineFor(wgpu::TextureFormat);
|
||||
|
||||
void generateMipmap(wgpu::CommandEncoder const&, wgpu::Texture const&,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "WebGPUConstants.h"
|
||||
#include "WebGPURenderPassMipmapGenerator.h"
|
||||
#include "WebGPUStrings.h"
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/BackendUtils.h"
|
||||
@@ -64,37 +65,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param format texture format to potentially be used for storage binding
|
||||
* @return true if the format is compatible with wgpu::TextureUsage::StorageBinding
|
||||
*/
|
||||
[[nodiscard]] constexpr bool isFormatStorageCompatible(const wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
// List of formats that support storage binding
|
||||
case wgpu::TextureFormat::R32Float:
|
||||
case wgpu::TextureFormat::R32Sint:
|
||||
case wgpu::TextureFormat::R32Uint:
|
||||
case wgpu::TextureFormat::RG32Float:
|
||||
case wgpu::TextureFormat::RG32Sint:
|
||||
case wgpu::TextureFormat::RG32Uint:
|
||||
case wgpu::TextureFormat::RGBA16Float:
|
||||
case wgpu::TextureFormat::RGBA16Sint:
|
||||
case wgpu::TextureFormat::RGBA16Uint:
|
||||
case wgpu::TextureFormat::RGBA32Float:
|
||||
case wgpu::TextureFormat::RGBA32Sint:
|
||||
case wgpu::TextureFormat::RGBA32Uint:
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
case wgpu::TextureFormat::RGBA8Snorm:
|
||||
case wgpu::TextureFormat::RGBA8Uint:
|
||||
case wgpu::TextureFormat::RGBA8Sint:
|
||||
return true;
|
||||
default:
|
||||
// All other formats, including packed floats (RG11B10Ufloat),
|
||||
// depth/stencil, and sRGB formats do not support storage.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param viewFormat potential view format to a texture
|
||||
* @return an underlying texture format that is storage binding compatible for which this view
|
||||
@@ -249,18 +219,6 @@ namespace {
|
||||
return wgpu::TextureAspect::All;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::TextureViewDimension toWebGPUTextureViewDimension(
|
||||
const SamplerType samplerType) {
|
||||
switch (samplerType) {
|
||||
case SamplerType::SAMPLER_2D: return wgpu::TextureViewDimension::e2D;
|
||||
case SamplerType::SAMPLER_2D_ARRAY: return wgpu::TextureViewDimension::e2DArray;
|
||||
case SamplerType::SAMPLER_CUBEMAP: return wgpu::TextureViewDimension::Cube;
|
||||
case SamplerType::SAMPLER_EXTERNAL: return wgpu::TextureViewDimension::e2D;
|
||||
case SamplerType::SAMPLER_3D: return wgpu::TextureViewDimension::e3D;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY: return wgpu::TextureViewDimension::CubeArray;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::TextureDimension toWebGPUTextureDimension(
|
||||
const SamplerType samplerType) {
|
||||
switch (samplerType) {
|
||||
|
||||
@@ -26,20 +26,6 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
[[nodiscard]] constexpr bool hasStencil(const wgpu::TextureFormat textureFormat) {
|
||||
return textureFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Stencil8;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool hasDepth(const wgpu::TextureFormat textureFormat) {
|
||||
return textureFormat == wgpu::TextureFormat::Depth16Unorm ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32Float ||
|
||||
textureFormat == wgpu::TextureFormat::Depth24Plus ||
|
||||
textureFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
}
|
||||
|
||||
class WebGPUTexture : public HwTexture {
|
||||
public:
|
||||
enum class MipmapGenerationStrategy : uint8_t {
|
||||
|
||||
226
filament/backend/src/webgpu/WebGPUTextureHelpers.h
Normal file
226
filament/backend/src/webgpu/WebGPUTextureHelpers.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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_FILAMENT_BACKEND_WEBGPUTEXTUREHELPERS_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUTEXTUREHELPERS_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
[[nodiscard]] constexpr bool hasStencil(const wgpu::TextureFormat textureFormat) {
|
||||
return textureFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Stencil8;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool hasDepth(const wgpu::TextureFormat textureFormat) {
|
||||
return textureFormat == wgpu::TextureFormat::Depth16Unorm ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32Float ||
|
||||
textureFormat == wgpu::TextureFormat::Depth24Plus ||
|
||||
textureFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param format texture format to potentially be used for storage binding
|
||||
* @return true if the format is compatible with wgpu::TextureUsage::StorageBinding
|
||||
*/
|
||||
[[nodiscard]] constexpr bool isFormatStorageCompatible(const wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
// List of formats that support storage binding
|
||||
case wgpu::TextureFormat::R32Float:
|
||||
case wgpu::TextureFormat::R32Sint:
|
||||
case wgpu::TextureFormat::R32Uint:
|
||||
case wgpu::TextureFormat::RG32Float:
|
||||
case wgpu::TextureFormat::RG32Sint:
|
||||
case wgpu::TextureFormat::RG32Uint:
|
||||
case wgpu::TextureFormat::RGBA16Float:
|
||||
case wgpu::TextureFormat::RGBA16Sint:
|
||||
case wgpu::TextureFormat::RGBA16Uint:
|
||||
case wgpu::TextureFormat::RGBA32Float:
|
||||
case wgpu::TextureFormat::RGBA32Sint:
|
||||
case wgpu::TextureFormat::RGBA32Uint:
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
case wgpu::TextureFormat::RGBA8Snorm:
|
||||
case wgpu::TextureFormat::RGBA8Uint:
|
||||
case wgpu::TextureFormat::RGBA8Sint:
|
||||
return true;
|
||||
default:
|
||||
// All other formats, including packed floats (RG11B10Ufloat),
|
||||
// depth/stencil, and sRGB formats do not support storage.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::TextureViewDimension toWebGPUTextureViewDimension(
|
||||
const SamplerType samplerType) {
|
||||
switch (samplerType) {
|
||||
case SamplerType::SAMPLER_2D: return wgpu::TextureViewDimension::e2D;
|
||||
case SamplerType::SAMPLER_2D_ARRAY: return wgpu::TextureViewDimension::e2DArray;
|
||||
case SamplerType::SAMPLER_CUBEMAP: return wgpu::TextureViewDimension::Cube;
|
||||
case SamplerType::SAMPLER_EXTERNAL: return wgpu::TextureViewDimension::e2D;
|
||||
case SamplerType::SAMPLER_3D: return wgpu::TextureViewDimension::e3D;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY: return wgpu::TextureViewDimension::CubeArray;
|
||||
}
|
||||
}
|
||||
|
||||
enum class ScalarSampleType : uint8_t {
|
||||
F32,
|
||||
I32,
|
||||
U32,
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr ScalarSampleType getScalarSampleTypeFrom(const wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::R8Uint:
|
||||
case wgpu::TextureFormat::R16Uint:
|
||||
case wgpu::TextureFormat::R32Uint:
|
||||
case wgpu::TextureFormat::Stencil8:
|
||||
case wgpu::TextureFormat::RG8Uint:
|
||||
case wgpu::TextureFormat::RG16Uint:
|
||||
case wgpu::TextureFormat::RGBA8Uint:
|
||||
case wgpu::TextureFormat::RGB10A2Uint:
|
||||
case wgpu::TextureFormat::RG32Uint:
|
||||
case wgpu::TextureFormat::RGBA16Uint:
|
||||
case wgpu::TextureFormat::RGBA32Uint:
|
||||
return ScalarSampleType::U32;
|
||||
case wgpu::TextureFormat::R8Sint:
|
||||
case wgpu::TextureFormat::R16Sint:
|
||||
case wgpu::TextureFormat::R32Sint:
|
||||
case wgpu::TextureFormat::RG8Sint:
|
||||
case wgpu::TextureFormat::RG16Sint:
|
||||
case wgpu::TextureFormat::RGBA8Sint:
|
||||
case wgpu::TextureFormat::RG32Sint:
|
||||
case wgpu::TextureFormat::RGBA16Sint:
|
||||
case wgpu::TextureFormat::RGBA32Sint:
|
||||
return ScalarSampleType::I32;
|
||||
case wgpu::TextureFormat::R8Snorm:
|
||||
case wgpu::TextureFormat::R8Unorm:
|
||||
case wgpu::TextureFormat::R16Float:
|
||||
case wgpu::TextureFormat::RG16Float:
|
||||
case wgpu::TextureFormat::RG8Unorm:
|
||||
case wgpu::TextureFormat::RG8Snorm:
|
||||
case wgpu::TextureFormat::R32Float:
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
||||
case wgpu::TextureFormat::RGBA8Snorm:
|
||||
case wgpu::TextureFormat::BGRA8Unorm:
|
||||
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
||||
case wgpu::TextureFormat::RGB10A2Unorm:
|
||||
case wgpu::TextureFormat::RG11B10Ufloat:
|
||||
case wgpu::TextureFormat::RGB9E5Ufloat:
|
||||
case wgpu::TextureFormat::RG32Float:
|
||||
case wgpu::TextureFormat::RGBA16Float:
|
||||
case wgpu::TextureFormat::RGBA32Float:
|
||||
case wgpu::TextureFormat::BC1RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC2RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC3RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC4RUnorm:
|
||||
case wgpu::TextureFormat::BC4RSnorm:
|
||||
case wgpu::TextureFormat::BC5RGUnorm:
|
||||
case wgpu::TextureFormat::BC5RGSnorm:
|
||||
case wgpu::TextureFormat::BC6HRGBUfloat:
|
||||
case wgpu::TextureFormat::BC6HRGBFloat:
|
||||
case wgpu::TextureFormat::BC7RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGB8Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGB8UnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGB8A1Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGBA8Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb:
|
||||
case wgpu::TextureFormat::EACR11Unorm:
|
||||
case wgpu::TextureFormat::EACR11Snorm:
|
||||
case wgpu::TextureFormat::EACRG11Unorm:
|
||||
case wgpu::TextureFormat::EACRG11Snorm:
|
||||
case wgpu::TextureFormat::ASTC4x4Unorm:
|
||||
case wgpu::TextureFormat::ASTC4x4UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC5x4Unorm:
|
||||
case wgpu::TextureFormat::ASTC5x4UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC5x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC5x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC6x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC6x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC6x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC6x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x8Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x8UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x8Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x8UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x10Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x10UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC12x10Unorm:
|
||||
case wgpu::TextureFormat::ASTC12x10UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC12x12Unorm:
|
||||
case wgpu::TextureFormat::ASTC12x12UnormSrgb:
|
||||
case wgpu::TextureFormat::R16Unorm:
|
||||
case wgpu::TextureFormat::RG16Unorm:
|
||||
case wgpu::TextureFormat::RGBA16Unorm:
|
||||
case wgpu::TextureFormat::R16Snorm:
|
||||
case wgpu::TextureFormat::RG16Snorm:
|
||||
case wgpu::TextureFormat::RGBA16Snorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar444Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm:
|
||||
return ScalarSampleType::F32;
|
||||
case wgpu::TextureFormat::Depth16Unorm:
|
||||
case wgpu::TextureFormat::Depth24Plus:
|
||||
case wgpu::TextureFormat::Depth24PlusStencil8:
|
||||
case wgpu::TextureFormat::Depth32Float:
|
||||
case wgpu::TextureFormat::Depth32FloatStencil8:
|
||||
PANIC_POSTCONDITION("A depth texture format requires special sampler treatment and "
|
||||
"does not generally support linear filtering (needed for mipmap "
|
||||
"generation). Thus no applicable scalar sample type for %d",
|
||||
format);
|
||||
break;
|
||||
case wgpu::TextureFormat::Undefined:
|
||||
case wgpu::TextureFormat::External:
|
||||
PANIC_POSTCONDITION("No scalar sample type for texture format %d", format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view toWGSLString(const ScalarSampleType type) {
|
||||
switch (type) {
|
||||
case ScalarSampleType::F32: return "f32";
|
||||
case ScalarSampleType::I32: return "i32";
|
||||
case ScalarSampleType::U32: return "u32";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUTEXTUREHELPERS_H
|
||||
537
filament/backend/src/webgpu/WebGPUTextureSwizzler.cpp
Normal file
537
filament/backend/src/webgpu/WebGPUTextureSwizzler.cpp
Normal file
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
* 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 "WebGPUTextureSwizzler.h"
|
||||
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream> // for shader template search and replace
|
||||
#include <string> // for shader template search and replace
|
||||
#include <string_view>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
// Note: would be nice to generate the constexpr string for the template from the constexpr
|
||||
// variables below, e.g. TEXTURE_BINDING_INDEX, to avoid them getting out of sync while still having
|
||||
// the compile/build time optimization
|
||||
constexpr uint32_t BIND_GROUP_INDEX{ 0 };
|
||||
constexpr size_t BIND_GROUP_ENTRY_SIZE{ 2 }; // texture and uniform
|
||||
constexpr uint32_t TEXTURE_BINDING_INDEX{ 0 };
|
||||
constexpr uint32_t UNIFORM_BINDING_INDEX{ 1 };
|
||||
constexpr std::string_view VERTEX_SHADER_ENTRY_POINT{ "vertexShaderMain" };
|
||||
constexpr std::string_view FRAGMENT_SHADER_ENTRY_POINT{ "fragmentShaderMain" };
|
||||
// note that the placeholders below must start and end with this prefix and suffix:
|
||||
constexpr std::string_view PLACEHOLDER_PREFIX{ "{{" };
|
||||
constexpr std::string_view PLACEHOLDER_SUFFIX{ "}}" };
|
||||
// texture_2d<...> or texture_multisampled_2d<...>
|
||||
constexpr std::string_view TEXTURE_TYPE_PLACEHOLDER{ "TEXTURE_TYPE" };
|
||||
// f32, i32, u32
|
||||
constexpr std::string_view SCALAR_TYPE_PLACEHOLDER{ "SCALAR_TYPE" };
|
||||
// empty for single-sampled, ", @builtin(sample_index) sampleIndex" for multi-sampled
|
||||
constexpr std::string_view SAMPLE_INDEX_PARAM_PLACEHOLDER{ "SAMPLE_INDEX_PARAM" };
|
||||
// "sampleIndex" for multi-sampled, "0" for single-sampled
|
||||
constexpr std::string_view SAMPLE_INDEX_OR_LEVEL_PLACEHOLDER{ "SAMPLE_INDEX_OR_LEVEL" };
|
||||
constexpr std::string_view SHADER_SOURCE_TEMPLATE{ R"(
|
||||
struct TextureSwizzle {
|
||||
redSource: u32,
|
||||
greenSource: u32,
|
||||
blueSource: u32,
|
||||
alphaSource: u32,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var sourceTexture: {{TEXTURE_TYPE}};
|
||||
@group(0) @binding(1) var<uniform> swizzle: TextureSwizzle;
|
||||
|
||||
fn getComponent(texel: vec4<{{SCALAR_TYPE}}>, index: u32) -> {{SCALAR_TYPE}} {
|
||||
switch index {
|
||||
case 0u: { return 0; }
|
||||
case 1u: { return 1; }
|
||||
case 2u: { return texel.r; }
|
||||
case 3u: { return texel.g; }
|
||||
case 4u: { return texel.b; }
|
||||
case 5u: { return texel.a; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertexShaderMain(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {
|
||||
let fullScreenTriangleVertices = array<vec2<f32>, 3>(
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>( 3.0, -1.0),
|
||||
vec2<f32>(-1.0, 3.0)
|
||||
);
|
||||
return vec4<f32>(fullScreenTriangleVertices[vertexIndex].xy, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragmentShaderMain(@builtin(position) vec4<f32> textureCoordinate{{SAMPLE_INDEX_PARAM}}) -> @location(0) vec4<{{SCALAR_TYPE}}> {
|
||||
let x = u32(textureCoordinate.x);
|
||||
let y = u32(textureCoordinate.y);
|
||||
let originalColor = textureLoad(sourceTexture, vec2<u32>(x, y), {{SAMPLE_INDEX_OR_LEVEL}});
|
||||
return vec4<{{SCALAR_TYPE}}>(
|
||||
getComponent(originalColor, swizzle.redSource),
|
||||
getComponent(originalColor, swizzle.greenSource),
|
||||
getComponent(originalColor, swizzle.blueSource),
|
||||
getComponent(originalColor, swizzle.alphaSource));
|
||||
}
|
||||
)" };
|
||||
|
||||
[[nodiscard]] constexpr uint32_t toSwizzleIndex(const TextureSwizzle swizzle) {
|
||||
switch (swizzle) {
|
||||
case TextureSwizzle::SUBSTITUTE_ZERO: return 0;
|
||||
case TextureSwizzle::SUBSTITUTE_ONE: return 1;
|
||||
case TextureSwizzle::CHANNEL_0: return 2;
|
||||
case TextureSwizzle::CHANNEL_1: return 3;
|
||||
case TextureSwizzle::CHANNEL_2: return 4;
|
||||
case TextureSwizzle::CHANNEL_3: return 5;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WebGPUTextureSwizzler::WebGPUTextureSwizzler(wgpu::Device const& device)
|
||||
: mDevice{ device } {}
|
||||
|
||||
void WebGPUTextureSwizzler::swizzle(wgpu::Queue const& queue, wgpu::Texture const& source,
|
||||
wgpu::Texture const& destination, const TextureSwizzle red, const TextureSwizzle green,
|
||||
const TextureSwizzle blue, const TextureSwizzle alpha) {
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetFormat() == destination.GetFormat())
|
||||
<< "Source and destination formats must match";
|
||||
FILAMENT_CHECK_PRECONDITION(!hasDepth(source.GetFormat()))
|
||||
<< "depth texture swizzling not supported";
|
||||
FILAMENT_CHECK_PRECONDITION(!hasStencil(source.GetFormat()))
|
||||
<< "stencil texture swizzling not supported";
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetDimension() == destination.GetDimension())
|
||||
<< "Source and destination dimensions must match";
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetDimension() == wgpu::TextureDimension::e2D)
|
||||
<< "Source and destination dimensions must be 2D";
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetWidth() == destination.GetWidth())
|
||||
<< "Source and destination widths must match";
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetHeight() == destination.GetHeight())
|
||||
<< "Source and destination heights must match";
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
source.GetDepthOrArrayLayers() == destination.GetDepthOrArrayLayers())
|
||||
<< "Source and destination depthOrArrayLayers must match";
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetMipLevelCount() == destination.GetMipLevelCount())
|
||||
<< "Source and destination mip level counts must match";
|
||||
FILAMENT_CHECK_PRECONDITION(source.GetSampleCount() == destination.GetSampleCount())
|
||||
<< "Source and destination sample counts must match";
|
||||
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
|
||||
.label = "texture_swizzle_render_pass_cmd_encoder",
|
||||
};
|
||||
const wgpu::CommandEncoder commandEncoder{ mDevice.CreateCommandEncoder(
|
||||
&commandEncoderDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(commandEncoder)
|
||||
<< "Failed to create command encoder for texture swizzle?";
|
||||
wgpu::BindGroupLayout const& bindGroupLayout{ getOrCreateBindGroupLayout(
|
||||
getScalarSampleTypeFrom(source.GetFormat()), source.GetSampleCount() > 1) };
|
||||
wgpu::RenderPipeline const& pipeline{ getOrCreateRenderPipelineFor(source.GetFormat(),
|
||||
source.GetSampleCount()) };
|
||||
// Create and upload the uniform buffer with swizzle parameters.
|
||||
uint32_t swizzleIndices[]{
|
||||
toSwizzleIndex(red),
|
||||
toSwizzleIndex(green),
|
||||
toSwizzleIndex(blue),
|
||||
toSwizzleIndex(alpha),
|
||||
};
|
||||
wgpu::BufferDescriptor bufferDescriptor{
|
||||
.label = "texture_swizzle_uniform_buffer",
|
||||
.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst,
|
||||
.size = sizeof(swizzleIndices),
|
||||
.mappedAtCreation = false, // explicitly set for consistency
|
||||
};
|
||||
const wgpu::Buffer uniformBuffer{ mDevice.CreateBuffer(&bufferDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(uniformBuffer)
|
||||
<< "Failed to create uniform buffer for texture swizzle?";
|
||||
queue.WriteBuffer(uniformBuffer, 0, swizzleIndices, sizeof(swizzleIndices));
|
||||
const uint32_t layerCount{ source.GetDepthOrArrayLayers() };
|
||||
for (uint32_t layer = 0; layer < layerCount; layer++) {
|
||||
for (uint32_t mipLevel = 1; mipLevel < source.GetMipLevelCount(); mipLevel++) {
|
||||
swizzle(commandEncoder, pipeline, bindGroupLayout, source, destination, layer, mipLevel,
|
||||
uniformBuffer);
|
||||
}
|
||||
}
|
||||
// submit the command buffer...
|
||||
const wgpu::CommandBufferDescriptor commandBufferDescriptor{
|
||||
.label = "texture_swizzle_render_pass_cmd_buffer",
|
||||
};
|
||||
const wgpu::CommandBuffer commandBuffer{ commandEncoder.Finish(&commandBufferDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(commandBuffer)
|
||||
<< "Failed to create command buffer for texture swizzle?";
|
||||
queue.Submit(1, &commandBuffer);
|
||||
}
|
||||
|
||||
void WebGPUTextureSwizzler::swizzle(wgpu::CommandEncoder const& commandEncoder,
|
||||
wgpu::RenderPipeline const& renderPipeline, wgpu::BindGroupLayout const& bindGroupLayout,
|
||||
wgpu::Texture const& source, wgpu::Texture const& destination, const uint32_t layer,
|
||||
const uint32_t mipLevel, wgpu::Buffer const& uniformBuffer) {
|
||||
// create texture views for this pass...
|
||||
const wgpu::TextureFormat format{ source.GetFormat() };
|
||||
// only 2D textures supported at this time...
|
||||
const wgpu::TextureViewDimension dimension{ wgpu::TextureViewDimension::e2D };
|
||||
const wgpu::TextureViewDescriptor sourceViewDescriptor{
|
||||
.label = "source_of_texture_swizzle_view",
|
||||
.format = format,
|
||||
.dimension = dimension,
|
||||
.baseMipLevel = mipLevel,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = layer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = wgpu::TextureAspect::All,
|
||||
.usage = wgpu::TextureUsage::TextureBinding,
|
||||
};
|
||||
const wgpu::TextureView sourceView{ source.CreateView(&sourceViewDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(sourceView)
|
||||
<< "Failed to create source texture view for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass texture swizzle?";
|
||||
const wgpu::TextureViewDescriptor destinationViewDescriptor{
|
||||
.label = "destination_of_texture_swizzle_view",
|
||||
.format = format,
|
||||
.dimension = dimension,
|
||||
.baseMipLevel = mipLevel,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = layer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = wgpu::TextureAspect::All,
|
||||
.usage = wgpu::TextureUsage::RenderAttachment,
|
||||
};
|
||||
const wgpu::TextureView destinationView{ destination.CreateView(&destinationViewDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(destinationView)
|
||||
<< "Failed to create destination texture view for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass texture swizzle?";
|
||||
// create the render pass...
|
||||
const wgpu::BindGroupEntry bindGroupEntries[BIND_GROUP_ENTRY_SIZE]{
|
||||
{
|
||||
.binding = TEXTURE_BINDING_INDEX,
|
||||
.textureView = sourceView,
|
||||
},
|
||||
{
|
||||
.binding = UNIFORM_BINDING_INDEX,
|
||||
.buffer = uniformBuffer,
|
||||
},
|
||||
};
|
||||
const wgpu::BindGroupDescriptor bindGroupDescriptor{
|
||||
.label = "render_pass_texture_swizzle_bind_group",
|
||||
.layout = bindGroupLayout,
|
||||
.entryCount = BIND_GROUP_ENTRY_SIZE,
|
||||
.entries = bindGroupEntries,
|
||||
};
|
||||
const wgpu::BindGroup bindGroup{ mDevice.CreateBindGroup(&bindGroupDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(bindGroup)
|
||||
<< "Failed to create bind group for layer " << layer << " and mip level " << mipLevel
|
||||
<< " for render pass texture swizzle?";
|
||||
const wgpu::RenderPassColorAttachment colorAttachment{
|
||||
.view = destinationView,
|
||||
.depthSlice = wgpu::kDepthSliceUndefined, // not applicable
|
||||
.resolveTarget = nullptr, // not applicable
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = { .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0 },
|
||||
};
|
||||
const wgpu::RenderPassDescriptor renderPassDescriptor{
|
||||
.label = "texture_swizzle_render_pass",
|
||||
.colorAttachmentCount = 1,
|
||||
.colorAttachments = &colorAttachment,
|
||||
.depthStencilAttachment = nullptr, // not applicable
|
||||
.occlusionQuerySet = nullptr, // not applicable
|
||||
.timestampWrites = nullptr, // not applicable
|
||||
};
|
||||
const wgpu::RenderPassEncoder renderPassEncoder{ commandEncoder.BeginRenderPass(
|
||||
&renderPassDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(renderPassEncoder)
|
||||
<< "Failed to create render pass encoder for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass texture swizzle?";
|
||||
renderPassEncoder.SetPipeline(renderPipeline);
|
||||
renderPassEncoder.SetBindGroup(BIND_GROUP_INDEX, bindGroup);
|
||||
renderPassEncoder.Draw(3); // draw the full-screen triangle
|
||||
// with hard-coded vertices in the shader
|
||||
renderPassEncoder.End();
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline const& WebGPUTextureSwizzler::getOrCreateRenderPipelineFor(
|
||||
const wgpu::TextureFormat format, const uint32_t sampleCount) {
|
||||
const bool multiSampled{ sampleCount > 1 };
|
||||
const size_t key{ hashPipelineKey(format, sampleCount) };
|
||||
if (mPipelines.find(key) == mPipelines.end()) {
|
||||
const ScalarSampleType scalarTextureFormat{ getScalarSampleTypeFrom(format) };
|
||||
const wgpu::ShaderModule& shaderModule{ getOrCreateShaderModuleFor(scalarTextureFormat,
|
||||
multiSampled) };
|
||||
const wgpu::PipelineLayout& pipelineLayout{ getOrCreatePipelineLayoutFor(
|
||||
scalarTextureFormat, multiSampled) };
|
||||
mPipelines[key] = createRenderPipeline(shaderModule, pipelineLayout, format, sampleCount);
|
||||
}
|
||||
return mPipelines[key];
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline WebGPUTextureSwizzler::createRenderPipeline(
|
||||
wgpu::ShaderModule const& shaderModule, wgpu::PipelineLayout const& pipelineLayout,
|
||||
const wgpu::TextureFormat textureFormat, const uint32_t sampleCount) {
|
||||
const wgpu::ColorTargetState colorTargetState{ .format = textureFormat };
|
||||
const wgpu::FragmentState fragmentState{
|
||||
.module = shaderModule,
|
||||
.entryPoint = VERTEX_SHADER_ENTRY_POINT,
|
||||
.constantCount = 0, // should not matter, just being consistently defined
|
||||
.constants = nullptr, // should not matter, just being consistently defined
|
||||
.targetCount = 1,
|
||||
.targets = &colorTargetState,
|
||||
};
|
||||
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
|
||||
.label = "render_pass_texture_swizzle_pipeline",
|
||||
.layout = pipelineLayout,
|
||||
.vertex = {
|
||||
.module = shaderModule,
|
||||
.entryPoint = FRAGMENT_SHADER_ENTRY_POINT,
|
||||
.constantCount = 0,
|
||||
.constants = nullptr,
|
||||
.bufferCount = 0,
|
||||
.buffers = nullptr,
|
||||
},
|
||||
.primitive = {
|
||||
.topology = wgpu::PrimitiveTopology::TriangleList,
|
||||
.stripIndexFormat = wgpu::IndexFormat::Undefined, // should not matter
|
||||
// (not using a strip topology),
|
||||
// just being consistently defined
|
||||
.frontFace = wgpu::FrontFace::Undefined, // should not matter (no cull mode),
|
||||
// just being consistently defined
|
||||
.cullMode = wgpu::CullMode::None,
|
||||
.unclippedDepth = false, // should not matter, just being consistently defined
|
||||
},
|
||||
.depthStencil = nullptr, // not applicable, but explicitly set here for
|
||||
// consistent definition
|
||||
.multisample = {
|
||||
.count = sampleCount,
|
||||
.mask = 0xFFFFFFFF,
|
||||
.alphaToCoverageEnabled = false,
|
||||
},
|
||||
.fragment = &fragmentState,
|
||||
};
|
||||
const wgpu::RenderPipeline pipeline{ mDevice.CreateRenderPipeline(&pipelineDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipeline)
|
||||
<< "Failed to create pipeline for render pass texture swizzle?";
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
wgpu::PipelineLayout const& WebGPUTextureSwizzler::getOrCreatePipelineLayoutFor(
|
||||
const ScalarSampleType scalarTextureFormat, const bool multiSampled) {
|
||||
const size_t key{ hashPipelineLayoutKey(scalarTextureFormat, multiSampled) };
|
||||
if (mPipelineLayouts.find(key) == mPipelineLayouts.end()) {
|
||||
mPipelineLayouts[key] = createPipelineLayout(scalarTextureFormat, multiSampled);
|
||||
}
|
||||
return mPipelineLayouts[key];
|
||||
}
|
||||
|
||||
wgpu::PipelineLayout WebGPUTextureSwizzler::createPipelineLayout(
|
||||
const ScalarSampleType scalarTextureFormat, const bool multiSampled) {
|
||||
wgpu::BindGroupLayout const& bindGroupLayout{ getOrCreateBindGroupLayout(scalarTextureFormat,
|
||||
multiSampled) };
|
||||
#ifndef NDEBUG
|
||||
std::stringstream labelStream{};
|
||||
labelStream << "texture_swizzle_pipeline_layout_" << toWGSLString(scalarTextureFormat)
|
||||
<< (multiSampled ? "_multisampled" : "_singlesampled");
|
||||
const wgpu::StringView label{ labelStream.str() };
|
||||
#else
|
||||
const wgpu::StringView label{ "texture_swizzle_pipeline_layout" };
|
||||
#endif
|
||||
const wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor{
|
||||
.label = label,
|
||||
.bindGroupLayoutCount = 1,
|
||||
.bindGroupLayouts = &bindGroupLayout,
|
||||
};
|
||||
const wgpu::PipelineLayout pipelineLayout{ mDevice.CreatePipelineLayout(
|
||||
&pipelineLayoutDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipelineLayout)
|
||||
<< "Failed to create pipeline layout for texture swizzling?";
|
||||
return pipelineLayout;
|
||||
}
|
||||
|
||||
wgpu::BindGroupLayout const& WebGPUTextureSwizzler::getOrCreateBindGroupLayout(
|
||||
const ScalarSampleType scalarTextureFormat, const bool multiSampled) {
|
||||
const size_t key{ hashBindGroupLayoutKey(scalarTextureFormat, multiSampled) };
|
||||
if (mBindGroupLayouts.find(key) == mBindGroupLayouts.end()) {
|
||||
mBindGroupLayouts[key] = createBindGroupLayout(scalarTextureFormat, multiSampled);
|
||||
}
|
||||
return mBindGroupLayouts[key];
|
||||
}
|
||||
|
||||
wgpu::BindGroupLayout WebGPUTextureSwizzler::createBindGroupLayout(
|
||||
const ScalarSampleType scalarTextureFormat, const bool multiSampled) {
|
||||
wgpu::TextureSampleType sampleType{ wgpu::TextureSampleType::Undefined };
|
||||
switch (scalarTextureFormat) {
|
||||
case ScalarSampleType::F32:
|
||||
sampleType = wgpu::TextureSampleType::UnfilterableFloat;
|
||||
break;
|
||||
case ScalarSampleType::I32:
|
||||
sampleType = wgpu::TextureSampleType::Sint;
|
||||
break;
|
||||
case ScalarSampleType::U32:
|
||||
sampleType = wgpu::TextureSampleType::Uint;
|
||||
break;
|
||||
}
|
||||
const wgpu::BindGroupLayoutEntry bindGroupLayoutEntries[BIND_GROUP_ENTRY_SIZE] {
|
||||
{
|
||||
.binding = TEXTURE_BINDING_INDEX,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture = {
|
||||
.sampleType = sampleType,
|
||||
.viewDimension = wgpu::TextureViewDimension::e2D, // only 2D supported for now
|
||||
.multisampled = multiSampled,
|
||||
},
|
||||
},
|
||||
{
|
||||
.binding = UNIFORM_BINDING_INDEX,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.buffer = {
|
||||
.type = wgpu::BufferBindingType::Uniform,
|
||||
.hasDynamicOffset = false, // set explicitly for consistency
|
||||
.minBindingSize = 0, // TODO set based on the swizzle data size
|
||||
},
|
||||
},
|
||||
};
|
||||
const wgpu::BindGroupLayoutDescriptor bindGroupLayoutDescriptor{
|
||||
.label = "render_pass_texture_swizzle_bind_group_layout",
|
||||
.entryCount = BIND_GROUP_ENTRY_SIZE,
|
||||
.entries = bindGroupLayoutEntries,
|
||||
};
|
||||
const wgpu::BindGroupLayout bindGroupLayout{ mDevice.CreateBindGroupLayout(
|
||||
&bindGroupLayoutDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(bindGroupLayout)
|
||||
<< "Failed to create texture bind group layout for render pass texture swizzle?";
|
||||
return bindGroupLayout;
|
||||
}
|
||||
|
||||
wgpu::ShaderModule const& WebGPUTextureSwizzler::getOrCreateShaderModuleFor(
|
||||
const ScalarSampleType scalarTextureFormat, const bool multiSampled) {
|
||||
const size_t key{ hashShaderModuleKey(scalarTextureFormat, multiSampled) };
|
||||
if (mShaderModules.find(key) == mShaderModules.end()) {
|
||||
const wgpu::ShaderModule shaderModule{ createShaderModule(scalarTextureFormat,
|
||||
multiSampled) };
|
||||
mShaderModules[key] = shaderModule;
|
||||
}
|
||||
return mShaderModules[key];
|
||||
}
|
||||
|
||||
wgpu::ShaderModule WebGPUTextureSwizzler::createShaderModule(
|
||||
const ScalarSampleType scalarTextureFormat, const bool multiSampled) {
|
||||
// build the proper source from the template...
|
||||
const std::string_view scalarType{ toWGSLString(scalarTextureFormat) };
|
||||
std::stringstream textureTypeStream{};
|
||||
textureTypeStream << (multiSampled ? "texture_multisampled_2d" : "texture_2d") << '<'
|
||||
<< scalarType << '>';
|
||||
const std::string textureType{ textureTypeStream.str() };
|
||||
const std::string_view sampleIndexParam{ multiSampled ? ", @builtin(sample_index) sampleIndex"
|
||||
: "" };
|
||||
const std::string_view sampleIndexOrLevel{ multiSampled ? "sampleIndex" : "0" };
|
||||
const char* const sourceData = SHADER_SOURCE_TEMPLATE.data();
|
||||
std::stringstream processedShaderSource{};
|
||||
size_t positionCursorInTemplateString{ 0 };
|
||||
while (positionCursorInTemplateString < SHADER_SOURCE_TEMPLATE.size()) {
|
||||
const size_t positionOfNextPlaceholder{ SHADER_SOURCE_TEMPLATE.find(PLACEHOLDER_PREFIX,
|
||||
positionCursorInTemplateString) };
|
||||
if (positionOfNextPlaceholder == std::string::npos) {
|
||||
// no more placeholders, so just stream the rest of the source code string
|
||||
processedShaderSource << std::string_view(sourceData + positionCursorInTemplateString,
|
||||
SHADER_SOURCE_TEMPLATE.size() - positionCursorInTemplateString);
|
||||
break;
|
||||
}
|
||||
const size_t positionOfPlaceholder{ positionOfNextPlaceholder + PLACEHOLDER_PREFIX.size() };
|
||||
// stream up to the placeholder...
|
||||
processedShaderSource << std::string_view(sourceData + positionCursorInTemplateString,
|
||||
positionOfPlaceholder);
|
||||
const size_t positionAfterPlaceholder{ SHADER_SOURCE_TEMPLATE.find(
|
||||
PLACEHOLDER_SUFFIX, positionOfPlaceholder) };
|
||||
assert_invariant(positionAfterPlaceholder != std::string::npos &&
|
||||
"Malformed shader with missing suffix to placeholder");
|
||||
const std::string_view placeholder{ std::string_view(sourceData + positionOfPlaceholder,
|
||||
positionAfterPlaceholder - positionOfPlaceholder) };
|
||||
// stream the value in place of the placeholder...
|
||||
if (placeholder == TEXTURE_TYPE_PLACEHOLDER) {
|
||||
processedShaderSource << textureType;
|
||||
} else if (placeholder == SCALAR_TYPE_PLACEHOLDER) {
|
||||
processedShaderSource << scalarType;
|
||||
} else if (placeholder == SAMPLE_INDEX_PARAM_PLACEHOLDER) {
|
||||
processedShaderSource << sampleIndexParam;
|
||||
} else if (placeholder == SAMPLE_INDEX_OR_LEVEL_PLACEHOLDER) {
|
||||
processedShaderSource << sampleIndexOrLevel;
|
||||
} else {
|
||||
PANIC_POSTCONDITION(
|
||||
"Unknown/unhandled placeholder %s when processing texture swizzler shader",
|
||||
placeholder.data());
|
||||
}
|
||||
// update the cursor for after the placeholder...
|
||||
positionCursorInTemplateString = positionAfterPlaceholder + PLACEHOLDER_SUFFIX.size();
|
||||
}
|
||||
const std::string shaderSource{ processedShaderSource.str() };
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||
wgslDescriptor.code = shaderSource.data();
|
||||
#ifndef NDEBUG
|
||||
std::stringstream labelStream{};
|
||||
labelStream << "texture_swizzle_shaders_" << scalarType
|
||||
<< (multiSampled ? "_multisampled" : "_singlesampled");
|
||||
const wgpu::StringView label{ labelStream.str() };
|
||||
#else
|
||||
const wgpu::StringView label{ "texture_swizzle_shaders" };
|
||||
#endif
|
||||
const wgpu::ShaderModuleDescriptor shaderModuleDescriptor{
|
||||
.nextInChain = &wgslDescriptor,
|
||||
.label = label,
|
||||
};
|
||||
const wgpu::ShaderModule shaderModule{ mDevice.CreateShaderModule(&shaderModuleDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(shaderModule)
|
||||
<< "Failed to create shader module for texture swizzling? scalarType " << scalarType
|
||||
<< " multiSampled " << multiSampled;
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
size_t WebGPUTextureSwizzler::hashBindGroupLayoutKey(const ScalarSampleType textureScalarFormat,
|
||||
const bool multiSampled) {
|
||||
size_t seed{ std::hash<uint32_t>{}(static_cast<uint8_t>(textureScalarFormat)) };
|
||||
utils::hash::combine(seed, multiSampled);
|
||||
return seed;
|
||||
}
|
||||
|
||||
size_t WebGPUTextureSwizzler::hashPipelineLayoutKey(const ScalarSampleType textureScalarFormat,
|
||||
const bool multiSampled) {
|
||||
size_t seed{ std::hash<uint32_t>{}(static_cast<uint8_t>(textureScalarFormat)) };
|
||||
utils::hash::combine(seed, multiSampled);
|
||||
return seed;
|
||||
}
|
||||
|
||||
size_t WebGPUTextureSwizzler::hashShaderModuleKey(const ScalarSampleType textureScalarFormat,
|
||||
const bool multiSampled) {
|
||||
size_t seed{ std::hash<uint32_t>{}(static_cast<uint8_t>(textureScalarFormat)) };
|
||||
utils::hash::combine(seed, multiSampled);
|
||||
return seed;
|
||||
}
|
||||
|
||||
size_t WebGPUTextureSwizzler::hashPipelineKey(const wgpu::TextureFormat format,
|
||||
const uint32_t sampleCount) {
|
||||
size_t seed{ std::hash<uint32_t>{}(static_cast<uint32_t>(format)) };
|
||||
utils::hash::combine(seed, sampleCount);
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
86
filament/backend/src/webgpu/WebGPUTextureSwizzler.h
Normal file
86
filament/backend/src/webgpu/WebGPUTextureSwizzler.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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_FILAMENT_BACKEND_WEBGPUTEXTURESWIZZLER_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUTEXTURESWIZZLER_H
|
||||
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUTextureSwizzler final {
|
||||
public:
|
||||
explicit WebGPUTextureSwizzler(wgpu::Device const&);
|
||||
|
||||
void swizzle(wgpu::Queue const&, wgpu::Texture const& source, wgpu::Texture const& destination,
|
||||
TextureSwizzle red, TextureSwizzle green, TextureSwizzle blue, TextureSwizzle alpha);
|
||||
|
||||
private:
|
||||
void swizzle(wgpu::CommandEncoder const&, wgpu::RenderPipeline const&,
|
||||
wgpu::BindGroupLayout const&, wgpu::Texture const& source,
|
||||
wgpu::Texture const& destination, uint32_t layer, uint32_t mipLevel,
|
||||
wgpu::Buffer const& uniformBuffer);
|
||||
|
||||
[[nodiscard]] wgpu::RenderPipeline const& getOrCreateRenderPipelineFor(wgpu::TextureFormat,
|
||||
uint32_t sampleCount);
|
||||
|
||||
[[nodiscard]] wgpu::RenderPipeline createRenderPipeline(wgpu::ShaderModule const&,
|
||||
wgpu::PipelineLayout const&, wgpu::TextureFormat, uint32_t sampleCount);
|
||||
|
||||
[[nodiscard]] wgpu::PipelineLayout const& getOrCreatePipelineLayoutFor(ScalarSampleType,
|
||||
bool multiSampled);
|
||||
|
||||
[[nodiscard]] wgpu::PipelineLayout createPipelineLayout(ScalarSampleType, bool multiSampled);
|
||||
|
||||
[[nodiscard]] wgpu::BindGroupLayout const& getOrCreateBindGroupLayout(
|
||||
ScalarSampleType scalarTextureFormat, bool multiSampled);
|
||||
|
||||
[[nodiscard]] wgpu::BindGroupLayout createBindGroupLayout(ScalarSampleType scalarTextureFormat,
|
||||
bool multiSampled);
|
||||
|
||||
[[nodiscard]] wgpu::ShaderModule const& getOrCreateShaderModuleFor(ScalarSampleType,
|
||||
bool multiSampled);
|
||||
|
||||
[[nodiscard]] wgpu::ShaderModule createShaderModule(ScalarSampleType, bool multiSampled);
|
||||
|
||||
[[nodiscard]] static size_t hashBindGroupLayoutKey(ScalarSampleType textureScalarFormat,
|
||||
bool multiSampled);
|
||||
|
||||
[[nodiscard]] static size_t hashPipelineLayoutKey(ScalarSampleType textureScalarFormat,
|
||||
bool multiSampled);
|
||||
|
||||
[[nodiscard]] static size_t hashShaderModuleKey(ScalarSampleType textureScalarFormat,
|
||||
bool multiSampled);
|
||||
|
||||
[[nodiscard]] static size_t hashPipelineKey(wgpu::TextureFormat, uint32_t sampleCount);
|
||||
|
||||
wgpu::Device const& mDevice;
|
||||
tsl::robin_map<size_t, wgpu::BindGroupLayout> mBindGroupLayouts{};
|
||||
tsl::robin_map<size_t, wgpu::PipelineLayout> mPipelineLayouts{};
|
||||
tsl::robin_map<size_t, wgpu::ShaderModule> mShaderModules{};
|
||||
tsl::robin_map<size_t, wgpu::RenderPipeline> mPipelines{};
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUTEXTURESWIZZLER_H
|
||||
Reference in New Issue
Block a user