Compare commits
6 Commits
ebridgewat
...
dk/opengl-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff356216d5 | ||
|
|
6fd87cdfef | ||
|
|
8ba001d0ca | ||
|
|
6fd031c611 | ||
|
|
b0e90c48a2 | ||
|
|
149acf4213 |
@@ -49,6 +49,8 @@ option(FILAMENT_SUPPORTS_OSMESA "Enable OSMesa (headless GL context) for Filamen
|
||||
|
||||
option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_INIT_GL_WARNINGS_FOR_OPTIMIZED_BUILD "Enable GL error warnings during init in optimized builds" OFF)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
|
||||
@@ -5,18 +5,6 @@ set(TARGET backend)
|
||||
set(PUBLIC_HDR_DIR include)
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# ==================================================================================================
|
||||
# Compilation options
|
||||
# ==================================================================================================
|
||||
#
|
||||
set(BACKEND_SANITIZATION "" CACHE STRING "Sanitization option")
|
||||
set_property(CACHE BACKEND_SANITIZATION PROPERTY STRINGS ";ASAN")
|
||||
|
||||
set(BACKEND_SANITIZERS)
|
||||
if (BACKEND_SANITIZATION STREQUAL "ASAN")
|
||||
set(BACKEND_SANITIZERS -fsanitize=address)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Sources and headers
|
||||
# ==================================================================================================
|
||||
@@ -330,6 +318,13 @@ if (WIN32 AND FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE "WGPU_IMPLEMENTATION")
|
||||
endif()
|
||||
|
||||
# enable OpenGL init warnings for the optimized build
|
||||
if(FILAMENT_ENABLE_INIT_GL_WARNINGS_FOR_OPTIMIZED_BUILD)
|
||||
target_compile_definitions(${TARGET} PRIVATE "FILAMENT_ENABLE_INIT_GL_WARNINGS_FOR_OPTIMIZED_BUILD")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
# ==================================================================================================
|
||||
# Expose a header-only target to minimize dependencies.
|
||||
# ==================================================================================================
|
||||
@@ -484,7 +479,6 @@ target_compile_options(${TARGET} PRIVATE
|
||||
${OSMESA_COMPILE_FLAGS}
|
||||
$<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}>
|
||||
$<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}>
|
||||
${BACKEND_SANITIZERS}
|
||||
)
|
||||
|
||||
if (FILAMENT_SUPPORTS_METAL)
|
||||
@@ -495,8 +489,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
|
||||
endif()
|
||||
|
||||
target_link_options(${TARGET} PRIVATE ${BACKEND_SANITIZERS})
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
${OSMESA_LINKER_FLAGS}
|
||||
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
|
||||
@@ -566,8 +558,6 @@ if (APPLE AND NOT IOS)
|
||||
test/test_RenderExternalImage.cpp)
|
||||
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
|
||||
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
|
||||
target_compile_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
|
||||
target_link_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
|
||||
|
||||
set(BACKEND_TEST_DEPS
|
||||
OSDependent
|
||||
@@ -606,7 +596,6 @@ if (APPLE AND NOT IOS)
|
||||
# linker from removing "unused" symbols.
|
||||
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
|
||||
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
|
||||
target_link_options(backend_test_mac PRIVATE ${BACKEND_SANITIZERS})
|
||||
|
||||
# This is needed after XCode 15.3
|
||||
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
@@ -616,8 +605,6 @@ endif()
|
||||
|
||||
if (LINUX)
|
||||
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
|
||||
target_compile_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
|
||||
target_link_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
|
||||
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
|
||||
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
|
||||
endif()
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
/**
|
||||
* Types and enums used by filament's driver.
|
||||
@@ -1140,7 +1139,6 @@ struct ExternalSamplerDatum {
|
||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
std::variant<utils::StaticString, utils::CString, std::monostate> label;
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
// TODO: uncomment when needed
|
||||
|
||||
@@ -50,6 +50,14 @@ void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char*
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(out) { GLUtils::assertGLError(out, __func__, __LINE__);}
|
||||
#elif defined(FILAMENT_ENABLE_INIT_GL_WARNINGS_FOR_OPTIMIZED_BUILD)
|
||||
# define CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(out) { GLUtils::checkGLError(out, __func__, __LINE__);}
|
||||
#else
|
||||
# define CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(out)
|
||||
#endif
|
||||
|
||||
constexpr GLuint getComponentCount(ElementType const type) noexcept {
|
||||
using ElementType = ElementType;
|
||||
switch (type) {
|
||||
|
||||
@@ -59,6 +59,9 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
// OpenGL version
|
||||
glGetIntegerv(GL_MAJOR_VERSION, major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, minor);
|
||||
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
|
||||
return (glGetError() == GL_NO_ERROR);
|
||||
#endif
|
||||
}
|
||||
@@ -109,6 +112,8 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gets.max_3d_texture_size);
|
||||
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gets.max_array_texture_layers);
|
||||
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
|
||||
mFeatureLevel = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
@@ -149,6 +154,7 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
|
||||
&gets.uniform_buffer_offset_alignment);
|
||||
#endif
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
}
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
@@ -235,6 +241,7 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
|
||||
// in practice KHR_Debug has never been useful, and actually is confusing. We keep this
|
||||
// only for our own debugging, in case we need it some day.
|
||||
@@ -269,6 +276,7 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
glDebugMessageCallback(cb, nullptr);
|
||||
}
|
||||
#endif
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
|
||||
mTimerQueryFactory = TimerQueryFactory::init(platform, *this);
|
||||
}
|
||||
@@ -384,6 +392,8 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
glEnable(GL_CLIP_DISTANCE1);
|
||||
}
|
||||
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
}
|
||||
|
||||
|
||||
@@ -761,6 +771,8 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->EXT_discard_framebuffer = true;
|
||||
ext->OES_vertex_array_object = true;
|
||||
}
|
||||
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
}
|
||||
|
||||
#endif // BACKEND_OPENGL_VERSION_GLES
|
||||
@@ -831,6 +843,8 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
if (major > 4 || (major == 4 && minor >= 5)) {
|
||||
ext->EXT_clip_control = true;
|
||||
}
|
||||
|
||||
CHECK_GL_INIT_ERROR_FOR_OPTIMIZED_BUILD(utils::slog.e)
|
||||
}
|
||||
|
||||
#endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -126,64 +125,21 @@ namespace {
|
||||
return module;
|
||||
}
|
||||
|
||||
// This is a 1 to 1 mapping of the ReservedSpecializationConstants enum in EngineEnums.h
|
||||
// The _hack is a workaround until https://issues.chromium.org/issues/42250586 is resolved
|
||||
// This workaround is the same one being used on the generateSpecializationConstant() function
|
||||
wgpu::StringView getSpecConstantStringId(uint32_t id) {
|
||||
switch (id) {
|
||||
case 0:
|
||||
return "0";// BACKEND_FEATURE_LEVEL_hack
|
||||
case 1:
|
||||
return "1";// CONFIG_MAX_INSTANCES_hack
|
||||
case 2:
|
||||
return "2";// ONFIG_STATIC_TEXTURE_TARGET_WORKAROUND_hack
|
||||
case 3:
|
||||
return "3";// CONFIG_SRGB_SWAPCHAIN_EMULATION_hack
|
||||
case 4:
|
||||
return "4";// CONFIG_FROXEL_BUFFER_HEIGHT_hack
|
||||
case 5:
|
||||
return "5";// CONFIG_POWER_VR_SHADER_WORKAROUNDS_hack
|
||||
case 6:
|
||||
return "6";// CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP_hack
|
||||
case 7:
|
||||
return "7";// CONFIG_DEBUG_FROXEL_VISUALIZATION_hack
|
||||
case 8:
|
||||
return "8";// CONFIG_STEREO_EYE_COUNT_hack
|
||||
case 9:
|
||||
return "9";// CONFIG_SH_BANDS_COUNT_hack
|
||||
case 10:
|
||||
return "10";// CONFIG_SHADOW_SAMPLING_METHOD_hack
|
||||
default:
|
||||
PANIC_POSTCONDITION("Unknown/unhandled spec constant key/id: %d", id);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wgpu::ConstantEntry> convertConstants(
|
||||
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
|
||||
constantsInfo) {
|
||||
std::vector<wgpu::ConstantEntry> constants;
|
||||
constants.reserve(constantsInfo.size());
|
||||
for (filament::backend::Program::SpecializationConstant const& constant: constantsInfo) {
|
||||
// CONFIG_MAX_INSTANCES (1) and CONFIG_FROXEL_BUFFER_HEIGHT (4) will not be present
|
||||
// as constant overrides in the generated WGSL, because WGSL doesn't support specialization
|
||||
// constants as an array length
|
||||
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
|
||||
// CONFIG_SRGB_SWAPCHAIN_EMULATION (3) is being skipped all together since it's only
|
||||
// included for the case of mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0, which should
|
||||
// not be possible for WebGPU
|
||||
if (constant.id == 1 || constant.id == 3 || constant.id == 4) {
|
||||
continue;
|
||||
std::vector<wgpu::ConstantEntry> constants(constantsInfo.size());
|
||||
for (size_t i = 0; i < constantsInfo.size(); i++) {
|
||||
filament::backend::Program::SpecializationConstant const& specConstant = constantsInfo[i];
|
||||
wgpu::ConstantEntry& constantEntry = constants[i];
|
||||
constantEntry.key = wgpu::StringView(std::to_string(specConstant.id));
|
||||
if (auto* v = std::get_if<int32_t>(&specConstant.value)) {
|
||||
constantEntry.value = static_cast<double>(*v);
|
||||
} else if (auto* f = std::get_if<float>(&specConstant.value)) {
|
||||
constantEntry.value = static_cast<double>(*f);
|
||||
} else if (auto* b = std::get_if<bool>(&specConstant.value)) {
|
||||
constantEntry.value = *b ? 0.0 : 1.0;
|
||||
}
|
||||
double value = 0.0;
|
||||
if (auto* v = std::get_if<int32_t>(&constant.value)) {
|
||||
value = static_cast<double>(*v);
|
||||
} else if (auto* f = std::get_if<float>(&constant.value)) {
|
||||
value = static_cast<double>(*f);
|
||||
} else if (auto* b = std::get_if<bool>(&constant.value)) {
|
||||
value = *b ? 0.0 : 1.0;
|
||||
}
|
||||
constants.push_back(
|
||||
wgpu::ConstantEntry{ .key = getSpecConstantStringId(constant.id), .value = value });
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -389,7 +389,9 @@ Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WGPUTexture>(); }
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
return allocHandle<WGPUProgram>();
|
||||
@@ -408,7 +410,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
@@ -436,7 +438,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
@@ -448,15 +450,15 @@ Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcep
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
@@ -468,8 +470,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||
mDevice, flags);
|
||||
assert_invariant(mSwapChain);
|
||||
WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(mDevice,
|
||||
mSwapChain->getColorFormat());
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
@@ -512,46 +512,30 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
|
||||
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {
|
||||
constructHandle<WGPUTexture>(th, target, levels, format, samples, w, h, depth, usage, mDevice);
|
||||
}
|
||||
TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {
|
||||
auto source = handleCast<WGPUTexture>(srch);
|
||||
|
||||
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
|
||||
}
|
||||
uint8_t baseLevel, uint8_t levelCount) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a) {
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
}
|
||||
backend::TextureSwizzle a) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
Platform::ExternalImageHandleRef externalImage) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
void* externalImage) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
void* image, uint32_t plane) {}
|
||||
|
||||
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
|
||||
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage usage) {
|
||||
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
|
||||
}
|
||||
uint32_t depth, TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
|
||||
@@ -591,7 +575,7 @@ void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
Handle<HwDescriptorSetLayout> dslh) {
|
||||
auto layout = handleCast<WebGPUDescriptorSetLayout>(dslh);
|
||||
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getBindGroupEntries());
|
||||
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getLayoutSize());
|
||||
}
|
||||
|
||||
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
|
||||
@@ -628,7 +612,7 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
|
||||
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
|
||||
// the GPU supports the given texture format with non-zero optimal tiling features.
|
||||
bool WebGPUDriver::isTextureFormatSupported(TextureFormat format) {
|
||||
return WGPUTexture::fToWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
@@ -776,31 +760,8 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||
assert_invariant(mCommandEncoder);
|
||||
|
||||
auto* renderTarget = handleCast<WGPURenderTarget>(rth);
|
||||
// if (renderTarget == mDefaultRenderTarget) {
|
||||
// FWGPU_LOGW << "Default render target"
|
||||
// << utils::io::endl;
|
||||
// } else {
|
||||
// FWGPU_LOGW << "Non Default render target"
|
||||
// << utils::io::endl;
|
||||
// }
|
||||
wgpu::RenderPassDescriptor renderPassDescriptor2;
|
||||
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
|
||||
.view = mSwapChain->getDepthTextureView(),
|
||||
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
|
||||
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
|
||||
.depthClearValue = static_cast<float>(params.clearDepth),
|
||||
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
|
||||
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
|
||||
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
|
||||
.stencilClearValue = params.clearStencil,
|
||||
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0
|
||||
};
|
||||
renderTarget->setUpRenderPassAttachments(renderPassDescriptor2, mTextureView, params);
|
||||
renderPassDescriptor2.depthStencilAttachment = &depthStencilAttachment;
|
||||
// TODO: Remove this code once WebGPU pipeline is implemented
|
||||
static float red = 1.0f;
|
||||
if (red - 0.01 > 0) {
|
||||
@@ -825,7 +786,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
|
||||
.timestampWrites = nullptr,
|
||||
};
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor2);
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
|
||||
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
|
||||
}
|
||||
@@ -953,7 +914,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
|
||||
mSwapChain->getDepthFormat());
|
||||
// TODO: uncomment once we have a valid pipeline to set
|
||||
mRenderPassEncoder.SetPipeline(pipeline);
|
||||
// mRenderPassEncoder.SetPipeline(pipeline);
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
@@ -973,18 +934,10 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
// Calling DrawIndexed with "firstInstance = 0" results in a NON spinning triangle
|
||||
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
||||
// Calling DrawIndexed with "firstInstance = 1" results in a spinning triangle
|
||||
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 1);
|
||||
// Calling Draw with "firstInstance = 0" results in a NON spinning triangle
|
||||
// Calling Draw with "firstInstance = 1" results in a spinning triangle
|
||||
// mRenderPassEncoder.Draw(indexCount, instanceCount, 0, 1);
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
|
||||
uint32_t indexCount, uint32_t instanceCount) {
|
||||
draw2(indexOffset, indexCount, instanceCount);
|
||||
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
@@ -1020,156 +973,34 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
|
||||
|
||||
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
||||
backend::descriptor_binding_t binding, Handle<HwTexture> th, SamplerParams params) {
|
||||
/*
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
auto texture = handleCast<WGPUTexture>(th);
|
||||
|
||||
// TODO very high odds badd assumptions are in here about handling HwTexture. Revisit with more
|
||||
// understanding. Right now assuming there is a wgpu::TextureView filled in
|
||||
if (!bindGroup->getIsLocked()) {
|
||||
// Dawn will cache duplicate samplers, so we don't strictly need to maintain a cache.
|
||||
// Making a cache might save us minor perf by reducing param translation
|
||||
auto sampler = makeSampler(params);
|
||||
// TODO making assumptions that size and offset mean the same thing here.
|
||||
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
|
||||
.textureView = texture->getTexView() };
|
||||
.textureView = texture->texView };
|
||||
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
|
||||
|
||||
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
|
||||
.sampler = sampler };
|
||||
.sampler = texture->sampler };
|
||||
bindGroup->addEntry(sEntry.binding, std::move(sEntry));
|
||||
}
|
||||
//TODO Just the setup, this function stilll needs the rest of logic implemented
|
||||
*/
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
|
||||
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
|
||||
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
const auto wbg = bindGroup->lockAndReturn(mDevice);
|
||||
assert_invariant(mRenderPassEncoder);
|
||||
// TODO is this how we should be getting the dynamic offsets?
|
||||
// should we add offsets for unused entries or is the input already have them?
|
||||
// this implementation assumes unused entries are not provided, and adds dummy values.
|
||||
// The count also includes unused entities, as not doing so produces errors
|
||||
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
|
||||
uint32_t const* const dynamicOffsetsWithUnused = bindGroup->setDynamicOffsets(offsets.data());
|
||||
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, dynamicOffsetsWithUnused);
|
||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descriptor_set_t set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
// TODO: presume we need this, use it. Probably Encoder::SetBindGroup
|
||||
auto wbg = bindGroup->lockAndReturn(mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
}
|
||||
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
|
||||
wgpu::SamplerDescriptor desc;
|
||||
|
||||
desc.label = "TODO";
|
||||
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
|
||||
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
|
||||
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
|
||||
switch (params.filterMag) {
|
||||
case SamplerMagFilter::NEAREST: {
|
||||
desc.magFilter = wgpu::FilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMagFilter::LINEAR: {
|
||||
desc.magFilter = wgpu::FilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.filterMin) {
|
||||
case SamplerMinFilter::NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.compareFunc) {
|
||||
case SamplerCompareFunc::LE: {
|
||||
desc.compare = wgpu::CompareFunction::LessEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::GE: {
|
||||
desc.compare = wgpu::CompareFunction::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::L: {
|
||||
desc.compare = wgpu::CompareFunction::Less;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::G: {
|
||||
desc.compare = wgpu::CompareFunction::Greater;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::E: {
|
||||
desc.compare = wgpu::CompareFunction::Equal;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::NE: {
|
||||
desc.compare = wgpu::CompareFunction::NotEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::A: {
|
||||
desc.compare = wgpu::CompareFunction::Always;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::N: {
|
||||
desc.compare = wgpu::CompareFunction::Never;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
desc.maxAnisotropy = 1u << params.anisotropyLog2;
|
||||
|
||||
|
||||
// Unused: Filament's compareMode, WGPU lodMinClamp/lodMaxClamp
|
||||
|
||||
//TODO Once we can properly map to descriptorsetlayout use the sampler.
|
||||
return mDevice.CreateSampler(/*&desc*/);
|
||||
}
|
||||
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
|
||||
switch (fWrapMode) {
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE: {
|
||||
return wgpu::AddressMode::ClampToEdge;
|
||||
break;
|
||||
}
|
||||
case SamplerWrapMode::REPEAT: {
|
||||
return wgpu::AddressMode::Repeat;
|
||||
break;
|
||||
}
|
||||
case SamplerWrapMode::MIRRORED_REPEAT: {
|
||||
return wgpu::AddressMode::MirrorRepeat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return wgpu::AddressMode::Undefined;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -56,8 +56,7 @@ private:
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
|
||||
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
|
||||
|
||||
template<typename GPUBufferObject>
|
||||
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
|
||||
uint32_t byteOffset) {
|
||||
|
||||
@@ -16,16 +16,7 @@
|
||||
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr wgpu::BufferUsage getBufferObjectUsage(
|
||||
@@ -119,44 +110,6 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::StringView getUserTextureLabel(filament::backend::SamplerType target) {
|
||||
// TODO will be helpful to get more useful info than this
|
||||
using filament::backend::SamplerType;
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "a_2D_user_texture";
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return "a_2D_array_user_texture";
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return "a_cube_map_user_texture";
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return "an_external_user_texture";
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "a_3D_user_texture";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "a_cube_mape_array_user_texture";
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target) {
|
||||
// TODO will be helpful to get more useful info than this
|
||||
using filament::backend::SamplerType;
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "a_2D_user_texture_view";
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return "a_2D_array_user_texture_view";
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return "a_cube_map_user_texture_view";
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return "an_external_user_texture_view";
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "a_3D_user_texture_view";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "a_cube_mape_array_user_texture_view";
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -184,7 +137,7 @@ WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attribut
|
||||
mAttributes[attrib.buffer].push_back({
|
||||
.format = vertexFormat,
|
||||
.offset = attrib.offset,
|
||||
.shaderLocation = attribIndex,
|
||||
.shaderLocation = static_cast<uint32_t>(mAttributes[attrib.buffer].size()),
|
||||
});
|
||||
|
||||
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
|
||||
@@ -239,20 +192,11 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
wgpu::Device const& device) {
|
||||
assert_invariant(device);
|
||||
|
||||
std::string baseLabel;
|
||||
if (std::holds_alternative<utils::StaticString>(layout.label)) {
|
||||
const auto& temp = std::get_if<utils::StaticString>(&layout.label);
|
||||
baseLabel = temp->c_str();
|
||||
} else if (std::holds_alternative<utils::CString>(layout.label)) {
|
||||
const auto& temp = std::get_if<utils::CString>(&layout.label);
|
||||
baseLabel = temp->c_str();
|
||||
}
|
||||
|
||||
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||
// debugging. For now, hack an incrementing value.
|
||||
static int layoutNum = 0;
|
||||
|
||||
unsigned int samplerCount =
|
||||
uint samplerCount =
|
||||
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
|
||||
return fEntry.type == DescriptorType::SAMPLER ||
|
||||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
|
||||
@@ -261,204 +205,86 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
|
||||
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
|
||||
wEntries.reserve(layout.bindings.size() + samplerCount);
|
||||
mBindGroupEntries.reserve(wEntries.capacity());
|
||||
|
||||
for (auto fEntry: layout.bindings) {
|
||||
auto& wEntry = wEntries.emplace_back();
|
||||
auto& entryInfo = mBindGroupEntries.emplace_back();
|
||||
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
|
||||
wEntry.binding = fEntry.binding * 2;
|
||||
entryInfo.binding = wEntry.binding;
|
||||
|
||||
switch (fEntry.type) {
|
||||
// TODO Metal treats these the same. Is this fine?
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
case DescriptorType::SAMPLER: {
|
||||
// Sampler binding is 2n+1 due to split.
|
||||
auto& samplerEntry = wEntries.emplace_back();
|
||||
auto& samplerEntryInfo = mBindGroupEntries.emplace_back();
|
||||
samplerEntry.binding = fEntry.binding * 2 + 1;
|
||||
samplerEntryInfo.binding = samplerEntry.binding;
|
||||
samplerEntryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER;
|
||||
samplerEntry.visibility = wEntry.visibility;
|
||||
// We are simply hoping that undefined and defaults suffices here.
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering; // Example default
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Float; // Example default
|
||||
// TODO: FIX! THIS IS HACK FOR HELLO-TRIANGLE!
|
||||
if (baseLabel.find("Skybox") != std::string::npos ||
|
||||
(baseLabel == "Filament Default Material_perView" && wEntry.binding == 22)) {
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
|
||||
} else {
|
||||
wEntry.texture.viewDimension =
|
||||
wgpu::TextureViewDimension::e2D;// Example default
|
||||
}
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW;
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
|
||||
break;
|
||||
}
|
||||
case DescriptorType::UNIFORM_BUFFER: {
|
||||
wEntry.buffer.hasDynamicOffset =
|
||||
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
|
||||
entryInfo.hasDynamicOffset = wEntry.buffer.hasDynamicOffset;
|
||||
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
// TODO: Ideally we fill minBindingSize
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::INPUT_ATTACHMENT: {
|
||||
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
|
||||
PANIC_POSTCONDITION("Input Attachment is not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER: {
|
||||
// TODO: Vulkan does not support this, can we?
|
||||
PANIC_POSTCONDITION("Shader storage is not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// fEntry.count is unused currently
|
||||
|
||||
// Currently flags are only used to specify dynamic offset.
|
||||
|
||||
// UNUSED
|
||||
// fEntry.count
|
||||
}
|
||||
std::string label = "layout_" + baseLabel + std::to_string(++layoutNum) ;
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
|
||||
.label{label.c_str()}, // Use .c_str() if label needs to be const char*
|
||||
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||
// debugging. For now, hack an incrementing value.
|
||||
.label{ "layout_" + std::to_string(++layoutNum) },
|
||||
.entryCount = wEntries.size(),
|
||||
.entries = wEntries.data()
|
||||
};
|
||||
// TODO Do we need to defer this until we have more info on textures and samplers??
|
||||
mLayoutSize = wEntries.size();
|
||||
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
|
||||
}
|
||||
|
||||
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
|
||||
|
||||
wgpu::Buffer WebGPUDescriptorSet::sDummyUniformBuffer = nullptr;
|
||||
wgpu::Texture WebGPUDescriptorSet::sDummyTexture = nullptr;
|
||||
wgpu::TextureView WebGPUDescriptorSet::sDummyTextureView = nullptr;
|
||||
wgpu::Sampler WebGPUDescriptorSet::sDummySampler = nullptr;
|
||||
|
||||
void WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(wgpu::Device const& device,
|
||||
wgpu::TextureFormat aColorFormat) {
|
||||
if (!sDummyUniformBuffer) {
|
||||
wgpu::BufferDescriptor bufferDescriptor{
|
||||
.label = "dummy_uniform_not_to_be_used",
|
||||
.usage = wgpu::BufferUsage::Uniform,
|
||||
.size = 4
|
||||
};
|
||||
sDummyUniformBuffer = device.CreateBuffer(&bufferDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
|
||||
<< "Failed to create dummy uniform buffer?";
|
||||
}
|
||||
if (!sDummyTexture || !sDummyTextureView) {
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = "dummy_texture_not_to_be_used",
|
||||
.usage = wgpu::TextureUsage::TextureBinding,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = wgpu::Extent3D{ .width = 4, .height = 4, .depthOrArrayLayers = 1 },
|
||||
.format = aColorFormat,
|
||||
};
|
||||
if (!sDummyTexture) {
|
||||
sDummyTexture = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy texture?";
|
||||
}
|
||||
if (!sDummyTextureView) {
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor{
|
||||
.label = "dummy_texture_view_not_to_be_used"
|
||||
};
|
||||
sDummyTextureView = sDummyTexture.CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
|
||||
<< "Failed to create dummy texture view?";
|
||||
}
|
||||
}
|
||||
if (!sDummySampler) {
|
||||
wgpu::SamplerDescriptor samplerDescriptor{
|
||||
.label = "dummy_sampler_not_to_be_used"
|
||||
};
|
||||
sDummySampler = device.CreateSampler(&samplerDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy sampler?";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedByBinding(
|
||||
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&
|
||||
bindGroupEntries) {
|
||||
assert_invariant(WebGPUDescriptorSet::sDummyUniformBuffer &&
|
||||
"Dummy uniform buffer must have been created before "
|
||||
"creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummyTexture &&
|
||||
"Dummy texture must have been created before creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummyTextureView &&
|
||||
"Dummy texture view must have been created before creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummySampler &&
|
||||
"Dummy sampler must have been created before creating dummy bind group entries.");
|
||||
using filament::backend::WebGPUDescriptorSetLayout;
|
||||
std::vector<wgpu::BindGroupEntry> entries;
|
||||
entries.reserve(bindGroupEntries.size());
|
||||
for (auto const& entryInfo: bindGroupEntries) {
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.binding = entryInfo.binding;
|
||||
switch (entryInfo.type) {
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::UNIFORM_BUFFER:
|
||||
entry.buffer = WebGPUDescriptorSet::sDummyUniformBuffer;
|
||||
break;
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW:
|
||||
entry.textureView = WebGPUDescriptorSet::sDummyTextureView;
|
||||
break;
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER:
|
||||
entry.sampler = WebGPUDescriptorSet::sDummySampler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](wgpu::BindGroupEntry const& a, wgpu::BindGroupEntry const& b) {
|
||||
return a.binding < b.binding;
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
||||
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
|
||||
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
|
||||
WebGPUDescriptorSet::WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize)
|
||||
: mLayout(layout),
|
||||
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) {
|
||||
entries(layoutSize, wgpu::BindGroupEntry{}) {
|
||||
// Establish the size of entries based on the layout. This should be reliable and efficient.
|
||||
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
|
||||
for (size_t i = 0; i < mEntryIndexByBinding.size(); i++) {
|
||||
mEntryIndexByBinding[i] = INVALID_INDEX;
|
||||
}
|
||||
for (size_t index = 0; index < mEntriesSortedByBinding.size(); index++) {
|
||||
wgpu::BindGroupEntry const& entry = mEntriesSortedByBinding[index];
|
||||
assert_invariant(entry.binding < mEntryIndexByBinding.size());
|
||||
mEntryIndexByBinding[entry.binding] = static_cast<uint8_t>(index);
|
||||
}
|
||||
for (auto const& entry : bindGroupEntries) {
|
||||
if (entry.hasDynamicOffset) {
|
||||
assert_invariant(entry.binding < mEntriesByBindingWithDynamicOffsets.size());
|
||||
mEntriesByBindingWithDynamicOffsets[entry.binding] = true;
|
||||
}
|
||||
}
|
||||
mDynamicOffsets.reserve(mEntriesSortedByBinding.size());
|
||||
}
|
||||
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
|
||||
mBindGroup = nullptr;
|
||||
mLayout = nullptr;
|
||||
}
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {}
|
||||
|
||||
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
|
||||
if (mBindGroup) {
|
||||
return mBindGroup;
|
||||
}
|
||||
// TODO label? Should we just copy layout label?
|
||||
wgpu::BindGroupDescriptor desc{
|
||||
.layout = mLayout,
|
||||
.entryCount = mEntriesSortedByBinding.size(),
|
||||
.entries = mEntriesSortedByBinding.data()
|
||||
};
|
||||
wgpu::BindGroupDescriptor desc{ .layout = mLayout,
|
||||
.entryCount = entries.size(),
|
||||
.entries = entries.data() };
|
||||
mBindGroup = device.CreateBindGroup(&desc);
|
||||
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
|
||||
// once we have created the bind group itself we should no longer need any other state
|
||||
mLayout = nullptr;
|
||||
mEntriesSortedByBinding.clear();
|
||||
mEntriesSortedByBinding.shrink_to_fit();
|
||||
return mBindGroup;
|
||||
}
|
||||
|
||||
void WebGPUDescriptorSet::addEntry(unsigned int index, wgpu::BindGroupEntry&& entry) {
|
||||
void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
|
||||
if (mBindGroup) {
|
||||
// We will keep getting hits from future updates, but shouldn't do anything
|
||||
// Filament guarantees this won't change after things have locked.
|
||||
@@ -466,490 +292,6 @@ void WebGPUDescriptorSet::addEntry(unsigned int index, wgpu::BindGroupEntry&& en
|
||||
}
|
||||
// TODO: Putting some level of trust that Filament is not going to reuse indexes or go past the
|
||||
// layout index for efficiency. Add guards if wrong.
|
||||
FILAMENT_CHECK_POSTCONDITION(index < mEntryIndexByBinding.size())
|
||||
<< "impossible/invalid index for a descriptor/binding (our of range or >= "
|
||||
"MAX_DESCRIPTOR_COUNT) "
|
||||
<< index;
|
||||
uint8_t entryIndex = mEntryIndexByBinding[index];
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
entryIndex != INVALID_INDEX && entryIndex < mEntriesSortedByBinding.size())
|
||||
<< "Invalid binding " << index;
|
||||
entry.binding = index;
|
||||
mEntriesSortedByBinding[entryIndex] = std::move(entry);
|
||||
mEntriesByBindingAdded[index] = true;
|
||||
entries[index] = std::move(entry);
|
||||
}
|
||||
|
||||
uint32_t const* WebGPUDescriptorSet::setDynamicOffsets(uint32_t const* offsets) {
|
||||
// mDynamicOffsets already reserves enough memory for the number of entries in the set
|
||||
mDynamicOffsets.clear();
|
||||
// this implementation copies the offsets to mDynamicOffsets, but also adds values for
|
||||
// unused entries TODO: is this necessary?
|
||||
size_t inputIndex = 0;
|
||||
size_t outputIndex = 0;
|
||||
for (auto const& entry : mEntriesSortedByBinding) {
|
||||
if (mEntriesByBindingWithDynamicOffsets[entry.binding]) {
|
||||
if (mEntriesByBindingAdded[entry.binding]) {
|
||||
mDynamicOffsets[outputIndex++] = offsets[inputIndex++];
|
||||
} else {
|
||||
mDynamicOffsets[outputIndex++] = 0; // dummy offset, as it was never added
|
||||
}
|
||||
}
|
||||
}
|
||||
return mDynamicOffsets.data();
|
||||
}
|
||||
|
||||
size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
|
||||
return mEntriesByBindingWithDynamicOffsets.count();
|
||||
}
|
||||
|
||||
// From createTextureR
|
||||
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
wgpu::Device const& device) noexcept {
|
||||
assert_invariant(
|
||||
samples == 1 ||
|
||||
samples == 4 &&
|
||||
"An invalid number of samples were requested, as WGPU requires the sample "
|
||||
"count to either be 1 (no multisampling) or 4, at least as of April 2025 of "
|
||||
"the spec. See https://www.w3.org/TR/webgpu/#texture-creation or "
|
||||
"https://gpuweb.github.io/gpuweb/#multisample-state");
|
||||
// First, the texture aspect, starting with the defaults/basic configuration
|
||||
mUsage = fToWGPUTextureUsage(usage);
|
||||
mFormat = fToWGPUTextureFormat(format);
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = getUserTextureLabel(target),
|
||||
.usage = mUsage,
|
||||
.dimension = target == SamplerType::SAMPLER_3D ? wgpu::TextureDimension::e3D
|
||||
: wgpu::TextureDimension::e2D,
|
||||
.size = { .width = width, .height = height, .depthOrArrayLayers = depth },
|
||||
.format = mFormat,
|
||||
.mipLevelCount = levels,
|
||||
.sampleCount = samples,
|
||||
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from
|
||||
// Filament
|
||||
.viewFormatCount = 0,
|
||||
.viewFormats = nullptr,
|
||||
};
|
||||
// adjust for specific cases
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
mArrayLayerCount = 1;
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
textureDescriptor.size.depthOrArrayLayers = 6;
|
||||
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
case SamplerType::SAMPLER_3D:
|
||||
mArrayLayerCount = 1;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
textureDescriptor.size.depthOrArrayLayers = depth * 6;
|
||||
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
|
||||
break;
|
||||
}
|
||||
assert_invariant(textureDescriptor.format != wgpu::TextureFormat::Undefined &&
|
||||
"Could not find appropriate WebGPU format");
|
||||
mTexture = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(mTexture)
|
||||
<< "Failed to create texture for " << textureDescriptor.label;
|
||||
// Second, the texture view aspect
|
||||
mTexView = makeTextureView(0, levels, target);
|
||||
}
|
||||
|
||||
// From createTextureViewR
|
||||
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
|
||||
mTexture = src->mTexture;
|
||||
mTexView = makeTextureView(baseLevel, levelCount, target);
|
||||
}
|
||||
|
||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
|
||||
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
|
||||
|
||||
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
|
||||
// TODO Validate assumptions, revisit if issues.
|
||||
if (any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopySrc;
|
||||
}
|
||||
if (any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopyDst;
|
||||
}
|
||||
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
// WGPU Render attachment covers either color or stencil situation dependant
|
||||
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
|
||||
// issues, investigate here.
|
||||
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT) &
|
||||
fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
|
||||
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
|
||||
// benefit though
|
||||
const bool useTransientAttachment =
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
if (useTransientAttachment) {
|
||||
retUsage |= wgpu::TextureUsage::TransientAttachment;
|
||||
}
|
||||
// NOTE: Unused wgpu flags:
|
||||
// StorageBinding
|
||||
// StorageAttachment
|
||||
|
||||
// NOTE: Unused Filament flags:
|
||||
// SUBPASS_INPUT VK goes to input attachment which we don't support right now
|
||||
// PROTECTED
|
||||
return retUsage;
|
||||
}
|
||||
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
|
||||
switch (fUsage) {
|
||||
case filament::backend::TextureFormat::R8:
|
||||
return wgpu::TextureFormat::R8Unorm;
|
||||
case filament::backend::TextureFormat::R8_SNORM:
|
||||
return wgpu::TextureFormat::R8Snorm;
|
||||
case filament::backend::TextureFormat::R8UI:
|
||||
return wgpu::TextureFormat::R8Uint;
|
||||
case filament::backend::TextureFormat::R8I:
|
||||
return wgpu::TextureFormat::R8Sint;
|
||||
case filament::backend::TextureFormat::STENCIL8:
|
||||
return wgpu::TextureFormat::Stencil8;
|
||||
case filament::backend::TextureFormat::R16F:
|
||||
return wgpu::TextureFormat::R16Float;
|
||||
case filament::backend::TextureFormat::R16UI:
|
||||
return wgpu::TextureFormat::R16Uint;
|
||||
case filament::backend::TextureFormat::R16I:
|
||||
return wgpu::TextureFormat::R16Sint;
|
||||
case filament::backend::TextureFormat::RG8:
|
||||
return wgpu::TextureFormat::RG8Unorm;
|
||||
case filament::backend::TextureFormat::RG8_SNORM:
|
||||
return wgpu::TextureFormat::RG8Snorm;
|
||||
case filament::backend::TextureFormat::RG8UI:
|
||||
return wgpu::TextureFormat::RG8Uint;
|
||||
case filament::backend::TextureFormat::RG8I:
|
||||
return wgpu::TextureFormat::RG8Sint;
|
||||
case filament::backend::TextureFormat::R32F:
|
||||
return wgpu::TextureFormat::R32Float;
|
||||
case filament::backend::TextureFormat::R32UI:
|
||||
return wgpu::TextureFormat::R32Uint;
|
||||
case filament::backend::TextureFormat::R32I:
|
||||
return wgpu::TextureFormat::R32Sint;
|
||||
case filament::backend::TextureFormat::RG16F:
|
||||
return wgpu::TextureFormat::RG16Float;
|
||||
case filament::backend::TextureFormat::RG16UI:
|
||||
return wgpu::TextureFormat::RG16Uint;
|
||||
case filament::backend::TextureFormat::RG16I:
|
||||
return wgpu::TextureFormat::RG16Sint;
|
||||
case filament::backend::TextureFormat::RGBA8:
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_A8:
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA8_SNORM:
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case filament::backend::TextureFormat::RGBA8UI:
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case filament::backend::TextureFormat::RGBA8I:
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case filament::backend::TextureFormat::DEPTH16:
|
||||
return wgpu::TextureFormat::Depth16Unorm;
|
||||
case filament::backend::TextureFormat::DEPTH24:
|
||||
return wgpu::TextureFormat::Depth24Plus;
|
||||
case filament::backend::TextureFormat::DEPTH32F:
|
||||
return wgpu::TextureFormat::Depth32Float;
|
||||
case filament::backend::TextureFormat::DEPTH24_STENCIL8:
|
||||
return wgpu::TextureFormat::Depth24PlusStencil8;
|
||||
case filament::backend::TextureFormat::DEPTH32F_STENCIL8:
|
||||
return wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
case filament::backend::TextureFormat::RG32F:
|
||||
return wgpu::TextureFormat::RG32Float;
|
||||
case filament::backend::TextureFormat::RG32UI:
|
||||
return wgpu::TextureFormat::RG32Uint;
|
||||
case filament::backend::TextureFormat::RG32I:
|
||||
return wgpu::TextureFormat::RG32Sint;
|
||||
case filament::backend::TextureFormat::RGBA16F:
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case filament::backend::TextureFormat::RGBA16UI:
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case filament::backend::TextureFormat::RGBA16I:
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case filament::backend::TextureFormat::RGBA32F:
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case filament::backend::TextureFormat::RGBA32UI:
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case filament::backend::TextureFormat::RGBA32I:
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case filament::backend::TextureFormat::EAC_R11:
|
||||
return wgpu::TextureFormat::EACR11Unorm;
|
||||
case filament::backend::TextureFormat::EAC_R11_SIGNED:
|
||||
return wgpu::TextureFormat::EACR11Snorm;
|
||||
case filament::backend::TextureFormat::EAC_RG11:
|
||||
return wgpu::TextureFormat::EACRG11Unorm;
|
||||
case filament::backend::TextureFormat::EAC_RG11_SIGNED:
|
||||
return wgpu::TextureFormat::EACRG11Snorm;
|
||||
case filament::backend::TextureFormat::ETC2_RGB8:
|
||||
return wgpu::TextureFormat::ETC2RGB8Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_SRGB8:
|
||||
return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
|
||||
case filament::backend::TextureFormat::ETC2_RGB8_A1:
|
||||
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_SRGB8_A1:
|
||||
return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
|
||||
case filament::backend::TextureFormat::ETC2_EAC_RGBA8:
|
||||
return wgpu::TextureFormat::ETC2RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::ETC2_EAC_SRGBA8:
|
||||
return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_4x4:
|
||||
return wgpu::TextureFormat::ASTC4x4Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
return wgpu::TextureFormat::ASTC4x4UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_5x4:
|
||||
return wgpu::TextureFormat::ASTC5x4Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
|
||||
return wgpu::TextureFormat::ASTC5x4UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_5x5:
|
||||
return wgpu::TextureFormat::ASTC5x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
|
||||
return wgpu::TextureFormat::ASTC5x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_6x5:
|
||||
return wgpu::TextureFormat::ASTC6x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
|
||||
return wgpu::TextureFormat::ASTC6x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_6x6:
|
||||
return wgpu::TextureFormat::ASTC6x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
|
||||
return wgpu::TextureFormat::ASTC6x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x5:
|
||||
return wgpu::TextureFormat::ASTC8x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
|
||||
return wgpu::TextureFormat::ASTC8x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x6:
|
||||
return wgpu::TextureFormat::ASTC8x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
|
||||
return wgpu::TextureFormat::ASTC8x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_8x8:
|
||||
return wgpu::TextureFormat::ASTC8x8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
|
||||
return wgpu::TextureFormat::ASTC8x8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x5:
|
||||
return wgpu::TextureFormat::ASTC10x5Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
|
||||
return wgpu::TextureFormat::ASTC10x5UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x6:
|
||||
return wgpu::TextureFormat::ASTC10x6Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
|
||||
return wgpu::TextureFormat::ASTC10x6UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x8:
|
||||
return wgpu::TextureFormat::ASTC10x8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
|
||||
return wgpu::TextureFormat::ASTC10x8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_10x10:
|
||||
return wgpu::TextureFormat::ASTC10x10Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
|
||||
return wgpu::TextureFormat::ASTC10x10UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_12x10:
|
||||
return wgpu::TextureFormat::ASTC12x10Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
|
||||
return wgpu::TextureFormat::ASTC12x10UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGBA_ASTC_12x12:
|
||||
return wgpu::TextureFormat::ASTC12x12Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
|
||||
return wgpu::TextureFormat::ASTC12x12UnormSrgb;
|
||||
case filament::backend::TextureFormat::RED_RGTC1:
|
||||
return wgpu::TextureFormat::BC4RUnorm;
|
||||
case filament::backend::TextureFormat::SIGNED_RED_RGTC1:
|
||||
return wgpu::TextureFormat::BC4RSnorm;
|
||||
case filament::backend::TextureFormat::RED_GREEN_RGTC2:
|
||||
return wgpu::TextureFormat::BC5RGUnorm;
|
||||
case filament::backend::TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
return wgpu::TextureFormat::BC5RGSnorm;
|
||||
case filament::backend::TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
return wgpu::TextureFormat::BC6HRGBUfloat;
|
||||
case filament::backend::TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
return wgpu::TextureFormat::BC6HRGBFloat;
|
||||
case filament::backend::TextureFormat::RGBA_BPTC_UNORM:
|
||||
return wgpu::TextureFormat::BC7RGBAUnorm;
|
||||
case filament::backend::TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
return wgpu::TextureFormat::BC7RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::RGB565:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and discard the alpha and lower precision.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB9_E5:
|
||||
return wgpu::TextureFormat::RGB9E5Ufloat;
|
||||
case filament::backend::TextureFormat::RGB5_A1:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGBA4:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB8:
|
||||
// No direct sRGB equivalent in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case filament::backend::TextureFormat::SRGB8:
|
||||
// No direct sRGB equivalent in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case filament::backend::TextureFormat::RGB8_SNORM:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case filament::backend::TextureFormat::RGB8UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case filament::backend::TextureFormat::RGB8I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case filament::backend::TextureFormat::R11F_G11F_B10F:
|
||||
return wgpu::TextureFormat::RG11B10Ufloat;
|
||||
case filament::backend::TextureFormat::UNUSED:
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case filament::backend::TextureFormat::RGB10_A2:
|
||||
return wgpu::TextureFormat::RGB10A2Unorm;
|
||||
case filament::backend::TextureFormat::RGB16F:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case filament::backend::TextureFormat::RGB16UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case filament::backend::TextureFormat::RGB16I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case filament::backend::TextureFormat::RGB32F:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case filament::backend::TextureFormat::RGB32UI:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case filament::backend::TextureFormat::RGB32I:
|
||||
// No direct mapping in wgpu without alpha.
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case filament::backend::TextureFormat::DXT1_RGB:
|
||||
return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT1_RGBA:
|
||||
return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT3_RGBA:
|
||||
return wgpu::TextureFormat::BC2RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT5_RGBA:
|
||||
return wgpu::TextureFormat::BC3RGBAUnorm;
|
||||
case filament::backend::TextureFormat::DXT1_SRGB:
|
||||
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT1_SRGBA:
|
||||
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT3_SRGBA:
|
||||
return wgpu::TextureFormat::BC2RGBAUnormSrgb;
|
||||
case filament::backend::TextureFormat::DXT5_SRGBA:
|
||||
return wgpu::TextureFormat::BC3RGBAUnormSrgb;
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
|
||||
SamplerType target) {
|
||||
// starting with the defaults/basic configuration
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor{
|
||||
.label = getUserTextureViewLabel(target),
|
||||
.format = mFormat,
|
||||
// dimension depends on target and is set below
|
||||
.baseMipLevel = baseLevel,
|
||||
.mipLevelCount = levelCount,
|
||||
// baseArrayLayer is required, making a guess
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = mArrayLayerCount,
|
||||
// Have not found an analog to aspect in other drivers, but ALL should be unrestrictive.
|
||||
// TODO Can we make this better?
|
||||
.aspect = wgpu::TextureAspect::All,
|
||||
.usage = mUsage
|
||||
};
|
||||
// adjust for specific cases
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube;
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e3D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::CubeArray;
|
||||
break;
|
||||
}
|
||||
wgpu::TextureView textureView = mTexture.CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(textureView)
|
||||
<< "Failed to create texture view " << textureViewDescriptor.label;
|
||||
return textureView;
|
||||
}
|
||||
|
||||
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
|
||||
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
auto result = color[index];
|
||||
if (index == 0 && defaultRenderTarget) {
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer) {
|
||||
auto clearFlags = params.flags.clear;
|
||||
auto discardStartFlags = params.flags.discardStart;
|
||||
if (any(clearFlags & buffer)) {
|
||||
return wgpu::LoadOp::Clear;
|
||||
} else if (any(discardStartFlags & buffer)) {
|
||||
return wgpu::LoadOp::Clear;
|
||||
}
|
||||
return wgpu::LoadOp::Load;
|
||||
}
|
||||
|
||||
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer) {
|
||||
const auto discardEndFlags = params.flags.discardEnd;
|
||||
if (any(discardEndFlags & buffer)) {
|
||||
return wgpu::StoreOp::Discard;
|
||||
}
|
||||
return wgpu::StoreOp::Store;
|
||||
}
|
||||
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||
wgpu::TextureView const& textureView, RenderPassParams const& params) {
|
||||
// auto discardFlags = params.flags.discardEnd;
|
||||
// (void) discardFlags;
|
||||
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
|
||||
colorAttachments.clear();
|
||||
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) {
|
||||
// auto attachment = getDrawColorAttachment(i);
|
||||
// if (attachment) {
|
||||
wgpu::RenderPassColorAttachment colorAttachment;
|
||||
colorAttachment.view = textureView;
|
||||
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i));
|
||||
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
|
||||
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a };
|
||||
colorAttachments.emplace_back(colorAttachment);
|
||||
// }
|
||||
}
|
||||
descriptor.colorAttachments = colorAttachments.data();
|
||||
descriptor.colorAttachmentCount = colorAttachments.size();
|
||||
descriptor.depthStencilAttachment = nullptr;
|
||||
descriptor.timestampWrites = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@@ -100,124 +98,80 @@ struct WGPUBufferObject : HwBufferObject {
|
||||
wgpu::Buffer buffer = nullptr;
|
||||
const BufferObjectBinding bufferObjectBinding;
|
||||
};
|
||||
|
||||
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
|
||||
public:
|
||||
|
||||
enum class BindGroupEntryType : uint8_t {
|
||||
UNIFORM_BUFFER,
|
||||
TEXTURE_VIEW,
|
||||
SAMPLER
|
||||
};
|
||||
|
||||
struct BindGroupEntryInfo final {
|
||||
uint8_t binding = 0;
|
||||
BindGroupEntryType type = BindGroupEntryType::UNIFORM_BUFFER;
|
||||
bool hasDynamicOffset = false;
|
||||
};
|
||||
|
||||
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
|
||||
~WebGPUDescriptorSetLayout();
|
||||
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
|
||||
[[nodiscard]] std::vector<BindGroupEntryInfo> const& getBindGroupEntries() const {
|
||||
return mBindGroupEntries;
|
||||
}
|
||||
[[nodiscard]] uint getLayoutSize() const { return mLayoutSize; }
|
||||
|
||||
private:
|
||||
// TODO: If this is useful elsewhere, remove it from this class
|
||||
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
|
||||
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
|
||||
std::vector<BindGroupEntryInfo> mBindGroupEntries;
|
||||
uint mLayoutSize;
|
||||
wgpu::BindGroupLayout mLayout;
|
||||
};
|
||||
|
||||
class WebGPUDescriptorSet final : public HwDescriptorSet {
|
||||
public:
|
||||
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
|
||||
wgpu::TextureFormat aColorFormat);
|
||||
|
||||
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
|
||||
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
|
||||
WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize);
|
||||
~WebGPUDescriptorSet();
|
||||
|
||||
wgpu::BindGroup lockAndReturn(wgpu::Device const&);
|
||||
void addEntry(unsigned int index, wgpu::BindGroupEntry&& entry);
|
||||
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
|
||||
wgpu::BindGroup lockAndReturn(wgpu::Device const& device);
|
||||
void addEntry(uint index, wgpu::BindGroupEntry&& entry);
|
||||
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
|
||||
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
|
||||
|
||||
private:
|
||||
|
||||
static wgpu::Buffer sDummyUniformBuffer;
|
||||
static wgpu::Texture sDummyTexture;
|
||||
static wgpu::TextureView sDummyTextureView;
|
||||
static wgpu::Sampler sDummySampler;
|
||||
|
||||
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
|
||||
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);
|
||||
|
||||
// TODO: Consider storing what we used to make the layout. However we need to essentially
|
||||
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
|
||||
// there isn't a mismatch may be easiest.
|
||||
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
|
||||
wgpu::BindGroupLayout mLayout = nullptr;
|
||||
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
|
||||
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
|
||||
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
|
||||
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets {};
|
||||
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded {};
|
||||
std::vector<uint32_t> mDynamicOffsets;
|
||||
wgpu::BindGroup mBindGroup = nullptr;
|
||||
wgpu::BindGroupLayout mLayout;
|
||||
std::vector<wgpu::BindGroupEntry> entries;
|
||||
wgpu::BindGroup mBindGroup;
|
||||
};
|
||||
|
||||
class WGPUTexture : public HwTexture {
|
||||
public:
|
||||
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPUTexture : public HwTexture {
|
||||
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
wgpu::Device const& device) noexcept;
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||
|
||||
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
// constructors for creating texture views
|
||||
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
const wgpu::Texture& getTexture() const { return mTexture; }
|
||||
const wgpu::TextureView& getTexView() const { return mTexView; }
|
||||
|
||||
// Public to allow checking for support of a texture format
|
||||
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
|
||||
|
||||
private:
|
||||
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
|
||||
SamplerType target);
|
||||
// CreateTextureR has info for a texture and sampler. Texture Views are needed for binding,
|
||||
// along with a sampler Current plan: Inherit the sampler and Texture to always exist (It is a
|
||||
// ref counted pointer) when making views. View is optional
|
||||
wgpu::Texture mTexture = nullptr;
|
||||
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
|
||||
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
|
||||
uint32_t mArrayLayerCount = 1;
|
||||
wgpu::TextureView mTexView = nullptr;
|
||||
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
|
||||
wgpu::Texture texture = nullptr;
|
||||
// TODO: Adding this but not yet setting it up. Filament "Textures" are combined image samplers,
|
||||
// rep both.
|
||||
wgpu::Sampler sampler = nullptr;
|
||||
//TODO: Not sure all the ways HwTexture is used. Overloading like this might be entirely wrong.
|
||||
wgpu::TextureView texView = nullptr;
|
||||
};
|
||||
|
||||
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
WGPURenderPrimitive() {}
|
||||
|
||||
void setBuffers(WGPUVertexBufferInfo const* const vbi,
|
||||
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
|
||||
|
||||
WGPUVertexBuffer* vertexBuffer = nullptr;
|
||||
WGPUIndexBuffer* indexBuffer = nullptr;
|
||||
};
|
||||
|
||||
class WGPURenderTarget : public HwRenderTarget {
|
||||
public:
|
||||
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPURenderTarget : public HwRenderTarget {
|
||||
class Attachment {
|
||||
public:
|
||||
friend class WGPURenderTarget;
|
||||
friend struct WGPURenderTarget;
|
||||
|
||||
Attachment() = default;
|
||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||
: level(level),
|
||||
layer(layer),
|
||||
texture(gpuTexture->getTexture()),
|
||||
texture(gpuTexture->texture),
|
||||
mWGPUTexture(gpuTexture) {}
|
||||
operator bool() const {
|
||||
return mWGPUTexture != nullptr;
|
||||
}
|
||||
|
||||
uint8_t level = 0;
|
||||
uint16_t layer = 0;
|
||||
@@ -233,8 +187,8 @@ public:
|
||||
: HwRenderTarget(0, 0),
|
||||
defaultRenderTarget(true) {}
|
||||
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||
wgpu::TextureView const& textureView, RenderPassParams const& params);
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
|
||||
const RenderPassParams& params);
|
||||
|
||||
math::uint2 getAttachmentSize() noexcept;
|
||||
|
||||
@@ -244,15 +198,15 @@ public:
|
||||
Attachment getDrawColorAttachment(size_t index);
|
||||
Attachment getReadColorAttachment(size_t index);
|
||||
|
||||
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
private:
|
||||
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
|
||||
bool defaultRenderTarget = false;
|
||||
uint8_t samples = 1;
|
||||
|
||||
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
math::uint2 attachmentSize = {};
|
||||
std::vector<wgpu::RenderPassColorAttachment> colorAttachments {};
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -235,7 +235,6 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
fragmentState.constantCount = program.constants.size(),
|
||||
fragmentState.constants = program.constants.data(),
|
||||
fragmentState.targetCount = 1; // TODO need to get this from the render target
|
||||
fragmentState.targets = colorTargets.data();
|
||||
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
|
||||
auto& colorTarget = colorTargets[targetIndex];
|
||||
|
||||
@@ -105,7 +105,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||
const std::array expectedColorFormats =
|
||||
useSRGBColorSpace ?
|
||||
@@ -123,7 +123,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
return *firstFoundColorFormat;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
|
||||
constexpr wgpu::TextureFormat selectDepthFormat(bool depth32FloatStencil8Enabled,
|
||||
bool needStencil) {
|
||||
if (needStencil) {
|
||||
if (depth32FloatStencil8Enabled) {
|
||||
@@ -137,7 +137,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
constexpr wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
wgpu::PresentMode const* availablePresentModes) {
|
||||
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
|
||||
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
|
||||
@@ -151,7 +151,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
return desiredPresentMode;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
constexpr wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
wgpu::CompositeAlphaMode const* availableAlphaModes) {
|
||||
bool autoAvailable = false;
|
||||
bool inheritAvailable = false;
|
||||
@@ -222,58 +222,13 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||
config.alphaMode = selectAlphaMode(capabilities.alphaModeCount, capabilities.alphaModes);
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::Texture createDepthTexture(wgpu::Device const& device,
|
||||
wgpu::Extent2D const& extent, wgpu::TextureFormat depthFormat) {
|
||||
wgpu::TextureDescriptor descriptor{ .label = "depth_texture",
|
||||
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = { .width = extent.width, .height = extent.height, .depthOrArrayLayers = 1 },
|
||||
.format = depthFormat,
|
||||
.mipLevelCount = 1,
|
||||
.sampleCount = 1,
|
||||
.viewFormatCount = 1,
|
||||
.viewFormats = &depthFormat
|
||||
};
|
||||
wgpu::Texture depthTexture = device.CreateTexture(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(depthTexture) << "Failed to create depth texture with width "
|
||||
<< extent.width << " and height " << extent.height;
|
||||
return depthTexture;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::TextureView createDepthTextureView(wgpu::Texture const& depthTexture,
|
||||
wgpu::TextureFormat depthFormat, bool needStencil) {
|
||||
wgpu::TextureViewDescriptor descriptor{
|
||||
.label = "depth_texture_view",
|
||||
.format = depthFormat,
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = wgpu::TextureAspect::DepthOnly,
|
||||
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment
|
||||
};
|
||||
if (needStencil) {
|
||||
descriptor.aspect = wgpu::TextureAspect::All;
|
||||
}
|
||||
wgpu::TextureView depthTextureView = depthTexture.CreateView(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(depthTextureView) << "Failed to create depth texture view";
|
||||
return depthTextureView;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||
wgpu::Adapter const& adapter, wgpu::Device const& device, uint64_t flags)
|
||||
: mDevice(device),
|
||||
mSurface(surface),
|
||||
mNeedStencil((flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0),
|
||||
mDepthFormat(selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
|
||||
mNeedStencil)),
|
||||
mDepthTexture(createDepthTexture(device, surfaceSize, mDepthFormat)),
|
||||
mDepthTextureView(createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil)) {
|
||||
: mSurface(surface) {
|
||||
wgpu::SurfaceCapabilities capabilities = {};
|
||||
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||
@@ -283,14 +238,19 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const&
|
||||
#endif
|
||||
}
|
||||
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
||||
const bool needStencil = (flags & SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
||||
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
|
||||
mDepthFormat = selectDepthFormat(device.HasFeature(wgpu::FeatureName::Depth32FloatStencil8),
|
||||
needStencil);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printSurfaceConfiguration(mConfig, mDepthFormat);
|
||||
#endif
|
||||
mSurface.Configure(&mConfig);
|
||||
}
|
||||
|
||||
WebGPUSwapChain::~WebGPUSwapChain() { mSurface.Unconfigure(); }
|
||||
WebGPUSwapChain::~WebGPUSwapChain() {
|
||||
mSurface.Unconfigure();
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
|
||||
@@ -307,8 +267,6 @@ void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||
// TODO we may need to ensure no surface texture is in flight when we do this. some
|
||||
// synchronization may be necessary
|
||||
mSurface.Configure(&mConfig);
|
||||
mDepthTexture = createDepthTexture(mDevice, currentSurfaceSize, mDepthFormat);
|
||||
mDepthTextureView = createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,20 +38,14 @@ public:
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
|
||||
|
||||
void present();
|
||||
|
||||
private:
|
||||
void setExtent(wgpu::Extent2D const&);
|
||||
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Surface mSurface = {};
|
||||
wgpu::SurfaceConfiguration mConfig = {};
|
||||
bool mNeedStencil = false;
|
||||
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::Texture mDepthTexture = nullptr;
|
||||
wgpu::TextureView mDepthTextureView = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -99,45 +99,22 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing limits
|
||||
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
|
||||
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
|
||||
|
||||
constexpr std::array desiredFeatures = {
|
||||
wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8,
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
std::vector<wgpu::FeatureName> requiredFeatures;
|
||||
requiredFeatures.reserve(desiredFeatures.size());
|
||||
wgpu::SupportedFeatures supportedFeatures;
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
|
||||
std::vector<wgpu::FeatureName> enabledFeatures;
|
||||
enabledFeatures.reserve(requiredFeatures.size() + optionalFeatures.size());
|
||||
|
||||
std::set_intersection(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount, requiredFeatures.begin(),
|
||||
requiredFeatures.end(), std::back_inserter(enabledFeatures));
|
||||
|
||||
if (enabledFeatures.size() != requiredFeatures.size()) {
|
||||
std::vector<wgpu::FeatureName> missingFeatures;
|
||||
std::set_difference(requiredFeatures.begin(), requiredFeatures.end(),
|
||||
supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
std::back_inserter(missingFeatures));
|
||||
|
||||
std::stringstream missingFeaturesStream{};
|
||||
for (const auto& entry: missingFeatures) {
|
||||
missingFeaturesStream << std::to_string(static_cast<uint32_t>(entry)) << " ";
|
||||
}
|
||||
PANIC_POSTCONDITION("Some required features are not available %s/n",
|
||||
missingFeaturesStream.str().c_str());
|
||||
}
|
||||
|
||||
std::set_intersection(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount, optionalFeatures.begin(),
|
||||
optionalFeatures.end(), std::back_inserter(enabledFeatures));
|
||||
|
||||
supportedFeatures.features + supportedFeatures.featureCount, desiredFeatures.begin(),
|
||||
desiredFeatures.end(), std::back_inserter(requiredFeatures));
|
||||
wgpu::DeviceDescriptor deviceDescriptor{};
|
||||
deviceDescriptor.label = "graphics_device";
|
||||
deviceDescriptor.defaultQueue.label = "default_queue";
|
||||
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
|
||||
deviceDescriptor.requiredFeatures = enabledFeatures.data();
|
||||
deviceDescriptor.requiredFeatureCount = requiredFeatures.size();
|
||||
deviceDescriptor.requiredFeatures = requiredFeatures.data();
|
||||
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
||||
wgpu::StringView message) {
|
||||
|
||||
@@ -1140,7 +1140,6 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* const parser) {
|
||||
UTILS_UNUSED_IN_RELEASE bool success;
|
||||
|
||||
|
||||
success = parser->getDescriptorBindings(&mProgramDescriptorBindings);
|
||||
assert_invariant(success);
|
||||
|
||||
@@ -1148,22 +1147,6 @@ void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* con
|
||||
success = parser->getDescriptorSetLayout(&descriptorSetLayout);
|
||||
assert_invariant(success);
|
||||
|
||||
// Setup Labels for debugging
|
||||
utils::CString namestr;
|
||||
utils::StaticString perViewStr = "_perView";
|
||||
utils::StaticString singleStr = "_single";
|
||||
|
||||
parser->getName(&namestr);
|
||||
|
||||
utils::CString singleLabel(namestr.c_str(), namestr.length() + singleStr.length());
|
||||
memccpy(singleLabel.c_str() + namestr.length(), singleStr.c_str(), '\0', singleStr.length());
|
||||
|
||||
utils::CString perViewLabel(namestr.c_str(), namestr.length() + perViewStr.length());
|
||||
memccpy(perViewLabel.c_str() + namestr.length(), perViewStr.c_str(), '\0', perViewStr.length());
|
||||
|
||||
descriptorSetLayout[0].label = namestr;
|
||||
descriptorSetLayout[1].label = perViewLabel;
|
||||
|
||||
mDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout[0]) };
|
||||
|
||||
@@ -33,13 +33,11 @@ namespace filament::descriptor_sets {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{
|
||||
utils::StaticString("postProcess"),{
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
}};
|
||||
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{
|
||||
utils::StaticString("depthVariant"),{
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
}};
|
||||
|
||||
@@ -48,16 +46,14 @@ static DescriptorSetLayout const depthVariantDescriptorSetLayout{
|
||||
// dedicated SSR vertex shader), which uses perViewDescriptorSetLayout.
|
||||
// This means that PerViewBindingPoints::SHADOWS must be in the layout even though it's not used
|
||||
// by the SSR variant.
|
||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{
|
||||
utils::StaticString("ssrVariant"),{
|
||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
}};
|
||||
|
||||
static DescriptorSetLayout perViewDescriptorSetLayout = {
|
||||
utils::StaticString("perView"),{
|
||||
static DescriptorSetLayout perViewDescriptorSetLayout = {{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
|
||||
@@ -72,8 +68,7 @@ static DescriptorSetLayout perViewDescriptorSetLayout = {
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
|
||||
}};
|
||||
|
||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {
|
||||
utils::StaticString("perRenderable"),{
|
||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
||||
|
||||
@@ -229,10 +229,10 @@ MaybeError ValidateWriteBuffer(const DeviceBase* device,
|
||||
uint64_t size) {
|
||||
DAWN_TRY(device->ValidateObject(buffer));
|
||||
|
||||
// DAWN_INVALID_IF(bufferOffset % 4 != 0, "BufferOffset (%u) is not a multiple of 4.",
|
||||
// bufferOffset);
|
||||
//
|
||||
// DAWN_INVALID_IF(size % 4 != 0, "Size (%u) is not a multiple of 4.", size);
|
||||
DAWN_INVALID_IF(bufferOffset % 4 != 0, "BufferOffset (%u) is not a multiple of 4.",
|
||||
bufferOffset);
|
||||
|
||||
DAWN_INVALID_IF(size % 4 != 0, "Size (%u) is not a multiple of 4.", size);
|
||||
|
||||
uint64_t bufferSize = buffer->GetSize();
|
||||
DAWN_INVALID_IF(bufferOffset > bufferSize || size > (bufferSize - bufferOffset),
|
||||
|
||||
Reference in New Issue
Block a user