Compare commits

..
179 changed files with 2292 additions and 2177 deletions
+6 -8
View File
@@ -17,17 +17,15 @@ runs:
echo "Date: ${TSTAMP}" >> /tmp/commit_msg.txt
echo "" >> /tmp/commit_msg.txt
echo "${{ github.event.head_commit.message }}" >> /tmp/commit_msg.txt
- name: Find commit message (PR)
- name: Checkout code
shell: bash
id: checkout_code
if: github.event_name == 'pull_request'
run: |
echo "+++++ head commit message +++++"
echo "$(git log -1 --no-merges)"
echo "+++++++++++++++++++++++++++++++"
echo "hash=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
git checkout ${{ github.event.pull_request.head.sha }}
echo "$(git log -1 --no-merges)" >> /tmp/commit_msg.txt
git checkout ${{ github.event.pull_request.head.ref }}
COMMIT_MESSAGE=$(git log -1)
echo "$(git log -1)" >> /tmp/commit_msg.txt
- shell: bash
id: action_output
run: |
@@ -38,8 +36,8 @@ runs:
echo "----- got commit message ---"
cat /tmp/commit_msg.txt
echo "----------------------------"
- name: Cleanup Find commit message (PR)
- name: Uncheckout code
shell: bash
if: github.event_name == 'pull_request'
run: |
git checkout ${{ steps.checkout_code.outputs.hash }}
git checkout ${{ steps.checkout_code.outputs.hash }}
+3
View File
@@ -122,6 +122,9 @@ jobs:
fetch-depth: 0
- id: get_commit_msg
uses: ./.github/actions/get-commit-msg
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Cache Mesa and deps
uses: actions/cache@v4
-6
View File
@@ -49,8 +49,6 @@ option(FILAMENT_SUPPORTS_OSMESA "Enable OSMesa (headless GL context) for Filamen
option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
option(FILAMENT_USE_ABSEIL_LOGGING "Use Abseil to log, may increase binary size" OFF)
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
# On the regular filament build (where size is of less concern), we enable GTAO by default.
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
@@ -593,10 +591,6 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT FILAMENT_BACKEND_DEBUG_FLAG STREQU
unset(FILAMENT_BACKEND_DEBUG_FLAG)
endif()
if (FILAMENT_USE_ABSEIL_LOGGING)
add_definitions(-DFILAMENT_USE_ABSEIL_LOGGING)
endif()
# ==================================================================================================
# Material compilation flags
# ==================================================================================================
+5 -1
View File
@@ -6,4 +6,8 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
**If you are cherry-picking a commit into an rc/ branch**: add the release note under the
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
## Release notes for next branch cut
- samples: samples now have a CLI to select backend api
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]
- materials: new mutable specialization constants feature. See the [materials documentation](https://google.github.io/filament/Materials.html) for details. [⚠️ **New Material Version**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.61.1'
implementation 'com.google.android.filament:filament-android:1.61.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.61.1'
pod 'Filament', '~> 1.61.0'
```
## Documentation
-4
View File
@@ -7,10 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.61.2
- samples: samples now have a CLI to select backend api
## v1.61.1
-5
View File
@@ -59,10 +59,6 @@ add_library(smol-v STATIC IMPORTED)
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
add_library(abseil STATIC IMPORTED)
set_target_properties(abseil PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libabseil.a)
if (FILAMENT_ENABLE_FGVIEWER)
add_library(fgviewer STATIC IMPORTED)
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
@@ -132,7 +128,6 @@ target_link_libraries(filament-jni
PRIVATE jnigraphics
PRIVATE utils
PRIVATE perfetto
PRIVATE abseil
# libgeometry is PUBLIC because gltfio uses it.
PUBLIC geometry
@@ -60,6 +60,14 @@ static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name
env->ReleaseStringUTFChars(name_, name);
}
template<typename T>
static void setConstant(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
instance->setConstant(name, v);
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetParameterBool(JNIEnv *env, jclass,
@@ -264,6 +272,13 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_, jboolean x) {
setConstant(env, nativeMaterialInstance, name_, bool(x));
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetScissor(
@@ -402,6 +402,16 @@ public class MaterialInstance {
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
}
/**
* Sets the value of a bool constant.
*
* @param name the name of the material constant
* @param x the value of the material constant
*/
public void setConstant(@NonNull String name, boolean x) {
nSetConstantBool(getNativeObject(), name, x);
}
/**
* Set-up a custom scissor rectangle; by default it is disabled.
*
@@ -921,6 +931,9 @@ public class MaterialInstance {
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
private static native void nSetConstantBool(long nativeMaterialInstance,
@NonNull String name, boolean x);
private static native void nSetParameterTexture(long nativeMaterialInstance,
@NonNull String name, long nativeTexture, long sampler);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.61.1
VERSION_NAME=1.61.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+6 -7
View File
@@ -204,10 +204,9 @@
"license": "Apache-2.0"
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -410,9 +409,9 @@
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="
},
"brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
+31 -24
View File
@@ -211,7 +211,7 @@ when using textures.
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by `baseColor`).
(reflected light takes on the color of the surfaced as defined by `baseColor`).
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
larger version).
@@ -247,7 +247,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
`roughness` property will also affect the refractions, as shown in figure
[roughnessRefractionProperty] (click on the image to see a larger version).
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
@@ -306,7 +306,7 @@ The sheen color controls the color appearance and strength of an optional sheen
base layer described by the properties above. The sheen layer always sits below the clear coat layer
if such a layer is present.
The sheen layer can be used to represent cloth and fabric materials. Please refer to
The sheen layer can be used to represent cloth and fabric materials. Please refer to
section [Cloth model] for more information about cloth and fabric materials.
The effect of `sheenColor` is shown in figure [materialSheenColor]
@@ -519,14 +519,14 @@ light to bend further away from the initial path.
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
Material | IOR
Material | IOR
--------------------------:|:-----------------
Air | 1.0
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
[Table [commonMatIOR]: Index of refraction of common materials]
The appearance of a refractive material will greatly depend on the `refractionType` and
@@ -1091,30 +1091,37 @@ Value
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
described in table [materialConstantsTypes].
Type | Description | Default
:----------------------|:-----------------------------------------|:------------------
int | A signed, 32 bit GLSL int | 0
float | A single-precision GLSL float | 0.0
bool | A GLSL bool | false
Constants may also be specified as mutable by setting the `mutable` property to `true`. Only
`bool` spec constants may be specified as mutable.
| Type | Description | Default | May be mutable? |
|:------|:------------------------------|:--------|-----------------|
| int | A signed, 32 bit GLSL int | | no |
| float | A single-precision GLSL float | 0.0 | no |
| bool | A GLSL bool | false | yes |
[Table [materialConstantsTypes]: Material constants types]
Description
: Lists the constant parameters accepted by your material. These constants can be set, or
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
the same material package with differing constant parameter specializations. Once a material is
loaded from a material package, its constant parameters cannot be changed. Compared to regular
parameters, constant parameters allow the compiler to generate more efficient code. Access
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
a constant parameter named `myConstant` is accessed in the shader as
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
used.
the same material package with differing constant parameter specializations. If a constant
parameter is specialized as mutable, it may be changed at any time on a per-instance basis via
`MaterialInstance::setConstant(name, value)`. Otherwise, once a material is loaded from a
material package, its constant parameters cannot be changed.
Compared to regular parameters, constant parameters allow the compiler to generate more
efficient code. Access constant parameters from the shader by prefixing the name with
`materialConstant_`. For example, a constant parameter named `myConstant` is accessed in the
shader as `materialConstant_myConstant`. If a constant parameter is not set at runtime, the
default is used.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
constants : [
{
name : overrideAlpha,
type : bool
type : bool,
mutable : true,
},
{
name : customAlpha,
@@ -2553,7 +2560,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
respectively a boolean and a cubemap texture.
```text
$ matc --reflect parameters filament/src/materials/skybox.mat
$ matc --reflect parameters filament/src/materials/skybox.mat
{
"parameters": [
{
@@ -2569,7 +2576,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
}
]
}
```
```
### --variant-filter
-4
View File
@@ -601,10 +601,6 @@ target_link_libraries(${TARGET} PUBLIC filaflat)
target_link_libraries(${TARGET} PUBLIC filabridge)
target_link_libraries(${TARGET} PUBLIC ibl-lite)
if (FILAMENT_USE_ABSEIL_LOGGING)
target_link_libraries(${TARGET} PUBLIC absl::log)
endif()
if (FILAMENT_ENABLE_FGVIEWER)
target_link_libraries(${TARGET} PUBLIC fgviewer)
add_definitions(-DFILAMENT_ENABLE_FGVIEWER=1)
+4 -5
View File
@@ -92,7 +92,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux
```make
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl -labseil
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
CC=clang++
main: main.o
@@ -110,13 +110,12 @@ clean:
### macOS
```make
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl -labseil
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
CC=clang++
ARCH ?= $(shell uname -m)
main: main.o
$(CC) -Llib/$(ARCH)/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main
$(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++17 -c main.cpp
@@ -140,7 +139,7 @@ used to change the run-time library version.
```make
FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib abseil.lib
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib
CC=cl.exe
main.exe: main.obj
+1 -5
View File
@@ -257,7 +257,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
list(APPEND SRCS
include/backend/platforms/WebGPUPlatform.h
src/webgpu/platform/WebGPUPlatform.cpp
src/webgpu/SpdMipmapGenerator/SpdMipmapGenerator.cpp
src/webgpu/WebGPUBufferBase.cpp
src/webgpu/WebGPUBufferBase.h
src/webgpu/WebGPUBufferObject.cpp
@@ -287,6 +286,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUVertexBuffer.h
src/webgpu/WebGPUVertexBufferInfo.cpp
src/webgpu/WebGPUVertexBufferInfo.h
src/webgpu/SpdMipmapGenerator.cpp
)
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
@@ -404,10 +404,6 @@ endif()
target_link_libraries(${TARGET} PUBLIC math)
target_link_libraries(${TARGET} PUBLIC utils)
if (FILAMENT_USE_ABSEIL_LOGGING)
target_link_libraries(${TARGET} PRIVATE absl::log)
endif()
# Android, iOS, and WebGL do not use bluegl.
if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WEBGL)
target_link_libraries(${TARGET} PRIVATE bluegl)
@@ -474,12 +474,7 @@ constexpr std::string_view to_string(DescriptorType type) noexcept {
enum class DescriptorFlags : uint8_t {
NONE = 0x00,
// Indicate a UNIFORM_BUFFER will have dynamic offsets.
DYNAMIC_OFFSET = 0x01,
// To indicate a texture/sampler type should be unfiltered.
UNFILTERABLE = 0x02,
DYNAMIC_OFFSET = 0x01
};
using descriptor_set_t = uint8_t;
@@ -149,13 +149,6 @@ public:
* - PlatformEGLAndroid
*/
bool assertNativeWindowIsValid = false;
/**
* The action to take if a Drawable cannot be acquired. If true, the
* frame is aborted instead of panic. This is only supported for:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
};
Platform() noexcept;
+4 -1
View File
@@ -20,6 +20,7 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/bitset.h>
#include <backend/DriverEnums.h>
@@ -66,6 +67,7 @@ public:
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
using MutableSpecConstantsInfo = utils::bitset8;
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
@@ -102,7 +104,8 @@ public:
Program& descriptorBindings(backend::descriptor_set_t set,
DescriptorBindingsInfo descriptorBindings) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants,
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
struct PushConstant {
utils::CString name;
@@ -128,7 +128,7 @@ protected:
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
/**
* Logs glGetError() to LOG(ERROR)
* Logs glGetError() to slog.e
* @param name a string giving some context on the error. Typically __func__.
*/
static void logEglError(const char* name) noexcept;
@@ -39,7 +39,7 @@
#define FILAMENT_DEBUG_COMMANDS_NONE 0x0
// Command debugging enabled. No logging by default.
#define FILAMENT_DEBUG_COMMANDS_ENABLE 0x1
// Command debugging enabled. Every command logged to DLOG(INFO)
// Command debugging enabled. Every command logged to slog.d
#define FILAMENT_DEBUG_COMMANDS_LOG 0x2
// Command debugging enabled. Every command logged to systrace
#define FILAMENT_DEBUG_COMMANDS_SYSTRACE 0x4
+2 -2
View File
@@ -16,7 +16,7 @@
#include "private/backend/CircularBuffer.h"
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/architecture.h>
#include <utils/ashmem.h>
@@ -127,7 +127,7 @@ void* CircularBuffer::alloc(size_t size) {
"couldn't allocate " << (size * 2 / 1024) <<
" KiB of virtual address space for the command buffer";
LOG(WARNING) << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)";
slog.w << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)" << io::endl;
// guard page at the end
void* guard = (void*)(uintptr_t(data) + size * 2);
+9 -8
View File
@@ -20,12 +20,12 @@
#include <private/utils/Tracing.h>
#include <utils/Logger.h>
#include <utils/Mutex.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <algorithm>
#include <mutex>
@@ -121,10 +121,11 @@ void CommandBufferQueue::flush() {
#ifndef NDEBUG
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
DLOG(INFO) << "CommandStream used too much space (will block): "
<< "needed space " << requiredSize << " out of " << mFreeSpace
<< ", totalUsed=" << totalUsed << ", current=" << used
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers";
slog.d << "CommandStream used too much space (will block): "
<< "needed space " << requiredSize << " out of " << mFreeSpace
<< ", totalUsed=" << totalUsed << ", current=" << used
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers"
<< io::endl;
mHighWatermark = std::max(mHighWatermark, totalUsed);
#endif
+5 -9
View File
@@ -22,13 +22,10 @@
#include <utils/CallStack.h>
#endif
#include <private/utils/Tracing.h>
#include <utils/Logger.h>
#include <utils/Profiler.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/sstream.h>
#include <utils/Profiler.h>
#include <cstddef>
#include <functional>
@@ -132,10 +129,9 @@ void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log(std::index_seq
#if DEBUG_COMMAND_STREAM
static_assert(UTILS_HAS_RTTI, "DEBUG_COMMAND_STREAM can only be used with RTTI");
std::string command = utils::CallStack::demangleTypeName(typeid(Command).name()).c_str();
DLOG(INFO) << extractMethodName(command) << " : size=" << sizeof(Command);
utils::io::sstream parameterPack;
printParameterPack(parameterPack, std::get<I>(mArgs)...);
DLOG(INFO) << "\t" << parameterPack.c_str();
slog.d << extractMethodName(command) << " : size=" << sizeof(Command) << "\n\t";
printParameterPack(slog.d, std::get<I>(mArgs)...);
slog.d << io::endl;
#endif
}
+2 -2
View File
@@ -25,9 +25,9 @@
#include <private/utils/Tracing.h>
#include <utils/Logger.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <math/half.h>
@@ -149,7 +149,7 @@ void DriverBase::scheduleRelease(AcquiredImage const& image) noexcept {
void DriverBase::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
if constexpr (bool(FILAMENT_DEBUG_COMMANDS > FILAMENT_DEBUG_COMMANDS_NONE)) {
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_LOG)) {
DLOG(INFO) << methodName;
utils::slog.d << methodName << utils::io::endl;
}
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) {
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
+3 -3
View File
@@ -20,7 +20,7 @@
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
@@ -57,8 +57,8 @@ HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& ar
size_t const maxHeapSize = std::min(area.size(), HANDLE_INDEX_MASK * getAlignment());
if (UTILS_UNLIKELY(maxHeapSize != area.size())) {
LOG(WARNING) << "HandleAllocator heap size reduced to " << maxHeapSize << " from "
<< area.size();
slog.w << "HandleAllocator heap size reduced to "
<< maxHeapSize << " from " << area.size() << io::endl;
}
// make sure we start with a clean arena. This is needed to ensure that all blocks start
+11 -1
View File
@@ -80,8 +80,18 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
return *this;
}
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants,
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept {
// String the two lists together.
mSpecializationConstants = std::move(specConstants);
uint32_t firstMutableIndex = specConstants.size();
mSpecializationConstants.reserve(specConstants.size() + mutableSpecConstants.size());
for (uint32_t i = 0; i < mutableSpecConstants.size(); i++) {
mSpecializationConstants[i + firstMutableIndex] = SpecializationConstant {
.id = i + firstMutableId,
.value = mutableSpecConstants[i],
};
}
return *this;
}
+3 -3
View File
@@ -19,8 +19,8 @@
#include "MetalContext.h"
#include "MetalUtils.h"
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/Log.h>
namespace filament::backend {
@@ -323,7 +323,7 @@ id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const
if (!library || !function) {
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
LOG(ERROR) << description;
utils::slog.e << description << utils::io::endl;
}
}
FILAMENT_CHECK_POSTCONDITION(library && function)
@@ -349,7 +349,7 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
if (!library || !function) {
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
LOG(ERROR) << description;
utils::slog.e << description << utils::io::endl;
}
}
FILAMENT_CHECK_POSTCONDITION(library && function)
@@ -18,8 +18,8 @@
#include "MetalContext.h"
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/trap.h>
#include <thread>
@@ -77,7 +77,8 @@ void MetalBufferPool::releaseBuffer(MetalBufferPoolEntry const *stage) noexcept
auto iter = mUsedStages.find(stage);
if (iter == mUsedStages.end()) {
LOG(ERROR) << "Unknown Metal buffer: " << stage->capacity << " bytes";
utils::slog.e << "Unknown Metal buffer: " << stage->capacity << " bytes"
<< utils::io::endl;
return;
}
stage->lastAccessed = mCurrentFrame;
+3 -4
View File
@@ -18,9 +18,8 @@
#include "MetalHandles.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Logger.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utility>
@@ -113,8 +112,8 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
auto errorCode = (MTLCommandBufferError)buffer.error.code;
if (@available(macOS 11.0, *)) {
if (errorCode == MTLCommandBufferErrorMemoryless) {
LOG(WARNING) << "Metal: memoryless geometry limit reached. Continuing with private "
"storage mode.";
utils::slog.w << "Metal: memoryless geometry limit reached. "
"Continuing with private storage mode." << utils::io::endl;
context->memorylessLimitsReached = true;
}
}
+25 -22
View File
@@ -38,10 +38,10 @@
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include <utils/Invocable.h>
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/sstream.h>
#include <utils/Invocable.h>
#include <algorithm>
@@ -78,18 +78,20 @@ Driver* MetalDriverFactory::create(PlatformMetal* const platform, const Platform
// MetalVertexBufferInfo : 552 moderate
// -- less than or equal to 552 bytes
DLOG(INFO) << "MetalSwapChain: " << sizeof(MetalSwapChain);
DLOG(INFO) << "MetalBufferObject: " << sizeof(MetalBufferObject);
DLOG(INFO) << "MetalVertexBuffer: " << sizeof(MetalVertexBuffer);
DLOG(INFO) << "MetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo);
DLOG(INFO) << "MetalIndexBuffer: " << sizeof(MetalIndexBuffer);
DLOG(INFO) << "MetalRenderPrimitive: " << sizeof(MetalRenderPrimitive);
DLOG(INFO) << "MetalTexture: " << sizeof(MetalTexture);
DLOG(INFO) << "MetalTimerQuery: " << sizeof(MetalTimerQuery);
DLOG(INFO) << "HwStream: " << sizeof(HwStream);
DLOG(INFO) << "MetalRenderTarget: " << sizeof(MetalRenderTarget);
DLOG(INFO) << "MetalFence: " << sizeof(MetalFence);
DLOG(INFO) << "MetalProgram: " << sizeof(MetalProgram);
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
<< "\nMetalTexture: " << sizeof(MetalTexture)
<< "\nMetalTimerQuery: " << sizeof(MetalTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nMetalRenderTarget: " << sizeof(MetalRenderTarget)
<< "\nMetalFence: " << sizeof(MetalFence)
<< "\nMetalProgram: " << sizeof(MetalProgram)
<< utils::io::endl;
#endif
return MetalDriver::create(platform, driverConfig);
}
@@ -133,18 +135,19 @@ MetalDriver::MetalDriver(
initializeSupportedGpuFamilies(mContext);
LOG(INFO) << "Supported GPU families: ";
utils::slog.v << "Supported GPU families: " << utils::io::endl;
if (mContext->highestSupportedGpuFamily.common > 0) {
LOG(INFO) << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common;
utils::slog.v << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common << utils::io::endl;
}
if (mContext->highestSupportedGpuFamily.apple > 0) {
LOG(INFO) << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple;
utils::slog.v << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple << utils::io::endl;
}
if (mContext->highestSupportedGpuFamily.mac > 0) {
LOG(INFO) << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac;
utils::slog.v << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac << utils::io::endl;
}
LOG(INFO) << "Features:";
LOG(INFO) << " readWriteTextureSupport: " << (bool) mContext->device.readWriteTextureSupport;
utils::slog.v << "Features:" << utils::io::endl;
utils::slog.v << " readWriteTextureSupport: " <<
(bool) mContext->device.readWriteTextureSupport << utils::io::endl;
// In order to support texture swizzling, the GPU needs to support it and the system be running
// iOS 13+.
@@ -585,7 +588,7 @@ void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
#if FILAMENT_METAL_DEBUG_LOG
auto handleId = rph.getId();
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
DLOG(INFO) << program;
utils::slog.d << program << utils::io::endl;
#endif
construct_handle<MetalProgram>(rph, *mContext, std::move(program));
}
@@ -2074,7 +2077,7 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
error:&error];
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
LOG(ERROR) << description;
utils::slog.e << description << utils::io::endl;
}
assert_invariant(!error);
@@ -20,14 +20,14 @@
#include "MetalEnums.h"
#include "MetalUtils.h"
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/trap.h>
#define NSERROR_CHECK(message) \
if (error) { \
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; \
LOG(ERROR) << description; \
utils::slog.e << description << utils::io::endl; \
} \
FILAMENT_CHECK_POSTCONDITION(error == nil) << message;
+8 -9
View File
@@ -27,11 +27,10 @@
#include "private/backend/BackendUtils.h"
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Panic.h>
#include <utils/trap.h>
#include <utils/debug.h>
#include <math/scalar.h>
@@ -116,14 +115,14 @@ MetalSwapChain::MetalSwapChain(
type(SwapChainType::CAMETALLAYER) {
if (!(flags & SwapChain::CONFIG_TRANSPARENT) && !nativeWindow.opaque) {
LOG(WARNING) << "Warning: Filament SwapChain has no CONFIG_TRANSPARENT flag, but the "
"CAMetaLayer("
<< (__bridge void*) nativeWindow << ") has .opaque set to NO.";
utils::slog.w << "Warning: Filament SwapChain has no CONFIG_TRANSPARENT flag, "
"but the CAMetaLayer(" << (__bridge void*) nativeWindow << ")"
" has .opaque set to NO." << utils::io::endl;
}
if ((flags & SwapChain::CONFIG_TRANSPARENT) && nativeWindow.opaque) {
LOG(WARNING) << "Warning: Filament SwapChain has the CONFIG_TRANSPARENT flag, but the "
"CAMetaLayer("
<< (__bridge void*) nativeWindow << ") has .opaque set to YES.";
utils::slog.w << "Warning: Filament SwapChain has the CONFIG_TRANSPARENT flag, "
"but the CAMetaLayer(" << (__bridge void*) nativeWindow << ")"
" has .opaque set to YES." << utils::io::endl;
}
// Needed so we can use the SwapChain as a blit source.
@@ -21,7 +21,6 @@
#include <backend/Program.h>
#include <utils/JobSystem.h>
#include <utils/Logger.h>
#include <utils/Mutex.h>
#include <chrono>
@@ -148,7 +147,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
LOG(WARNING) << description;
utils::slog.w << description << utils::io::endl;
errorMessage = error.localizedDescription;
}
PANIC_LOG("Failed to compile Metal program.");
@@ -179,7 +178,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
LOG(WARNING) << description;
utils::slog.w << description << utils::io::endl;
errorMessage = error.localizedDescription;
}
PANIC_LOG("Failed to load main0 in Metal program.");
+3 -3
View File
@@ -18,7 +18,7 @@
#include "MetalEnums.h"
#include <utils/Logger.h>
#include <utils/Log.h>
namespace filament {
namespace backend {
@@ -95,7 +95,7 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
[NSString stringWithFormat:@"Could not create Metal pipeline state: %@",
error ? error.localizedDescription : @"unknown error"];
auto description = [errorMessage cStringUsingEncoding:NSUTF8StringEncoding];
LOG(ERROR) << description;
utils::slog.e << description << utils::io::endl;
[[NSException exceptionWithName:@"MetalRenderPipelineFailure"
reason:errorMessage
userInfo:nil] raise];
@@ -155,7 +155,7 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
// MTLSamplerDescriptor.
// In practice, this means shadows are not supported when running in the simulator.
if (![device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
LOG(WARNING) << "Warning: sample comparison not supported by this GPU";
utils::slog.w << "Warning: sample comparison not supported by this GPU" << utils::io::endl;
samplerDescriptor.compareFunction = MTLCompareFunctionNever;
}
#endif
+4 -6
View File
@@ -19,7 +19,7 @@
#include "MetalDriverFactory.h"
#include <utils/Logger.h>
#include <utils/Log.h>
#import <Foundation/Foundation.h>
@@ -53,9 +53,6 @@ PlatformMetal::~PlatformMetal() noexcept {
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
return MetalDriverFactory::create(this, driverConfig);
}
@@ -132,8 +129,9 @@ void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
result = MTLCreateSystemDefaultDevice();
}
LOG(INFO) << "Selected physical device '"
<< [result.name cStringUsingEncoding:NSUTF8StringEncoding] << "'";
utils::slog.i << "Selected physical device '"
<< [result.name cStringUsingEncoding:NSUTF8StringEncoding] << "'"
<< utils::io::endl;
outDevice.device = result;
mDevice = result;
@@ -360,7 +360,7 @@ void GLDescriptorSet::bind(
}
}, entry.desc);
});
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void GLDescriptorSet::validate(HandleAllocatorGL& allocator,
+10 -16
View File
@@ -18,7 +18,6 @@
#include "private/backend/Driver.h"
#include <utils/Logger.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/trap.h>
@@ -26,7 +25,6 @@
#include <string_view>
#include <stddef.h>
#include <cstdio>
namespace filament::backend {
@@ -57,21 +55,19 @@ std::string_view getGLErrorString(GLenum error) noexcept {
}
UTILS_NOINLINE
GLenum checkGLError(const char* function, size_t line) noexcept {
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const error = glGetError();
if (UTILS_VERY_UNLIKELY(error != GL_NO_ERROR)) {
auto const string = getGLErrorString(error);
char hexError[16];
snprintf(hexError, sizeof(hexError), "%#x", error);
LOG(ERROR) << "OpenGL error " << hexError << " (" << string << ") in \"" << function
<< "\" at line " << line;
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
<< function << "\" at line " << io::dec << line << io::endl;
}
return error;
}
UTILS_NOINLINE
void assertGLError(const char* function, size_t line) noexcept {
GLenum const err = checkGLError(function, line);
void assertGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const err = checkGLError(out, function, line);
if (UTILS_VERY_UNLIKELY(err != GL_NO_ERROR)) {
debug_trap();
}
@@ -101,21 +97,19 @@ std::string_view getFramebufferStatusString(GLenum status) noexcept {
}
UTILS_NOINLINE
GLenum checkFramebufferStatus(GLenum target, const char* function, size_t line) noexcept {
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = glCheckFramebufferStatus(target);
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
auto const string = getFramebufferStatusString(status);
char hexStatus[16];
snprintf(hexStatus, sizeof(hexStatus), "%#x", status);
LOG(ERROR) << "OpenGL framebuffer error " << hexStatus << " (" << string << ") in \""
<< function << "\" at line " << line;
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
<< function << "\" at line " << io::dec << line << io::endl;
}
return status;
}
UTILS_NOINLINE
void assertFramebufferStatus(GLenum target, const char* function, size_t line) noexcept {
GLenum const status = checkFramebufferStatus(target, function, line);
void assertFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = checkFramebufferStatus(out, target, function, line);
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
debug_trap();
}
+10 -10
View File
@@ -33,21 +33,21 @@
namespace filament::backend::GLUtils {
std::string_view getGLErrorString(GLenum error) noexcept;
GLenum checkGLError(const char* function, size_t line) noexcept;
void assertGLError(const char* function, size_t line) noexcept;
GLenum checkGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
void assertGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
std::string_view getFramebufferStatusString(GLenum err) noexcept;
GLenum checkFramebufferStatus(GLenum target, const char* function, size_t line) noexcept;
void assertFramebufferStatus(GLenum target, const char* function, size_t line) noexcept;
GLenum checkFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
#ifdef NDEBUG
# define CHECK_GL_ERROR()
# define CHECK_GL_ERROR_NON_FATAL()
# define CHECK_GL_FRAMEBUFFER_STATUS(target)
# define CHECK_GL_ERROR(out)
# define CHECK_GL_ERROR_NON_FATAL(out)
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target)
#else
# define CHECK_GL_ERROR() { GLUtils::assertGLError(__func__, __LINE__); }
# define CHECK_GL_ERROR_NON_FATAL() { GLUtils::checkGLError(__func__, __LINE__); }
# define CHECK_GL_FRAMEBUFFER_STATUS(target) { GLUtils::checkFramebufferStatus( target, __func__, __LINE__); }
# define CHECK_GL_ERROR(out) { GLUtils::assertGLError(out, __func__, __LINE__); }
# define CHECK_GL_ERROR_NON_FATAL(out) { GLUtils::checkGLError(out, __func__, __LINE__); }
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
#endif
constexpr GLuint getComponentCount(ElementType const type) noexcept {
+51 -30
View File
@@ -22,9 +22,9 @@
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <utils/Logger.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <functional>
@@ -77,8 +77,8 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
state.version = (char const*)glGetString(GL_VERSION);
state.shader = (char const*)glGetString(GL_SHADING_LANGUAGE_VERSION);
LOG(INFO) << "[" << state.vendor << "], [" << state.renderer << "], "
"[" << state.version << "], [" << state.shader << "]";
slog.v << "[" << state.vendor << "], [" << state.renderer << "], "
"[" << state.version << "], [" << state.shader << "]" << io::endl;
/*
* Figure out GL / GLES version, extensions and capabilities we need to
@@ -164,33 +164,51 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
}
#endif
LOG(INFO) << "Feature level: " << +mFeatureLevel;
LOG(INFO) << "Active workarounds: ";
slog.v << "Feature level: " << +mFeatureLevel << '\n';
slog.v << "Active workarounds: " << '\n';
UTILS_NOUNROLL
for (auto [enabled, name, _] : mBugDatabase) {
if (enabled) {
LOG(INFO) << name;
slog.v << name << '\n';
}
}
flush(slog.v);
#ifndef NDEBUG
// this is useful for development
LOG(INFO) << "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = " << gets.max_anisotropy;
LOG(INFO) << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = " << gets.max_combined_texture_image_units;
LOG(INFO) << "GL_MAX_TEXTURE_SIZE = " << gets.max_texture_size;
LOG(INFO) << "GL_MAX_CUBE_MAP_TEXTURE_SIZE = " << gets.max_cubemap_texture_size;
LOG(INFO) << "GL_MAX_3D_TEXTURE_SIZE = " << gets.max_3d_texture_size;
LOG(INFO) << "GL_MAX_ARRAY_TEXTURE_LAYERS = " << gets.max_array_texture_layers;
LOG(INFO) << "GL_MAX_DRAW_BUFFERS = " << gets.max_draw_buffers;
LOG(INFO) << "GL_MAX_RENDERBUFFER_SIZE = " << gets.max_renderbuffer_size;
LOG(INFO) << "GL_MAX_SAMPLES = " << gets.max_samples;
LOG(INFO) << "GL_MAX_TEXTURE_IMAGE_UNITS = " << gets.max_texture_image_units;
LOG(INFO) << "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
<< gets.max_transform_feedback_separate_attribs;
LOG(INFO) << "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size;
LOG(INFO) << "GL_MAX_UNIFORM_BUFFER_BINDINGS = " << gets.max_uniform_buffer_bindings;
LOG(INFO) << "GL_NUM_PROGRAM_BINARY_FORMATS = " << gets.num_program_binary_formats;
LOG(INFO) << "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = " << gets.uniform_buffer_offset_alignment;
slog.v
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = "
<< gets.max_anisotropy << '\n'
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
<< gets.max_combined_texture_image_units << '\n'
<< "GL_MAX_TEXTURE_SIZE = "
<< gets.max_texture_size << '\n'
<< "GL_MAX_CUBE_MAP_TEXTURE_SIZE = "
<< gets.max_cubemap_texture_size << '\n'
<< "GL_MAX_3D_TEXTURE_SIZE = "
<< gets.max_3d_texture_size << '\n'
<< "GL_MAX_ARRAY_TEXTURE_LAYERS = "
<< gets.max_array_texture_layers << '\n'
<< "GL_MAX_DRAW_BUFFERS = "
<< gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE = "
<< gets.max_renderbuffer_size << '\n'
<< "GL_MAX_SAMPLES = "
<< gets.max_samples << '\n'
<< "GL_MAX_TEXTURE_IMAGE_UNITS = "
<< gets.max_texture_image_units << '\n'
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
<< gets.max_transform_feedback_separate_attribs << '\n'
<< "GL_MAX_UNIFORM_BLOCK_SIZE = "
<< gets.max_uniform_block_size << '\n'
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS = "
<< gets.max_uniform_buffer_bindings << '\n'
<< "GL_NUM_PROGRAM_BINARY_FORMATS = "
<< gets.num_program_binary_formats << '\n'
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = "
<< gets.uniform_buffer_offset_alignment << '\n'
;
flush(slog.v);
#endif
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
@@ -224,14 +242,15 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
if (ext.KHR_debug) {
auto cb = +[](GLenum, GLenum type, GLuint, GLenum severity, GLsizei length,
const GLchar* message, const void *) {
auto logSeverity = utils::LogSeverity::kInfo;
io::ostream* stream = &slog.i;
switch (severity) {
case GL_DEBUG_SEVERITY_HIGH: logSeverity = utils::LogSeverity::kError; break;
case GL_DEBUG_SEVERITY_MEDIUM: logSeverity = utils::LogSeverity::kWarning; break;
case GL_DEBUG_SEVERITY_LOW: logSeverity = utils::LogSeverity::kInfo; break;
case GL_DEBUG_SEVERITY_HIGH: stream = &slog.e; break;
case GL_DEBUG_SEVERITY_MEDIUM: stream = &slog.w; break;
case GL_DEBUG_SEVERITY_LOW: stream = &slog.d; break;
case GL_DEBUG_SEVERITY_NOTIFICATION:
default: break;
}
io::ostream& out = *stream;
const char* level = ": ";
switch (type) {
case GL_DEBUG_TYPE_ERROR: level = "ERROR: "; break;
@@ -243,7 +262,7 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
case GL_DEBUG_TYPE_MARKER: level = "MARKER: "; break;
default: break;
}
LOG(LEVEL(logSeverity)) << "KHR_debug " << level << std::string_view{ message, size_t(length) };
out << "KHR_debug " << level << std::string_view{ message, size_t(length) } << io::endl;
};
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
@@ -688,8 +707,9 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
GLUtils::unordered_string_set const exts = GLUtils::split(extensions);
if constexpr (DEBUG_PRINT_EXTENSIONS) {
for (auto extension: exts) {
DLOG(INFO) << "\"" << std::string_view(extension) << "\"";
slog.d << "\"" << std::string_view(extension) << "\"\n";
}
flush(slog.d);
}
// figure out and initialize the extensions we need
@@ -763,8 +783,9 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
}
if constexpr (DEBUG_PRINT_EXTENSIONS) {
for (auto extension: exts) {
DLOG(INFO) << "\"" << std::string_view(extension) << "\"";
slog.d << "\"" << std::string_view(extension) << "\"\n";
}
flush(slog.d);
}
using namespace std::literals;
@@ -1024,7 +1045,7 @@ GLuint OpenGLContext::getSamplerSlow(SamplerParams params) const noexcept {
std::min(gets.max_anisotropy, anisotropy));
}
#endif
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
mSamplerMap[params] = s;
return s;
}
+68 -67
View File
@@ -48,13 +48,12 @@
#include <type_traits>
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/CString.h>
#include <utils/Invocable.h>
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/Slice.h>
#include <utils/Systrace.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
@@ -185,18 +184,20 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
// GLVertexBufferInfo : 132 moderate
// -- less than or equal to 136 bytes
DLOG(INFO) << "GLSwapChain: " << sizeof(GLSwapChain);
DLOG(INFO) << "GLBufferObject: " << sizeof(GLBufferObject);
DLOG(INFO) << "GLVertexBuffer: " << sizeof(GLVertexBuffer);
DLOG(INFO) << "GLVertexBufferInfo: " << sizeof(GLVertexBufferInfo);
DLOG(INFO) << "GLIndexBuffer: " << sizeof(GLIndexBuffer);
DLOG(INFO) << "GLRenderPrimitive: " << sizeof(GLRenderPrimitive);
DLOG(INFO) << "GLTexture: " << sizeof(GLTexture);
DLOG(INFO) << "GLTimerQuery: " << sizeof(GLTimerQuery);
DLOG(INFO) << "GLStream: " << sizeof(GLStream);
DLOG(INFO) << "GLRenderTarget: " << sizeof(GLRenderTarget);
DLOG(INFO) << "GLFence: " << sizeof(GLFence);
DLOG(INFO) << "OpenGLProgram: " << sizeof(OpenGLProgram);
slog.d
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
<< "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo)
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
<< "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive)
<< "\nGLTexture: " << sizeof(GLTexture)
<< "\nGLTimerQuery: " << sizeof(GLTimerQuery)
<< "\nGLStream: " << sizeof(GLStream)
<< "\nGLRenderTarget: " << sizeof(GLRenderTarget)
<< "\nGLFence: " << sizeof(GLFence)
<< "\nOpenGLProgram: " << sizeof(OpenGLProgram)
<< io::endl;
#endif
// here we check we're on a supported version of GL before initializing the driver
@@ -288,7 +289,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
mStreamsWithPendingAcquiredImage.reserve(8);
#ifndef NDEBUG
LOG(INFO) << "OS version: " << mPlatform.getOSVersion();
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
#endif
// Timer queries are core in GL 3.3, otherwise we need EXT_disjoint_timer_query
@@ -667,7 +668,7 @@ void OpenGLDriver::createIndexBufferR(
gl.bindVertexArray(nullptr);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, getBufferUsage(usage));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
@@ -692,7 +693,7 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
glBufferData(bo->gl.binding, byteCount, nullptr, getBufferUsage(usage));
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
@@ -729,7 +730,7 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
// this records the index buffer into the currently bound VAO
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
@@ -759,7 +760,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
}
construct<OpenGLProgram>(ph, *this, std::move(program));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
UTILS_NOINLINE
@@ -952,7 +953,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
renderBufferStorage(t->gl.id, internalFormat, w, h, samples);
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
@@ -998,7 +999,7 @@ void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
assert_invariant(ref);
ref->count++;
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
@@ -1061,7 +1062,7 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
assert_invariant(ref);
ref->count++;
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerType target,
@@ -1224,7 +1225,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
#endif
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
@@ -1484,7 +1485,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
// we shouldn't be here
break;
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
} else
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_multisampled_render_to_texture
@@ -1505,7 +1506,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
GL_RENDERBUFFER, t->gl.id);
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
} else
#endif // GL_EXT_multisampled_render_to_texture
#endif // __EMSCRIPTEN__
@@ -1587,13 +1588,13 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
break;
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
rt->gl.resolve |= resolveFlags;
CHECK_GL_ERROR()
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
CHECK_GL_ERROR(utils::slog.e)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
}
void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width, // NOLINT(readability-convert-member-functions-to-static)
@@ -1622,7 +1623,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
// unbind the renderbuffer, to avoid any later confusion
glBindRenderbuffer(GL_RENDERBUFFER, 0);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createDefaultRenderTargetR(
@@ -1719,7 +1720,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
if (UTILS_LIKELY(!getContext().isES2())) {
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
#endif
@@ -1759,7 +1760,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
assert_invariant(any(targets & TargetBufferFlags::ALL));
assert_invariant(tmin == tmax);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createFenceR(Handle<HwFence> fh, int) {
@@ -2110,7 +2111,7 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer, const m
if (UTILS_UNLIKELY(glstream->user_thread.pending.image)) {
scheduleRelease(glstream->user_thread.pending);
LOG(WARNING) << "Acquired image is set more than once per frame.";
slog.w << "Acquired image is set more than once per frame." << io::endl;
}
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
@@ -2559,7 +2560,7 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// OpenGL context has changed, resynchronize the state with the cache
mContext.synchronizeStateAndCache(index);
DLOG(INFO) << "*** OpenGL context change : " << (index ? "protected" : "default");
slog.d << "*** OpenGL context change : " << (index ? "protected" : "default") << io::endl;
});
mCurrentDrawSwapChain = scDraw;
@@ -2600,7 +2601,7 @@ void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
vb->bufferObjectsVersion = (version + 1) % kMaxVersion;
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::updateIndexBuffer(
@@ -2617,7 +2618,7 @@ void OpenGLDriver::updateIndexBuffer(
scheduleDestroy(std::move(p));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
@@ -2697,7 +2698,7 @@ void OpenGLDriver::updateBufferObject(
scheduleDestroy(std::move(bd));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::updateBufferObjectUnsynchronized(
@@ -2744,7 +2745,7 @@ retry:
scheduleDestroy(std::move(bd));
}
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
#endif
}
@@ -2794,7 +2795,7 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
glGenerateMipmap(t->gl.target);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::setTextureData(GLTexture const* t, uint32_t level,
@@ -2903,7 +2904,7 @@ void OpenGLDriver::setTextureData(GLTexture const* t, uint32_t level,
scheduleDestroy(std::move(p));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::setCompressedTextureData(GLTexture const* t, uint32_t level,
@@ -2989,7 +2990,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture const* t, uint32_t level,
scheduleDestroy(std::move(p));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
@@ -3152,7 +3153,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
TargetBufferFlags discardFlags = params.flags.discardStart & rt->targets;
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
// each render-pass starts with a disabled scissor
gl.disable(GL_SCISSOR_TEST);
@@ -3164,7 +3165,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
if (attachmentCount) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
} else {
// It's important to clear the framebuffer before drawing, as it resets
// the fb to a known state (resets fb compression and possibly other things).
@@ -3251,7 +3252,7 @@ void OpenGLDriver::endRenderPass(int) {
if (attachmentCount) {
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
}
@@ -3297,13 +3298,13 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, read);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, draw);
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(GL_DRAW_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
gl.disable(GL_SCISSOR_TEST);
glBlitFramebuffer(0, 0, (GLint)rt->width, (GLint)rt->height,
0, 0, (GLint)rt->width, (GLint)rt->height, mask, GL_NEAREST);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
#endif
}
@@ -3482,7 +3483,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
if (buffer) {
gl.bindFramebuffer(GL_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, buffer);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
// now we need to flip the buffer vertically to match our API
size_t const stride = p.stride ? p.stride : width;
@@ -3514,7 +3515,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
glBufferData(GL_PIXEL_PACK_BUFFER, pboSize, nullptr, GL_STATIC_DRAW);
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, nullptr);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
// we're forced to make a copy on the heap because otherwise it deletes std::function<> copy
// constructor.
@@ -3554,7 +3555,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
glDeleteBuffers(1, &pbo);
scheduleDestroy(std::move(p));
delete pUserBuffer;
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
});
#endif
}
@@ -3579,7 +3580,7 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
glCopyBufferSubData(bo->gl.binding, GL_PIXEL_PACK_BUFFER, offset, 0, size);
gl.bindBuffer(bo->gl.binding, 0);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
// then, we schedule a mapBuffer of the PBO later, once the fence has signaled
auto* pUserBuffer = new BufferDescriptor(std::move(p));
@@ -3596,7 +3597,7 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
glDeleteBuffers(1, &pbo);
scheduleDestroy(std::move(p));
delete pUserBuffer;
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
});
} else {
gl.bindBuffer(bo->gl.binding, bo->gl.id);
@@ -3609,7 +3610,7 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
}
gl.bindBuffer(bo->gl.binding, 0);
scheduleDestroy(std::move(p));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
#endif
}
@@ -3639,7 +3640,7 @@ void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) noexcept {
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) noexcept {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mGpuCommandCompleteOps.emplace_back(sync, fn);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
@@ -3857,7 +3858,7 @@ void OpenGLDriver::clearWithRasterPipe(TargetBufferFlags clearFlags,
}
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::resolve(
@@ -3968,7 +3969,7 @@ void OpenGLDriver::blit(
case SamplerType::SAMPLER_EXTERNAL:
break;
}
CHECK_GL_FRAMEBUFFER_STATUS(GL_DRAW_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
switch (s->target) {
@@ -3994,14 +3995,14 @@ void OpenGLDriver::blit(
case SamplerType::SAMPLER_EXTERNAL:
break;
}
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
gl.disable(GL_SCISSOR_TEST);
glBlitFramebuffer(
srcOrigin.x, srcOrigin.y, srcOrigin.x + size.x, srcOrigin.y + size.y,
dstOrigin.x, dstOrigin.y, dstOrigin.x + size.x, dstOrigin.y + size.y,
mask, GL_NEAREST);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER);
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
@@ -4064,15 +4065,15 @@ void OpenGLDriver::blitDEPRECATED(TargetBufferFlags buffers,
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, d->gl.fbo);
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(GL_DRAW_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
gl.disable(GL_SCISSOR_TEST);
glBlitFramebuffer(
srcRect.left, srcRect.bottom, srcRect.right(), srcRect.top(),
dstRect.left, dstRect.bottom, dstRect.right(), dstRect.top(),
GL_COLOR_BUFFER_BIT, glFilterMode);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
#endif
}
@@ -4199,9 +4200,9 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
#endif
#if FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL()
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
#endif
}
@@ -4231,9 +4232,9 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
reinterpret_cast<const void*>(indexOffset << rp->gl.indicesShift));
#if FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL()
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
#endif
}
@@ -4283,9 +4284,9 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, uint3 workGroupCou
#endif // BACKEND_OPENGL_LEVEL_GLES31
#if FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL()
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
#endif
}
@@ -214,7 +214,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
case DescriptorType::INPUT_ATTACHMENT:
break;
}
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
}
@@ -114,14 +114,14 @@ void TimerQueryNativeFactory::createTimerQuery(GLTimerQuery* tq) {
tq->state = std::make_shared<GLTimerQuery::State>();
mContext.procs.genQueries(1u, &tq->state->gl.query);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void TimerQueryNativeFactory::destroyTimerQuery(GLTimerQuery* tq) {
assert_invariant(tq->state);
mContext.procs.deleteQueries(1u, &tq->state->gl.query);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
tq->state.reset();
}
@@ -131,14 +131,14 @@ void TimerQueryNativeFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
mContext.procs.beginQuery(GL_TIME_ELAPSED, tq->state->gl.query);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) {
assert_invariant(tq->state);
mContext.procs.endQuery(GL_TIME_ELAPSED);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
@@ -153,7 +153,7 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
GLuint available = 0;
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
if (!available) {
// we need to try this one again later
return false;
@@ -31,14 +31,14 @@
#include <private/utils/Tracing.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/JobSystem.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <algorithm>
#include <array>
@@ -65,9 +65,9 @@ static std::string to_string(bool const b) { return b ? "true" : "false"; }
static std::string to_string(int const i) { return std::to_string(i); }
static std::string to_string(float const f) { return "float(" + std::to_string(f) + ")"; }
static void logCompilationError(ShaderStage shaderType, const char* name, GLuint shaderId,
CString const& sourceCode) noexcept;
static void logProgramLinkError(char const* name, GLuint program) noexcept;
static void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
GLuint shaderId, CString const& sourceCode) noexcept;
static void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept;
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext const& context, char* source,
size_t len) noexcept;
@@ -122,18 +122,12 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
}
std::optional<CallbackManager::Handle> handle{};
// Only valid when the blob functions are provided by users. The validity of this variable
// doesn't guarantee that the program was created from the cache blob.
BlobCacheKey key;
// Used for the `THREAD_POOL` mode.
mutable Mutex lock;
mutable Condition cond;
bool signaled = false;
// Indicate this program was created from the cache blob.
bool retrievedFromBlobCache = false;
};
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() {
@@ -265,7 +259,6 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
// Try retrieving the cached program blob if available.
token->gl.program = mBlobCache.retrieve(&token->key, mDriver.mPlatform, program);
if (token->gl.program) {
token->retrievedFromBlobCache = true;
return token;
}
@@ -344,15 +337,12 @@ GLuint ShaderCompilerService::getProgram(program_token_t& token) {
assert_invariant(token);// This function should be called when the token is still alive.
// Finalize any pending shader compilation tasks only when the token was created without cache.
if (!token->retrievedFromBlobCache) {
if (token->compiler.mMode == Mode::THREAD_POOL) {
auto const job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// It's likely that the job was already completed. But it may be still being
// executed at this moment. Just try waiting for it to avoid a race.
token->wait();
}
if (token->compiler.mMode == Mode::THREAD_POOL) {
auto const job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// It's likely that the job was already completed. But it may be still being
// executed at this moment. Just try waiting for it to avoid a race.
token->wait();
}
}
@@ -443,9 +433,8 @@ void ShaderCompilerService::ensureTokenIsReady(program_token_t const& token) {
// just log warnings here instead of repeatedly checking compile status. If this turns
// out to be a real issue later, we would need to consider doing the canonical way.
if (!isCompileCompleted(token)) {
LOG(WARNING)
<< "Shader compilation for OpenGL program " << token->name.c_str_safe()
<< " is not completed yet. The following program link may not succeed.";
slog.w << "Shader compilation for OpenGL program " << token->name.c_str_safe()
<< " is not completed yet. The following program link may not succeed.";
}
linkProgram(mDriver.getContext(), token);
@@ -655,7 +644,8 @@ void ShaderCompilerService::executeTickOps() noexcept {
}
// Something went wrong. Log the error message.
const ShaderStage type = static_cast<ShaderStage>(i);
logCompilationError(type, token->name.c_str_safe(), shader, token->shaderSourceCode[i]);
logCompilationError(slog.e, type, token->name.c_str_safe(), shader,
token->shaderSourceCode[i]);
}
}
@@ -707,7 +697,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
glGetProgramiv(token->gl.program, GL_LINK_STATUS, &status);
if (UTILS_UNLIKELY(status != GL_TRUE)) {
// Something went wrong. Log the error message.
logProgramLinkError(token->name.c_str_safe(), token->gl.program);
logProgramLinkError(slog.e, token->name.c_str_safe(), token->gl.program);
linked = false;
}
// No need to keep the shaders around regardless of the result of the program linking.
@@ -757,7 +747,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
/* static */ void logCompilationError(ShaderStage shaderType, const char* name,
/* static */ void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
GLuint const shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
{ // scope for the temporary string storage
@@ -779,9 +769,8 @@ UTILS_NOINLINE
CString infoLog(length);
glGetShaderInfoLog(shaderId, length, nullptr, infoLog.data());
LOG(ERROR) << "Compilation error in " << to_string(shaderType) << " shader \"" << name
<< "\":";
LOG(ERROR) << "\"" << infoLog.c_str() << "\"";
out << "Compilation error in " << to_string(shaderType) << " shader \"" << name << "\":\n"
<< "\"" << infoLog.c_str() << "\"" << io::endl;
}
#ifndef NDEBUG
@@ -796,25 +785,26 @@ UTILS_NOINLINE
} else {
line = shader.substr(start, end - start);
}
LOG(ERROR) << lc++ << ": " << line.c_str();
out << lc++ << ": " << line.c_str() << '\n';
if (end == std::string::npos) {
break;
}
start = end + 1;
}
LOG(ERROR) << "";
out << io::endl;
#endif
}
UTILS_NOINLINE
/* static */ void logProgramLinkError(char const* name, GLuint program) noexcept {
/* static */ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
GLint length = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
CString infoLog(length);
glGetProgramInfoLog(program, length, nullptr, infoLog.data());
LOG(ERROR) << "Link error in \"" << name << "\":\n" << "\"" << infoLog.c_str() << "\"";
out << "Link error in \"" << name << "\":\n"
<< "\"" << infoLog.c_str() << "\"" << io::endl;
}
// If usages of the Google-style line directive are present, remove them, as some
@@ -96,7 +96,7 @@ CocoaExternalImage::SharedGl::~SharedGl() noexcept {
CocoaExternalImage::CocoaExternalImage(const CVOpenGLTextureCacheRef textureCache,
const SharedGl &sharedGl) noexcept : mSharedGl(sharedGl), mTextureCache(textureCache) {
glGenFramebuffers(1, &mFBO);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
CocoaExternalImage::~CocoaExternalImage() noexcept {
@@ -125,7 +125,7 @@ bool CocoaExternalImage::set(CVPixelBufferRef image) noexcept {
mTexture = createTextureFromImage(image);
mRgbaTexture = encodeCopyRectangleToTexture2D(CVOpenGLTextureGetName(mTexture),
CVPixelBufferGetWidth(image), CVPixelBufferGetHeight(image));
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
return true;
}
@@ -182,33 +182,33 @@ GLuint CocoaExternalImage::encodeCopyRectangleToTexture2D(GLuint rectangle,
// Create a texture to hold the result of the blit image.
glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
// source textures
glBindSampler(0, mSharedGl.sampler);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, rectangle);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
// destination texture
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
CHECK_GL_ERROR()
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
CHECK_GL_ERROR(utils::slog.e)
// draw
glViewport(0, 0, width, height);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
glUseProgram(mSharedGl.program);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
glDisableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, 3);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
mState.restore();
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
return texture;
}
@@ -119,7 +119,7 @@ CocoaTouchExternalImage::CocoaTouchExternalImage(const CVOpenGLESTextureCacheRef
glGenFramebuffers(1, &mFBO);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
}
CocoaTouchExternalImage::~CocoaTouchExternalImage() noexcept {
@@ -247,7 +247,7 @@ GLuint CocoaTouchExternalImage::encodeColorConversionPass(GLuint yPlaneTexture,
glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
// source textures
glBindSampler(0, mSharedGl.sampler);
@@ -261,8 +261,8 @@ GLuint CocoaTouchExternalImage::encodeColorConversionPass(GLuint yPlaneTexture,
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
CHECK_GL_ERROR()
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
CHECK_GL_ERROR(utils::slog.e)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
// geometry
glBindVertexArray(0);
@@ -275,7 +275,7 @@ GLuint CocoaTouchExternalImage::encodeColorConversionPass(GLuint yPlaneTexture,
glUseProgram(mSharedGl.program);
glDrawArrays(GL_TRIANGLES, 0, 3);
CHECK_GL_ERROR()
CHECK_GL_ERROR(utils::slog.e)
mState.restore();
@@ -20,9 +20,9 @@
#include <backend/Platform.h>
#include <utils/Logger.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#if __has_include(<android/surface_texture.h>)
@@ -59,7 +59,7 @@ void ExternalStreamManagerAndroid::destroy(ExternalStreamManagerAndroid* pExtern
ExternalStreamManagerAndroid::ExternalStreamManagerAndroid() noexcept
: mVm(VirtualMachineEnv::get()) {
if (__builtin_available(android 28, *)) {
DLOG(INFO) << "Using ASurfaceTexture";
slog.d << "Using ASurfaceTexture" << io::endl;
}
}
@@ -32,9 +32,9 @@
#endif
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/Logger.h>
#include <utils/debug.h>
#include <utils/Invocable.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <algorithm>
@@ -74,7 +74,7 @@ void PlatformEGL::logEglError(const char* name) noexcept {
}
void PlatformEGL::logEglError(const char* name, EGLint error) noexcept {
LOG(ERROR) << name << " failed with " << getEglErrorName(error);
slog.e << name << " failed with " << getEglErrorName(error) << io::endl;
}
const char* PlatformEGL::getEglErrorName(EGLint error) noexcept {
@@ -101,7 +101,7 @@ void PlatformEGL::clearGlError() noexcept {
// clear GL error that may have been set by previous calls
GLenum const error = glGetError();
if (error != GL_NO_ERROR) {
LOG(WARNING) << "Ignoring pending GL error " << io::hex << error;
slog.w << "Ignoring pending GL error " << io::hex << error << io::endl;
}
}
@@ -142,7 +142,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
}
if (UTILS_UNLIKELY(!initialized)) {
LOG(ERROR) << "eglInitialize failed";
slog.e << "eglInitialize failed" << io::endl;
return nullptr;
}
@@ -29,12 +29,13 @@
#include <android/native_window.h>
#include <android/hardware_buffer.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <EGL/egl.h>
@@ -326,7 +327,7 @@ bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImag
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
if (eglImage == EGL_NO_IMAGE_KHR) {
// Handle error
LOG(ERROR) << "Failed to create EGL image";
slog.e << "Failed to create EGL image" << io::endl;
glDeleteTextures(1, &texture->id);
return false;
}
@@ -339,7 +340,7 @@ bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImag
glBindTexture(texture->target, texture->id);
GLenum error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
LOG(ERROR) << "Error after glBindTexture: " << error;
slog.e << "Error after glBindTexture: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
@@ -349,7 +350,7 @@ bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImag
glEGLImageTargetTexture2DOES(texture->target, static_cast<GLeglImageOES>(eglImage));
error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
LOG(ERROR) << "Error after glEGLImageTargetTexture2DOES: " << error;
slog.e << "Error after glEGLImageTargetTexture2DOES: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
@@ -407,7 +408,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
if (!clientBuffer) {
LOG(ERROR) << "Unable to get EGLClientBuffer from AHardwareBuffer.";
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
return {};
}
@@ -426,7 +427,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
if (eglImage == EGL_NO_IMAGE_KHR) {
LOG(ERROR) << "eglCreateImageKHR returned no image.";
slog.e << "eglCreateImageKHR returned no image." << io::endl;
return {};
}
@@ -441,7 +442,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
auto patchedCallback = [](void* image, void* userdata) {
Closure* closure = (Closure*)userdata;
if (eglDestroyImageKHR(closure->display, (EGLImageKHR) image) == EGL_FALSE) {
LOG(ERROR) << "eglDestroyImageKHR failed.";
slog.e << "eglDestroyImageKHR failed." << io::endl;
}
closure->acquiredImage.callback(closure->acquiredImage.image, closure->acquiredImage.userData);
delete closure;
@@ -21,9 +21,9 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace utils;
@@ -42,12 +42,12 @@ backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
if (UTILS_UNLIKELY(!bindAPI)) {
LOG(ERROR) << "eglBindAPI EGL_OPENGL_API failed";
slog.e << "eglBindAPI EGL_OPENGL_API failed" << io::endl;
return nullptr;
}
int bindBlueGL = bluegl::bind();
if (UTILS_UNLIKELY(bindBlueGL != 0)) {
LOG(ERROR) << "bluegl bind failed";
slog.e << "bluegl bind failed" << io::endl;
return nullptr;
}
@@ -16,7 +16,7 @@
#include <backend/platforms/PlatformGLX.h>
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <X11/Xlib.h>
@@ -84,7 +84,7 @@ static PFNGLXGETPROCADDRESSPROC getProcAddress;
static bool loadLibraries() {
g_glx.library = dlopen(LIBRARY_GLX, RTLD_LOCAL | RTLD_NOW);
if (!g_glx.library) {
LOG(ERROR) << "Could not find library " << LIBRARY_GLX;
utils::slog.e << "Could not find library " << LIBRARY_GLX << utils::io::endl;
return false;
}
@@ -115,7 +115,7 @@ static bool loadLibraries() {
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
if (!g_x11.library) {
LOG(ERROR) << "Could not find library " << LIBRARY_X11;
utils::slog.e << "Could not find library " << LIBRARY_X11 << utils::io::endl;
return false;
}
@@ -134,7 +134,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
// Get the display device
mGLXDisplay = g_x11.openDisplay(NULL);
if (mGLXDisplay == nullptr) {
LOG(ERROR) << "Failed to open X display. (exiting).";
utils::slog.e << "Failed to open X display. (exiting)." << utils::io::endl;
exit(1);
}
@@ -145,7 +145,8 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
r = g_glx.queryContext(mGLXDisplay, sharedCtx, GLX_FBCONFIG_ID, &usedFbId);
if (r != 0) {
LOG(ERROR) << "Failed to get GLX_FBCONFIG_ID from shared GL context.";
utils::slog.e << "Failed to get GLX_FBCONFIG_ID from shared GL context."
<< utils::io::endl;
return nullptr;
}
@@ -153,7 +154,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
GLXFBConfig* fbConfigs = g_glx.getFbConfigs(mGLXDisplay, 0, &numConfigs);
if (fbConfigs == nullptr) {
LOG(ERROR) << "Failed to get the available GLXFBConfigs.";
utils::slog.e << "Failed to get the available GLXFBConfigs." << utils::io::endl;
return nullptr;
}
@@ -163,7 +164,8 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
for (int i = 0; i < numConfigs; ++i) {
r = g_glx.getFbConfigAttrib(mGLXDisplay, fbConfigs[i], GLX_FBCONFIG_ID, &fbId);
if (r != 0) {
LOG(ERROR) << "Failed to get GLX_FBCONFIG_ID for entry " << i << ".";
utils::slog.e << "Failed to get GLX_FBCONFIG_ID for entry " << i << "."
<< utils::io::endl;
continue;
}
@@ -174,7 +176,8 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
}
if (fbIndex < 0) {
LOG(ERROR) << "Failed to find an `GLXFBConfig` with the requested ID.";
utils::slog.e << "Failed to find an `GLXFBConfig` with the requested ID."
<< utils::io::endl;
return nullptr;
}
@@ -199,7 +202,8 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
getProcAddress((GLubyte*)"glXCreateContextAttribsARB");
if (glXCreateContextAttribs == nullptr) {
LOG(INFO) << "Unable to retrieve function pointer for `glXCreateContextAttribs()`.";
utils::slog.i << "Unable to retrieve function pointer for `glXCreateContextAttribs()`."
<< utils::io::endl;
return nullptr;
}
@@ -32,7 +32,7 @@
#include "GL/glext.h"
#include "GL/wglext.h"
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Panic.h>
namespace {
@@ -55,7 +55,8 @@ void reportWindowsError(DWORD dwError) {
0, nullptr
);
LOG(ERROR) << "Windows error code: " << dwError << ". " << lpMessageBuffer;
utils::slog.e << "Windows error code: " << dwError << ". " << lpMessageBuffer
<< utils::io::endl;
LocalFree(lpMessageBuffer);
}
@@ -105,7 +106,7 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
HDC whdc = mWhdc = GetDC(mHWnd);
if (whdc == NULL) {
dwError = GetLastError();
LOG(ERROR) << "CreateWindowA() failed";
utils::slog.e << "CreateWindowA() failed" << utils::io::endl;
goto error;
}
@@ -116,7 +117,8 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
tempContext = wglCreateContext(whdc);
if (!wglMakeCurrent(whdc, tempContext)) {
dwError = GetLastError();
LOG(ERROR) << "wglMakeCurrent() failed, whdc=" << whdc << ", tempContext=" << tempContext;
utils::slog.e << "wglMakeCurrent() failed, whdc=" << whdc << ", tempContext=" <<
tempContext << utils::io::endl;
goto error;
}
@@ -140,7 +142,7 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
}
if (!mContext) {
LOG(ERROR) << "wglCreateContextAttribs() failed, whdc=" << whdc;
utils::slog.e << "wglCreateContextAttribs() failed, whdc=" << whdc << utils::io::endl;
goto error;
}
@@ -150,7 +152,8 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
if (!wglMakeCurrent(whdc, mContext)) {
dwError = GetLastError();
LOG(ERROR) << "wglMakeCurrent() failed, whdc=" << whdc << ", mContext=" << mContext;
utils::slog.e << "wglMakeCurrent() failed, whdc=" << whdc << ", mContext=" <<
mContext << utils::io::endl;
goto error;
}
@@ -40,7 +40,7 @@ inline void blitFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect, V
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
<< " layout=" << src.getLayout()
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
<< " layout=" << dst.getLayout();
<< " layout=" << dst.getLayout() << utils::io::endl;
}
VkImageSubresourceRange const srcRange = src.getSubresourceRange();
@@ -80,7 +80,7 @@ inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
<< " layout=" << src.getLayout()
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
<< " layout=" << dst.getLayout();
<< " layout=" << dst.getLayout() << utils::io::endl;
}
VkImageSubresourceRange const srcRange = src.getSubresourceRange();
@@ -215,7 +215,7 @@ VkSemaphore VulkanCommandBuffer::submit() {
}
FVK_LOGI << ") "
<< " signal=" << mSubmission
<< " fence=" << mFence;
<< " fence=" << mFence << utils::io::endl;
#endif
mFenceStatus->setStatus(VK_NOT_READY);
@@ -224,7 +224,7 @@ VkSemaphore VulkanCommandBuffer::submit() {
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
FVK_LOGD << "Failed command buffer submission result: " << result;
FVK_LOGD << "Failed command buffer submission result: " << result << utils::io::endl;
}
#endif
assert_invariant(result == VK_SUCCESS);
@@ -490,7 +490,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
mProtectedPool->pushMarker(str, timestamp);
}
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
FVK_LOGD << "----> " << str;
FVK_LOGD << "----> " << str << utils::io::endl;
#endif
}
@@ -502,7 +502,8 @@ void VulkanCommands::popGroupMarker() {
auto const& startTime = ret.second;
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms";
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
#else
mPool->popMarker();
#endif // FVK_DEBUG_PRINT_GROUP_MARKERS
@@ -17,7 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
#define TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
#include <utils/Logger.h>
#include <utils/Log.h>
#include <stdint.h>
@@ -73,8 +73,8 @@
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
// to debug with print statements and want ordered logging (e.g LOG(INFO) and LOG(ERROR) will not
// appear in order of calls).
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
// order of calls).
#define FVK_DEBUG_FORCE_LOG_TO_I 0x00020000
// Enable a minimal set of traces to assess the performance of the backend.
@@ -174,15 +174,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
#if FVK_ENABLED(FVK_DEBUG_FORCE_LOG_TO_I)
#define FVK_LOGI LOG(INFO)
#define FVK_LOGI (utils::slog.i)
#define FVK_LOGD FVK_LOGI
#define FVK_LOGE FVK_LOGI
#define FVK_LOGW FVK_LOGI
#else
#define FVK_LOGE LOG(ERROR)
#define FVK_LOGW LOG(WARNING)
#define FVK_LOGD DLOG(INFO)
#define FVK_LOGI LOG(INFO)
#define FVK_LOGE (utils::slog.e)
#define FVK_LOGW (utils::slog.w)
#define FVK_LOGD (utils::slog.d)
#define FVK_LOGI (utils::slog.i)
#endif
// All vkCreate* functions take an optional allocator. For now we select the default allocator by
+30 -23
View File
@@ -104,15 +104,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
FVK_LOGE << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage;
FVK_LOGE << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
} else {
// TODO: emit best practices warnings about aggressive pipeline barriers.
if (strstr(pMessage, "ALL_GRAPHICS_BIT") || strstr(pMessage, "ALL_COMMANDS_BIT")) {
return VK_FALSE;
}
FVK_LOGW << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage;
FVK_LOGW << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
}
FVK_LOGE;
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -122,16 +122,18 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
void* pUserData) {
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
FVK_LOGE << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage;
FVK_LOGE << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
<< utils::io::endl;
} else {
// TODO: emit best practices warnings about aggressive pipeline barriers.
if (strstr(cbdata->pMessage, "ALL_GRAPHICS_BIT")
|| strstr(cbdata->pMessage, "ALL_COMMANDS_BIT")) {
return VK_FALSE;
}
FVK_LOGW << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage;
FVK_LOGW << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
<< utils::io::endl;
}
FVK_LOGE << "";
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
@@ -274,18 +276,20 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
// VulkanRenderTarget : 312 few
// -- less than or equal to 312 bytes
FVK_LOGD << "VulkanSwapChain: " << sizeof(VulkanSwapChain);
FVK_LOGD << "VulkanBufferObject: " << sizeof(VulkanBufferObject);
FVK_LOGD << "VulkanVertexBuffer: " << sizeof(VulkanVertexBuffer);
FVK_LOGD << "VulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo);
FVK_LOGD << "VulkanIndexBuffer: " << sizeof(VulkanIndexBuffer);
FVK_LOGD << "VulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive);
FVK_LOGD << "VulkanTexture: " << sizeof(VulkanTexture);
FVK_LOGD << "VulkanTimerQuery: " << sizeof(VulkanTimerQuery);
FVK_LOGD << "HwStream: " << sizeof(HwStream);
FVK_LOGD << "VulkanRenderTarget: " << sizeof(VulkanRenderTarget);
FVK_LOGD << "VulkanFence: " << sizeof(VulkanFence);
FVK_LOGD << "VulkanProgram: " << sizeof(VulkanProgram);
FVK_LOGD
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
<< "\nVulkanTexture: " << sizeof(VulkanTexture)
<< "\nVulkanTimerQuery: " << sizeof(VulkanTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nVulkanRenderTarget: " << sizeof(VulkanRenderTarget)
<< "\nVulkanFence: " << sizeof(VulkanFence)
<< "\nVulkanProgram: " << sizeof(VulkanProgram)
<< utils::io::endl;
#endif
assert_invariant(platform);
@@ -795,12 +799,14 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
mResourceManager.gc();
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
<< utils::io::endl;
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
if (!isProtectedContentSupported()) {
FVK_LOGW << "protected swapchain requested, but Platform does not support it";
FVK_LOGW << "protected swapchain requested, but Platform does not support it"
<< utils::io::endl;
}
}
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
@@ -811,7 +817,8 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
<< utils::io::endl;
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
@@ -1299,7 +1306,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
uint64_t const end = results.endTime;
if (begin >= end) {
// TODO: queries might have ran on different command buffers.
FVK_LOGW << "Timestamps are not monotonically increasing. ";
FVK_LOGW << "Timestamps are not monotonically increasing. " << utils::io::endl;
*elapsedTime = 0;
return TimerQueryResult::ERROR;
}
@@ -2037,7 +2044,7 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
assert_invariant(inRenderPass);
inRenderPass = false;
} else if (inRenderPass && OUTSIDE_COMMANDS.find(command) != OUTSIDE_COMMANDS.end()) {
FVK_LOGE << command.data() << " issued inside a render pass.";
FVK_LOGE << command.data() << " issued inside a render pass." << utils::io::endl;
}
#endif
}
@@ -100,7 +100,8 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
<< "for render pass " << config.renderPass << ", "
<< "samples = " << int(config.samples) << ", "
<< "depth = " << (config.depth ? 1 : 0) << ", "
<< "attachmentCount = " << attachmentCount;
<< "attachmentCount = " << attachmentCount
<< utils::io::endl;
#endif
VkFramebufferCreateInfo info {
@@ -340,7 +341,8 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
<< "needsResolveMask = " << int(config.needsResolveMask) << ", "
<< "usesLazilyAllocatedMemory = " << int(config.usesLazilyAllocatedMemory) << ", "
<< "viewCount = " << int(config.viewCount) << ", "
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
<< utils::io::endl;
#endif
return renderPass;
@@ -287,7 +287,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
<< ", " << modules[1] << ")";
<< ", " << modules[1] << ")" << utils::io::endl;
#endif
}
@@ -218,7 +218,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
<< shaderStages[0].module << ", " << shaderStages[1].module << ")";
<< shaderStages[0].module << ", " << shaderStages[1].module << ")"
<< utils::io::endl;
#endif
PipelineCacheEntry cacheEntry = {
.lastUsed = mCurrentTime,
@@ -227,7 +228,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
VKALLOC, &cacheEntry.handle);
assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) {
FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
FVK_LOGE << "vkCreateGraphicsPipelines error " << error << utils::io::endl;
return nullptr;
}
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
@@ -241,7 +242,7 @@ void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> pro
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
FVK_LOGE << "Binding missing shader: " << program->name.c_str();
FVK_LOGE << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
}
@@ -42,7 +42,7 @@ fvkmemory::resource_ptr<VulkanTimerQuery> VulkanQueryManager::getNextQuery(
fvkmemory::ResourceManager* resourceManager) {
auto unused = ~mUsed;
if (unused.empty()) {
FVK_LOGE << "More than " << mUsed.size() << " timers are not supported.";
FVK_LOGE << "More than " << mUsed.size() << " timers are not supported." << utils::io::endl;
return {};
}
@@ -171,7 +171,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
#if FVK_ENABLED(FVK_DEBUG_READ_PIXELS)
FVK_LOGD << "readPixels created image=" << stagingImage
<< " to copy from image=" << srcTexture->getVkImage()
<< " src-layout=" << srcTexture->getLayout(0, 0);
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
#endif
VkMemoryRequirements memReqs;
@@ -188,7 +188,8 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
FVK_LOGW
<< "readPixels is slow because VK_MEMORY_PROPERTY_HOST_CACHED_BIT is not available";
<< "readPixels is slow because VK_MEMORY_PROPERTY_HOST_CACHED_BIT is not available"
<< utils::io::endl;
}
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
@@ -304,7 +305,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
fence = readCompleteFence]() mutable {
VkResult status = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
if (status != VK_SUCCESS) {
FVK_LOGE << "Failed to wait for readPixels fence";
FVK_LOGE << "Failed to wait for readPixels fence" << utils::io::endl;
return;
}
@@ -322,7 +323,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
fvkutils::getComponentCount(srcFormat), srcPixels,
static_cast<int>(subResourceLayout.rowPitch), static_cast<int>(width),
static_cast<int>(height), swizzle)) {
FVK_LOGE << "Unsupported PixelDataFormat or PixelDataType";
FVK_LOGE << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
}
vkUnmapMemory(device, stagingMemory);
@@ -108,9 +108,9 @@ VulkanStage* VulkanStagePool::allocateNewStage(uint32_t capacity) {
#if FVK_ENABLED(FVK_DEBUG_STAGING_ALLOCATION)
if (result != VK_SUCCESS) {
FVK_LOGE << "Allocation error: " << result;
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
} else {
FVK_LOGD << "Allocated stage with hndl " << buffer;
FVK_LOGD << "Allocated stage with hndl " << buffer << utils::io::endl;
}
#endif
+10 -8
View File
@@ -188,8 +188,8 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << vkFormat
<< " is not sampleable with optimal tiling.";
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << vkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
}
#endif
@@ -361,7 +361,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
} else {
FVK_LOGW << "Note: creating 2D views on 3D image is not available on this platform. "
<< "i.e. we cannot render to slices of a 3D image";
<< "i.e. we cannot render to slices of a 3D image" << utils::io::endl;
}
} else if (target == SamplerType::SAMPLER_CUBEMAP) {
imageInfo.arrayLayers = 6;
@@ -420,7 +420,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
<< "type = " << imageInfo.imageType << ", "
<< "flags = " << imageInfo.flags << ", "
<< "target = " << static_cast<int>(target) <<", "
<< "format = " << vkFormat;
<< "format = " << vkFormat << utils::io::endl;
}
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image."
<< " error=" << static_cast<int32_t>(result);
@@ -731,14 +731,14 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
<< range.levelCount << ")" << " from=" << oldLayout << " to=" << newLayout
<< " format=" << mState->mVkFormat << " depth="
<< fvkutils::isVkDepthFormat(mState->mVkFormat)
<< " slice-by-slice=" << transitionSliceBySlice;
<< " slice-by-slice=" << transitionSliceBySlice << utils::io::endl;
#endif
} else {
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION)
FVK_LOGD << "transition texture=" << mState->mTextureImage << " (" << range.baseArrayLayer
<< "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << ","
<< range.levelCount << ")" << " to=" << newLayout
<< " is skipped because of no change in layout";
<< " is skipped because of no change in layout" << utils::io::endl;
#endif
}
@@ -849,7 +849,8 @@ void VulkanTexture::print() const {
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
FVK_LOGD << "[" << mState->mTextureImage << "]: (" << layer << "," << level
<< ")=" << getLayout(layer, level)
<< " primary=" << primary;
<< " primary=" << primary
<< utils::io::endl;
}
}
@@ -858,7 +859,8 @@ void VulkanTexture::print() const {
FVK_LOGD << "[" << mState->mTextureImage << ", imageView=" << view.second << "]=>"
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type;
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type
<< utils::io::endl;
}
}
#endif
@@ -17,7 +17,6 @@
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/VulkanHandles.h"
#include <utils/Logger.h>
#include <utils/Panic.h>
namespace filament::backend::fvkmemory {
@@ -128,11 +127,11 @@ void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
void ResourceManager::print() const noexcept {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
LOG(ERROR) << "-------------------";
utils::slog.e << "-------------------" << utils::io::endl;
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
LOG(ERROR) << " " << getTypeStr((ResourceType) i) << "=" << COUNTER[i];
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;
}
LOG(ERROR) << "+++++++++++++++++++";
utils::slog.e << "+++++++++++++++++++" << utils::io::endl;
#endif
}
@@ -25,9 +25,8 @@
#include "vulkan/utils/Helper.h"
#include <bluevk/BlueVK.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/PrivateImplementation-impl.h>
#include <utils/Panic.h>
#define SWAPCHAIN_RET_FUNC(func, handle, ...) \
if (mImpl->mSurfaceSwapChains.find(handle) != mImpl->mSurfaceSwapChains.end()) { \
@@ -115,7 +114,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
chainStruct(&physicalDeviceProperties2, &driverProperties);
vkGetPhysicalDeviceProperties2(device, &physicalDeviceProperties2);
FVK_LOGI << "Vulkan device driver: " << driverProperties.driverName << " "
<< driverProperties.driverInfo;
<< driverProperties.driverInfo << utils::io::endl;
}
VkPhysicalDeviceProperties deviceProperties;
@@ -152,7 +151,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
<< "(vendor " << utils::io::hex << vendorID << ", "
<< "device " << deviceID << ", "
<< "driver " << driverVersion << ", " << utils::io::dec << "api " << major << "."
<< minor << ")";
<< minor << ")" << utils::io::endl;
}
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -175,7 +174,7 @@ void printDepthFormats(VkPhysicalDevice device) {
FVK_LOGI << format << " ";
}
}
FVK_LOGI << "";
FVK_LOGI << utils::io::endl;
}
#endif
@@ -295,10 +294,10 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
} else {
#if defined(__ANDROID__)
FVK_LOGD << "Validation layers are not available; did you set jniLibs in your "
<< "gradle file?";
<< "gradle file?" << utils::io::endl;
#else
FVK_LOGD << "Validation layer not available; did you install the Vulkan SDK?\n"
<< "Please ensure that VK_LAYER_PATH is set correctly.";
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
#endif // __ANDROID__
}
@@ -985,7 +984,8 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
if (!mImpl->mContext.mProtectedMemorySupported) {
LOG(WARNING) << "protected swapchain requested, but VulkanPlatform does not support it";
utils::slog.w << "protected swapchain requested, but VulkanPlatform does not support it"
<< utils::io::endl;
}
}
@@ -163,7 +163,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
// the number of images, though there may be limits related to the total amount of memory used
// by presentable images."
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images.";
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
<< utils::io::endl;
desiredImageCount = caps.minImageCount;
}
@@ -265,7 +266,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
<< "identity-transform=" << (caps.currentTransform == 1) << ", "
<< "depth=" << mSwapChainBundle.depthFormat << ", "
<< "protected=" << mSwapChainBundle.isProtected;
<< "protected=" << mSwapChainBundle.isProtected
<< io::endl;
VkSemaphoreCreateInfo const semaphoreCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
@@ -291,7 +293,7 @@ VkResult VulkanPlatformSurfaceSwapChain::acquire(VulkanPlatform::ImageSyncData*
// Users should be notified of a suboptimal surface, but it should not cause a cascade of
// log messages or a loop of re-creations.
if (result == VK_SUBOPTIMAL_KHR && !mSuboptimal) {
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain.";
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain." << io::endl;
mSuboptimal = true;
}
return result;
@@ -313,7 +315,7 @@ VkResult VulkanPlatformSurfaceSwapChain::present(uint32_t index, VkSemaphore fin
// On Android Q and above, a suboptimal surface is always reported after screen rotation:
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
if (result == VK_SUBOPTIMAL_KHR && !mSuboptimal) {
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain.";
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain." << utils::io::endl;
mSuboptimal = true;
}
return result;
@@ -1,406 +1,257 @@
#include "SpdMipmapGenerator.h"
#include <sstream>
#include <stdexcept>
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments- do not merge like this
namespace spd {
// Helper to convert enums to strings for map keys or shader code
const char* to_string(wgpu::TextureFormat format) {
switch (format) {
case wgpu::TextureFormat::RGBA8Unorm:
return "rgba8unorm";
case wgpu::TextureFormat::BGRA8Unorm:
return "bgra8unorm";
case wgpu::TextureFormat::R32Float:
return "r32float";
case wgpu::TextureFormat::RG32Float:
return "rg32float";
case wgpu::TextureFormat::RGBA32Float:
return "rgba32float";
case wgpu::TextureFormat::R16Float:
return "r16float";
case wgpu::TextureFormat::RG16Float:
return "rg16float";
case wgpu::TextureFormat::RGBA16Float:
return "rgba16float";
// Add other formats as needed
default:
return "rgba8unorm";
// Helper to convert enums to strings for map keys or shader code
const char* to_string(wgpu::TextureFormat format) {
switch (format) {
case wgpu::TextureFormat::RGBA8Unorm: return "rgba8unorm";
case wgpu::TextureFormat::BGRA8Unorm: return "bgra8unorm";
case wgpu::TextureFormat::R32Float: return "r32float";
case wgpu::TextureFormat::RG32Float: return "rg32float";
case wgpu::TextureFormat::RGBA32Float: return "rgba32float";
case wgpu::TextureFormat::R16Float: return "r16float";
case wgpu::TextureFormat::RG16Float: return "rg16float";
case wgpu::TextureFormat::RGBA16Float: return "rgba16float";
// Add other formats as needed
default: return "rgba8unorm";
}
}
}
std::string MipmapGenerator::GetFilterCode(SPDFilter filter) {
switch (filter) {
case SPDFilter::Min:
return R"(
std::string MipmapGenerator::GetFilterCode(SPDFilter filter) {
switch (filter) {
case SPDFilter::Min:
return R"(
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
return min(min(v0, v1), min(v2, v3));
}
)";
case SPDFilter::Max:
return R"(
case SPDFilter::Max:
return R"(
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
return max(max(v0, v1), max(v2, v3));
}
)";
case SPDFilter::MinMax:
return R"(
case SPDFilter::MinMax:
return R"(
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
let max4 = max(max(v0.xy, v1.xy), max(v2.xy, v3.xy));
return vec4<SPDScalar>(min(min(v0.x, v1.x), min(v2.x, v3.x)), max(max4.x, max4.y), 0.0, 0.0);
}
)";
case SPDFilter::Average:
default:
return R"(
case SPDFilter::Average:
default:
return R"(
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
return (v0 + v1 + v2 + v3) * 0.25;
}
)";
}
}
MipmapGenerator::MipmapGenerator(const wgpu::Device& device)
: m_device(device) {
wgpu::BindGroupLayoutEntry bglEntry{};
bglEntry.binding = 0;
bglEntry.visibility = wgpu::ShaderStage::Compute;
bglEntry.buffer.type = wgpu::BufferBindingType::Uniform;
bglEntry.buffer.minBindingSize = 16;
wgpu::BindGroupLayoutDescriptor bglDesc{};
bglDesc.entryCount = 1;
bglDesc.entries = &bglEntry;
m_internalResourcesBindGroupLayout =
m_device.CreateBindGroupLayout(&bglDesc); // Basic BGL for <= 6 mips
wgpu::Limits deviceLimits;
m_device.GetLimits(&deviceLimits);
// Max number of *output* mips we can generate in a single pass.
// This is device.maxStorageTexturesPerShaderStage - 1 (for the source texture binding),
// further capped at 11 output mips (which means 12 total texture bindings: 1 source + 11
// outputs). WebGPU minspec for maxStorageTexturesPerShaderStage is 4, so
// deviceLimits.maxStorageTexturesPerShaderStage >= 4. Thus, m_maxMipsPerPass will be at least
// std::min(4u - 1, 11u) = 3.
m_maxMipsPerPass =
std::min(static_cast<uint32_t>(deviceLimits.maxStorageTexturesPerShaderStage) - 1, 11u);
m_maxArrayLayers = deviceLimits.maxStorageTexturesPerShaderStage;
// If we can generate more than 6 mips in a pass, we need an advanced BGL
// that includes atomic counter and mid-mip buffer for levels > 5.
if (m_maxMipsPerPass > 6) {
std::vector<wgpu::BindGroupLayoutEntry> entriesAdvanced(3);
// Entry 0: Uniform buffer (DownsamplePassMeta)
entriesAdvanced[0].binding = 0;
entriesAdvanced[0].visibility = wgpu::ShaderStage::Compute;
entriesAdvanced[0].buffer.type = wgpu::BufferBindingType::Uniform;
entriesAdvanced[0].buffer.minBindingSize = 16; // sizeof(DownsamplePassMeta) effectively
// Entry 1: Storage buffer (spd_global_counter)
entriesAdvanced[1].binding = 1;
entriesAdvanced[1].visibility = wgpu::ShaderStage::Compute;
entriesAdvanced[1].buffer.type = wgpu::BufferBindingType::Storage;
entriesAdvanced[1].buffer.minBindingSize = 4; // sizeof(atomic<u32>)
// Entry 2: Storage buffer (mip_dst_6_buffer)
entriesAdvanced[2].binding = 2;
entriesAdvanced[2].visibility = wgpu::ShaderStage::Compute;
entriesAdvanced[2].buffer.type = wgpu::BufferBindingType::Storage;
// Size for array<array<array<vec4<f32>, 64>, 64>> which is 16 (bytes for vec4f32) * 64 * 64
// * arrayLayers. The shader defines it as [slice][uv.y][uv.x], so 64x64 per slice.
// minBindingSize here is for one slice as a baseline. Actual buffer will be larger for
// multiple array layers.
entriesAdvanced[2].buffer.minBindingSize = 16 * 64 * 64;
wgpu::BindGroupLayoutDescriptor bglDescAdvanced{};
bglDescAdvanced.label = "SPD Internal BGL (Advanced)";
bglDescAdvanced.entryCount = static_cast<uint32_t>(entriesAdvanced.size());
bglDescAdvanced.entries = entriesAdvanced.data();
m_internalResourcesBindGroupLayout_Advanced =
m_device.CreateBindGroupLayout(&bglDescAdvanced);
}
}
void MipmapGenerator::PreparePipelines(wgpu::TextureFormat format, SPDFilter filter,
bool halfPrecision) {
SPDScalarType scalarType = SanitizeScalarType(format, halfPrecision);
// Prepare for a reasonable number of mips, up to m_maxMipsPerPass
// The loop should go from 1 up to and including m_maxMipsPerPass.
// If m_maxMipsPerPass is 0 (e.g. maxStorageTexturesPerShaderStage <=1 ), this loop won't run.
for (uint32_t i = 1; i <= m_maxMipsPerPass; ++i) {
GetOrCreatePipeline({ format, scalarType, filter, i });
}
}
SPDScalarType MipmapGenerator::SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision) {
std::string formatStr = to_string(format);
std::transform(formatStr.begin(), formatStr.end(), formatStr.begin(), ::tolower);
SPDScalarType texelType = SPDScalarType::F32; // Default to F32
if (formatStr.find("sint") != std::string::npos) {
texelType = SPDScalarType::I32;
} else if (formatStr.find("uint") != std::string::npos) {
texelType = SPDScalarType::U32;
}
if (halfPrecision && texelType == SPDScalarType::F32) {
if (m_device.HasFeature(wgpu::FeatureName::ShaderF16)) {
return SPDScalarType::F16;
} else {
// Optional: Log a warning here that halfPrecision was requested for a float format
// but the device does not support ShaderF16, so F32 will be used.
}
} else if (halfPrecision && texelType != SPDScalarType::F32) {
// Optional: Log a warning here that halfPrecision was requested for a non-float format.
}
return texelType;
}
SPDPipeline& MipmapGenerator::GetOrCreatePipeline(const PipelineCacheKey& key) {
uint32_t numMipsForPipeline = std::min(key.numMips, m_maxMipsPerPass);
if (m_pipelines.find(key) == m_pipelines.end()) {
// Create the pipeline
SPDPipeline spdPipeline;
std::vector<wgpu::BindGroupLayoutEntry> mipsBglEntries;
// Bindings are 0 for source, 1 to numMipsForPipeline for destinations
for (uint32_t i = 0; i <= numMipsForPipeline; ++i) {
wgpu::BindGroupLayoutEntry entry{};
entry.binding = i;
entry.visibility = wgpu::ShaderStage::Compute;
if (i == 0) {
entry.texture.sampleType = (key.scalarType == SPDScalarType::I32)
? wgpu::TextureSampleType::Sint
: (key.scalarType == SPDScalarType::U32)
? wgpu::TextureSampleType::Uint
: wgpu::TextureSampleType::UnfilterableFloat;
entry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
} else {
entry.storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
entry.storageTexture.format = key.format;
entry.storageTexture.viewDimension = wgpu::TextureViewDimension::e2DArray;
MipmapGenerator::MipmapGenerator(const wgpu::Device& device) : m_device(device) {
wgpu::BindGroupLayoutEntry bglEntry{};
bglEntry.binding = 0;
bglEntry.visibility = wgpu::ShaderStage::Compute;
bglEntry.buffer.type = wgpu::BufferBindingType::Uniform;
bglEntry.buffer.minBindingSize = 16;
wgpu::BindGroupLayoutDescriptor bglDesc{};
bglDesc.entryCount = 1;
bglDesc.entries = &bglEntry;
m_internalResourcesBindGroupLayout = m_device.CreateBindGroupLayout(&bglDesc);
}
void MipmapGenerator::PreparePipelines(wgpu::TextureFormat format, SPDFilter filter, bool halfPrecision) {
SPDScalarType scalarType = SanitizeScalarType(format, halfPrecision);
// Prepare for a reasonable number of mips
for (uint32_t i = 1; i <= 4; ++i) {
GetOrCreatePipeline(format, filter, i, scalarType);
}
}
SPDScalarType MipmapGenerator::SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision) {
std::string formatStr = to_string(format);
std::transform(formatStr.begin(), formatStr.end(), formatStr.begin(), ::tolower);
SPDScalarType texelType = SPDScalarType::F32;
if (formatStr.find("sint") != std::string::npos) {
texelType = SPDScalarType::I32;
} else if (formatStr.find("uint") != std::string::npos) {
texelType = SPDScalarType::U32;
}
if (halfPrecision) {
bool hasF16 = false;
// In a real Dawn app, you would check device.GetSupportedFeatures()
// For now, let's assume it's available if requested.
hasF16 = true;
if (!hasF16) {
// Log warning: half precision requested but not supported
}
mipsBglEntries.push_back(entry);
if (texelType != SPDScalarType::F32) {
// Log warning: half precision for non-float format
}
if (hasF16 && texelType == SPDScalarType::F32) {
return SPDScalarType::F16;
}
}
return texelType;
}
SPDPipeline& MipmapGenerator::GetOrCreatePipeline(wgpu::TextureFormat format, SPDFilter filter, uint32_t numMips, SPDScalarType scalarType) {
if (m_pipelines[format][scalarType][filter].count(numMips) == 0) {
// Create the pipeline
SPDPipeline spdPipeline;
std::vector<wgpu::BindGroupLayoutEntry> mipsBglEntries;
for (uint32_t i = 0; i <= numMips; ++i) {
wgpu::BindGroupLayoutEntry entry{};
entry.binding = i;
entry.visibility = wgpu::ShaderStage::Compute;
if (i == 0) {
entry.texture.sampleType = (scalarType == SPDScalarType::I32) ? wgpu::TextureSampleType::Sint :
(scalarType == SPDScalarType::U32) ? wgpu::TextureSampleType::Uint :
wgpu::TextureSampleType::UnfilterableFloat;
entry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
} else {
entry.storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
entry.storageTexture.format = format;
entry.storageTexture.viewDimension = wgpu::TextureViewDimension::e2DArray;
}
mipsBglEntries.push_back(entry);
}
wgpu::BindGroupLayoutDescriptor mipsBglDesc{};
mipsBglDesc.entryCount = mipsBglEntries.size();
mipsBglDesc.entries = mipsBglEntries.data();
spdPipeline.mipsBindGroupLayout = m_device.CreateBindGroupLayout(&mipsBglDesc);
std::string shaderCode = MakeShaderCode(format, GetFilterCode(filter), numMips, scalarType);
wgpu::ShaderModuleWGSLDescriptor wgslDesc{};
wgslDesc.code = shaderCode.c_str();
wgpu::ShaderModuleDescriptor shaderModuleDesc{};
shaderModuleDesc.nextInChain = &wgslDesc;
wgpu::ShaderModule shaderModule = m_device.CreateShaderModule(&shaderModuleDesc);
wgpu::BindGroupLayout bgls[] = { spdPipeline.mipsBindGroupLayout, m_internalResourcesBindGroupLayout };
wgpu::PipelineLayoutDescriptor layoutDesc{};
layoutDesc.bindGroupLayoutCount = 2;
layoutDesc.bindGroupLayouts = bgls;
wgpu::ComputePipelineDescriptor pipelineDesc{};
pipelineDesc.layout = m_device.CreatePipelineLayout(&layoutDesc);
pipelineDesc.compute.module = shaderModule;
pipelineDesc.compute.entryPoint = "downsample";
spdPipeline.pipeline = m_device.CreateComputePipeline(&pipelineDesc);
m_pipelines[format][scalarType][filter][numMips] = std::move(spdPipeline);
}
return m_pipelines[format][scalarType][filter][numMips];
}
void MipmapGenerator::Generate(
wgpu::CommandEncoder& commandEncoder,
wgpu::Texture srcTexture,
const SPDPassConfig& config)
{
uint32_t width = srcTexture.GetWidth();
uint32_t height = srcTexture.GetHeight();
uint32_t arrayLayerCount = srcTexture.GetDepthOrArrayLayers();
wgpu::Texture target = config.targetTexture ? config.targetTexture : srcTexture;
uint32_t numMips = config.numMips > 0 ? config.numMips : target.GetMipLevelCount() - 1;
if (numMips == 0) return;
SPDScalarType scalarType = SanitizeScalarType(srcTexture.GetFormat(), config.halfPrecision);
SPDPipeline& spdPipeline = GetOrCreatePipeline(target.GetFormat(), config.filter, numMips, scalarType);
// --- Create Bind Group 0 (Mips) ---
std::vector<wgpu::BindGroupEntry> mipEntries;
wgpu::TextureViewDescriptor srcViewDesc{};
srcViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
srcViewDesc.baseMipLevel = config.sourceMipLevel;
srcViewDesc.mipLevelCount = 1;
srcViewDesc.baseArrayLayer = 0;
srcViewDesc.arrayLayerCount = arrayLayerCount;
wgpu::BindGroupEntry srcEntry{};
srcEntry.binding = 0;
srcEntry.textureView = srcTexture.CreateView(&srcViewDesc);
mipEntries.push_back(srcEntry);
for (uint32_t i = 0; i < numMips; ++i) {
wgpu::TextureViewDescriptor dstViewDesc{};
dstViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
dstViewDesc.baseMipLevel = config.sourceMipLevel + i + 1;
dstViewDesc.mipLevelCount = 1;
dstViewDesc.baseArrayLayer = 0;
dstViewDesc.arrayLayerCount = arrayLayerCount;
wgpu::BindGroupEntry dstEntry{};
dstEntry.binding = i + 1;
dstEntry.textureView = target.CreateView(&dstViewDesc);
mipEntries.push_back(dstEntry);
}
wgpu::BindGroupLayoutDescriptor mipsBglDesc{};
mipsBglDesc.entryCount = mipsBglEntries.size();
mipsBglDesc.entries = mipsBglEntries.data();
spdPipeline.mipsBindGroupLayout = m_device.CreateBindGroupLayout(&mipsBglDesc);
wgpu::BindGroupDescriptor mipBindGroupDesc{};
mipBindGroupDesc.layout = spdPipeline.mipsBindGroupLayout;
mipBindGroupDesc.entryCount = mipEntries.size();
mipBindGroupDesc.entries = mipEntries.data();
wgpu::BindGroup mipsBindGroup = m_device.CreateBindGroup(&mipBindGroupDesc);
std::string shaderCode = MakeShaderCode(key.format, GetFilterCode(key.filter),
numMipsForPipeline, key.scalarType);
wgpu::ShaderModuleWGSLDescriptor wgslDesc{};
wgslDesc.code = shaderCode.c_str();
// --- Create Bind Group 1 (Internal Resources) ---
uint32_t numWorkGroupsX = (width + 63) / 64;
uint32_t numWorkGroupsY = (height + 63) / 64;
uint32_t numWorkGroups = numWorkGroupsX * numWorkGroupsY;
wgpu::ShaderModuleDescriptor shaderModuleDesc{};
shaderModuleDesc.nextInChain = &wgslDesc;
wgpu::ShaderModule shaderModule = m_device.CreateShaderModule(&shaderModuleDesc);
struct DownsamplePassMeta {
uint32_t work_group_offset[2] = {0, 0};
uint32_t num_work_groups;
uint32_t mips;
uint32_t padding[12]; // Ensure size is multiple of 16
} meta;
meta.num_work_groups = numWorkGroups;
meta.mips = numMips;
wgpu::BufferDescriptor metaBufferDesc{};
metaBufferDesc.size = sizeof(DownsamplePassMeta);
metaBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
wgpu::Buffer metaBuffer = m_device.CreateBuffer(&metaBufferDesc);
m_device.GetQueue().WriteBuffer(metaBuffer, 0, &meta, sizeof(meta));
wgpu::BindGroupLayout chosenInternalBGL =
(numMipsForPipeline > 6 && m_internalResourcesBindGroupLayout_Advanced)
? m_internalResourcesBindGroupLayout_Advanced
: m_internalResourcesBindGroupLayout;
wgpu::BindGroupLayout bgls[] = { spdPipeline.mipsBindGroupLayout, chosenInternalBGL };
wgpu::PipelineLayoutDescriptor layoutDesc{};
layoutDesc.bindGroupLayoutCount = 2;
layoutDesc.bindGroupLayouts = bgls;
wgpu::ComputePipelineDescriptor pipelineDesc{};
pipelineDesc.layout = m_device.CreatePipelineLayout(&layoutDesc);
pipelineDesc.compute.module = shaderModule;
pipelineDesc.compute.entryPoint = "downsample";
spdPipeline.pipeline = m_device.CreateComputePipeline(&pipelineDesc);
m_pipelines[key] = std::move(spdPipeline);
}
return m_pipelines[key];
}
void MipmapGenerator::Generate(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
const SPDPassConfig& config) {
if (config.numMips <= 1 || config.numMips <= config.sourceMipLevel + 1) {
return;
}
if (m_maxMipsPerPass == 0) {
return;
}
PreparePipelines(srcTexture.GetFormat(), config.filter, config.halfPrecision);
uint32_t totalSrcArrayLayers = srcTexture.GetDepthOrArrayLayers();
// Loop over array layer chunks
for (uint32_t currentBaseArrayLayer = 0; currentBaseArrayLayer < totalSrcArrayLayers;
currentBaseArrayLayer += m_maxArrayLayers) {
uint32_t arrayLayersThisPass =
std::min(m_maxArrayLayers, totalSrcArrayLayers - currentBaseArrayLayer);
uint32_t mipsToGenerateCount = config.numMips - (config.sourceMipLevel + 1);
uint32_t currentSourceMipLevel = config.sourceMipLevel;
while (mipsToGenerateCount > 0) {
uint32_t mipsThisPass = std::min(mipsToGenerateCount, m_maxMipsPerPass);
SPDPassConfig passSpecificConfig = {};
passSpecificConfig.filter = config.filter;
passSpecificConfig.halfPrecision = config.halfPrecision;
passSpecificConfig.targetTexture =
config.targetTexture ? config.targetTexture : srcTexture;
passSpecificConfig.numMips = mipsThisPass;
passSpecificConfig.sourceMipLevel = currentSourceMipLevel;
// Pass baseArrayLayer and numArrayLayers directly to generatePass
generatePass(commandEncoder, srcTexture, passSpecificConfig, currentBaseArrayLayer,
arrayLayersThisPass);
mipsToGenerateCount -= mipsThisPass;
currentSourceMipLevel += mipsThisPass;
}
}
}
void MipmapGenerator::generatePass(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
const SPDPassConfig& passConfig, uint32_t baseArrayLayer, uint32_t numArrayLayers) {
uint32_t width = srcTexture.GetWidth() >> passConfig.sourceMipLevel;
uint32_t height = srcTexture.GetHeight() >> passConfig.sourceMipLevel;
uint32_t arrayLayerCountForThisPass = numArrayLayers;
wgpu::Texture target = passConfig.targetTexture ? passConfig.targetTexture : srcTexture;
uint32_t numMipsThisPass = passConfig.numMips;
if (numMipsThisPass == 0) return;
SPDScalarType scalarType = SanitizeScalarType(srcTexture.GetFormat(), passConfig.halfPrecision);
SPDPipeline& spdPipeline = GetOrCreatePipeline(
{ target.GetFormat(), scalarType, passConfig.filter, numMipsThisPass });
// --- Create Bind Group 0 (Mips) ---
std::vector<wgpu::BindGroupEntry> mipEntries;
wgpu::TextureViewDescriptor srcViewDesc{};
srcViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
srcViewDesc.baseMipLevel = passConfig.sourceMipLevel;
srcViewDesc.mipLevelCount = 1;
srcViewDesc.baseArrayLayer = baseArrayLayer;
srcViewDesc.arrayLayerCount = arrayLayerCountForThisPass;
wgpu::BindGroupEntry srcEntry{};
srcEntry.binding = 0;
srcEntry.textureView = srcTexture.CreateView(&srcViewDesc);
mipEntries.push_back(srcEntry);
for (uint32_t i = 0; i < numMipsThisPass; ++i) {
wgpu::TextureViewDescriptor dstViewDesc{};
dstViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
dstViewDesc.baseMipLevel = passConfig.sourceMipLevel + i + 1;
dstViewDesc.mipLevelCount = 1;
dstViewDesc.baseArrayLayer = baseArrayLayer;
dstViewDesc.arrayLayerCount = arrayLayerCountForThisPass;
wgpu::BindGroupEntry dstEntry{};
dstEntry.binding = i + 1;
dstEntry.textureView = target.CreateView(&dstViewDesc);
mipEntries.push_back(dstEntry);
}
wgpu::BindGroupDescriptor mipBindGroupDesc{};
mipBindGroupDesc.layout = spdPipeline.mipsBindGroupLayout;
mipBindGroupDesc.entryCount = mipEntries.size();
mipBindGroupDesc.entries = mipEntries.data();
wgpu::BindGroup mipsBindGroup = m_device.CreateBindGroup(&mipBindGroupDesc);
// --- Create Bind Group 1 (Internal Resources) ---
uint32_t widthDiv64 = (width + 63) / 64;
uint32_t heightDiv64 = (height + 63) / 64;
uint32_t numWorkGroups = widthDiv64 * heightDiv64;
struct DownsamplePassMeta {
uint32_t work_group_offset[2] = { 0, 0 };
uint32_t num_work_groups;
uint32_t mips;
uint32_t padding[12];
} meta;
meta.num_work_groups = numWorkGroups;
meta.mips = numMipsThisPass;
wgpu::BufferDescriptor metaBufferDesc{};
metaBufferDesc.size = sizeof(DownsamplePassMeta);
metaBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
wgpu::Buffer metaBuffer = m_device.CreateBuffer(&metaBufferDesc);
m_device.GetQueue().WriteBuffer(metaBuffer, 0, &meta, sizeof(meta));
wgpu::BindGroup internalBindGroup;
wgpu::BindGroupLayout chosenInternalBGL =
(numMipsThisPass > 6 && m_internalResourcesBindGroupLayout_Advanced)
? m_internalResourcesBindGroupLayout_Advanced
: m_internalResourcesBindGroupLayout;
if (numMipsThisPass > 6 && m_internalResourcesBindGroupLayout_Advanced) {
wgpu::BufferDescriptor atomicCounterBufferDesc{};
atomicCounterBufferDesc.size = sizeof(uint32_t) * arrayLayerCountForThisPass;
atomicCounterBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
wgpu::Buffer atomicCounterBuffer = m_device.CreateBuffer(&atomicCounterBufferDesc);
commandEncoder.ClearBuffer(atomicCounterBuffer, 0, WGPU_WHOLE_SIZE);
wgpu::BufferDescriptor midMipBufferDesc{};
midMipBufferDesc.size = 64 * 64 * 16 * arrayLayerCountForThisPass;
midMipBufferDesc.usage = wgpu::BufferUsage::Storage;
wgpu::Buffer midMipBuffer = m_device.CreateBuffer(&midMipBufferDesc);
std::vector<wgpu::BindGroupEntry> internalEntries(3);
internalEntries[0].binding = 0;
internalEntries[0].buffer = metaBuffer;
internalEntries[0].size = sizeof(DownsamplePassMeta);
internalEntries[1].binding = 1;
internalEntries[1].buffer = atomicCounterBuffer;
internalEntries[1].size = atomicCounterBuffer.GetSize();
internalEntries[2].binding = 2;
internalEntries[2].buffer = midMipBuffer;
internalEntries[2].size = midMipBuffer.GetSize();
wgpu::BindGroupDescriptor internalBindGroupDesc{};
internalBindGroupDesc.layout = chosenInternalBGL;
internalBindGroupDesc.entryCount = static_cast<uint32_t>(internalEntries.size());
internalBindGroupDesc.entries = internalEntries.data();
internalBindGroup = m_device.CreateBindGroup(&internalBindGroupDesc);
} else {
wgpu::BindGroupEntry metaEntry{};
metaEntry.binding = 0;
metaEntry.buffer = metaBuffer;
metaEntry.size = sizeof(DownsamplePassMeta);
wgpu::BindGroupDescriptor internalBindGroupDesc{};
internalBindGroupDesc.layout = chosenInternalBGL;
internalBindGroupDesc.layout = m_internalResourcesBindGroupLayout;
internalBindGroupDesc.entryCount = 1;
internalBindGroupDesc.entries = &metaEntry;
internalBindGroup = m_device.CreateBindGroup(&internalBindGroupDesc);
wgpu::BindGroup internalBindGroup = m_device.CreateBindGroup(&internalBindGroupDesc);
// --- Dispatch ---
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
pass.SetPipeline(spdPipeline.pipeline);
pass.SetBindGroup(0, mipsBindGroup);
pass.SetBindGroup(1, internalBindGroup);
pass.DispatchWorkgroups(numWorkGroupsX, numWorkGroupsY, arrayLayerCount);
pass.End();
}
// --- Dispatch ---
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
pass.SetPipeline(spdPipeline.pipeline);
pass.SetBindGroup(0, mipsBindGroup);
pass.SetBindGroup(1, internalBindGroup);
pass.DispatchWorkgroups(widthDiv64, heightDiv64, arrayLayerCountForThisPass);
pass.End();
}
// Main shader generation logic
// Main shader generation logic
// Helper function to check if a string is in a vector of strings
bool includes(const std::vector<std::string>& vec, const std::string& str) {
for (const auto& s: vec) {
for (const auto& s : vec) {
if (s == str) {
return true;
}
@@ -408,9 +259,10 @@ bool includes(const std::vector<std::string>& vec, const std::string& str) {
return false;
}
std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string& filterOp,
unsigned int numMips, // Assuming a default value for numMips
SPDScalarType scalarType) { // Default scalarType
std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
const std::string& filterOp,
unsigned int numMips, // Assuming a default value for numMips
SPDScalarType scalarType) { // Default scalarType
std::stringstream ss;
std::string texelType;
@@ -425,7 +277,7 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string&
bool useF16 = (scalarType == SPDScalarType::F16);
std::string filterCode = filterOp;
if (filterOp == SPD_FILTER_AVERAGE && !includes({ "f32", "f16" }, texelType)) {
if (filterOp == SPD_FILTER_AVERAGE && !includes({"f32", "f16"}, texelType)) {
// Replace "* 0.25" with "/ 4"
size_t pos = filterCode.find("* 0.25");
if (pos != std::string::npos) {
@@ -436,9 +288,7 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string&
// Generate mipsBindings
std::string mipsBindings;
for (unsigned int i = 0; i < numMips; ++i) {
mipsBindings += "@group(0) @binding(" + std::to_string(i + 1) + ") var dst_mip_" +
std::to_string(i + 1) + ": texture_storage_2d_array<" +
to_string(outputFormat) + ", write>;\n";
mipsBindings += "@group(0) @binding(" + std::to_string(i + 1) + ") var dst_mip_" + std::to_string(i + 1) + ": texture_storage_2d_array<" + to_string(outputFormat) + ", write>;\n";
}
// Generate mipsAccessorBody
@@ -446,8 +296,7 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string&
for (unsigned int i = 0; i < numMips; ++i) {
if (i == 5 && numMips > 6) {
mipsAccessorBody += " else if mip == 6 {\n";
mipsAccessorBody += " textureStore(dst_mip_6, uv, slice, " +
(useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
mipsAccessorBody += " textureStore(dst_mip_6, uv, slice, " + (useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
mipsAccessorBody += " mip_dst_6_buffer[slice][uv.y][uv.x] = value;\n";
mipsAccessorBody += " }";
} else {
@@ -455,16 +304,12 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string&
mipsAccessorBody += " else ";
}
mipsAccessorBody += "if mip == " + std::to_string(i + 1) + " {\n";
mipsAccessorBody += " textureStore(dst_mip_" + std::to_string(i + 1) +
", uv, slice, " +
(useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
mipsAccessorBody += " textureStore(dst_mip_" + std::to_string(i + 1) + ", uv, slice, " + (useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
mipsAccessorBody += " }";
}
}
std::string mipsAccessor =
"fn store_dst_mip(value: vec4<SPDScalar>, uv: vec2<u32>, slice: u32, mip: u32) {\n" +
mipsAccessorBody + "\n}";
std::string mipsAccessor = "fn store_dst_mip(value: vec4<SPDScalar>, uv: vec2<u32>, slice: u32, mip: u32) {\n" + mipsAccessorBody + "\n}";
std::string midMipAccessor = "return mip_dst_6_buffer[slice][uv.y][uv.x];";
// Start building the final shader code string
@@ -498,8 +343,7 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string&
if (useF16) {
ss << "enable f16;\n";
}
ss << "alias SPDScalar = " << texelType
<< ";\n\n"; // Using texelType here, assuming SPDScalar maps to it.
ss << "alias SPDScalar = " << texelType << ";\n\n"; // Using texelType here, assuming SPDScalar maps to it.
ss << R"(
// Helpers ------------------------------------------------------------------------------------------------------------
@@ -556,10 +400,8 @@ struct DownsamplePassMeta {
// In the original version dst_mip_i is an image2Darray [SPD_MAX_MIP_LEVELS+1], i.e., 12+1, but WGSL doesn't support arrays of textures yet
// Also these are read_write because for mips 7-13, the workgroup reads from mip level 6 - since most formats don't support read_write access in WGSL yet, we use a single read_write buffer in such cases instead
@group(0) @binding(0) var src_mip_0: texture_2d_array<)"
<< texelType << R"(>;
)" << mipsBindings
<< R"(
@group(0) @binding(0) var src_mip_0: texture_2d_array<)" << texelType << R"(>;
)" << mipsBindings << R"(
@group(1) @binding(0) var<uniform> downsample_pass_meta : DownsamplePassMeta;
@group(1) @binding(1) var<storage, read_write> spd_global_counter: array<atomic<u32>>;
@@ -591,8 +433,7 @@ fn load_mid_mip_image(uv: vec2<u32>, slice: u32) -> vec4<SPDScalar> {
ss << R"(
}
)" << mipsAccessor
<< R"(
)" << mipsAccessor << R"(
// Workgroup -----------------------------------------------------------------------------------------------------------
@@ -630,8 +471,7 @@ fn spd_exit_workgroup(num_work_groups: u32, local_invocation_index: u32, slice:
// Pixel access --------------------------------------------------------------------------------------------------------
)" << filterCode
<< R"(
)" << filterCode << R"(
fn spd_store(pix: vec2<u32>, out_value: vec4<SPDScalar>, mip: u32, slice: u32) {
store_dst_mip(out_value, pix, slice, mip + 1);
@@ -930,8 +770,7 @@ fn spd_downsample(workgroup_id: vec2<u32>, local_invocation_index: u32, mips: u3
spd_downsample_next_four(xy.x, xy.y, workgroup_id, local_invocation_index, 2, mips, slice);
)";
if (numMips > 6) {
ss << " spd_downsample_last_6(xy.x, xy.y, local_invocation_index, mips, "
"num_work_groups, slice);\n";
ss << " spd_downsample_last_6(xy.x, xy.y, local_invocation_index, mips, num_work_groups, slice);\n";
}
ss << R"(}
@@ -0,0 +1,83 @@
#pragma once
#include <webgpu/webgpu_cpp.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <optional>
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments- do not merge like this
namespace spd {
// Enum for selecting the downsampling filter.
enum class SPDFilter {
Average,
Min,
Max,
MinMax
};
// Enum for shader scalar types.
enum class SPDScalarType {
F32,
F16,
I32,
U32
};
// Configuration for a single mipmap generation pass.
struct SPDPassConfig {
SPDFilter filter = SPDFilter::Average;
wgpu::Texture targetTexture = nullptr;
uint32_t numMips = 0;
bool halfPrecision = false;
uint32_t sourceMipLevel = 0;
};
// Holds a pipeline and its corresponding bind group layout.
struct SPDPipeline {
wgpu::BindGroupLayout mipsBindGroupLayout = nullptr;
wgpu::ComputePipeline pipeline = nullptr;
};
// Manages pipeline creation, caching, and execution for mipmap generation.
class MipmapGenerator {
public:
MipmapGenerator(const wgpu::Device& device);
// Pre-creates pipelines for specified formats and filters.
void PreparePipelines(wgpu::TextureFormat format, SPDFilter filter, bool halfPrecision = false);
// Generates a compute pass for creating mipmaps.
void Generate(
wgpu::CommandEncoder& commandEncoder,
wgpu::Texture srcTexture,
const SPDPassConfig& config
);
private:
wgpu::Device m_device;
wgpu::BindGroupLayout m_internalResourcesBindGroupLayout;
// Cached pipelines: Map<TextureFormat, Map<SPDScalarType, Map<Filter, Map<NumMips, Pipeline>>>>
std::unordered_map<wgpu::TextureFormat,
std::unordered_map<SPDScalarType,
std::unordered_map<SPDFilter,
std::unordered_map<uint32_t, SPDPipeline>>>> m_pipelines;
// Helper methods
SPDPipeline& GetOrCreatePipeline(wgpu::TextureFormat format, SPDFilter filter, uint32_t numMips, SPDScalarType scalarType);
SPDScalarType SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision);
std::string GetFilterCode(SPDFilter filter);
};
// Assuming SPD_FILTER_AVERAGE is a string constant
const std::string SPD_FILTER_AVERAGE = "value * 0.25"; // Original filter operation
// Generates the WGSL shader code dynamically.
std::string MakeShaderCode(
wgpu::TextureFormat outputFormat,
const std::string& filterOp = SPD_FILTER_AVERAGE,
uint32_t numMips = 0,
SPDScalarType scalarType = SPDScalarType::F32
);
} // namespace spd
@@ -1,5 +0,0 @@
WebGPU does not include a trivial way to create mipmaps, and thus we must provide a render pass or compute pass to generate them.
This directory is the result of porting https://github.com/JolifantoBambla/webgpu-spd to C++, which itself is a WebGPU port of https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/blob/main/docs/samples/single-pass-downsampler.md.
The first version of our port is primarily from a Gemini "Convert this to C++" request, which worked as an MVP with very few changes. Future commits will likely alter this version significantly, but it is being merged in this early state to facilitate rapid iteration.
@@ -1,99 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <tsl/robin_map.h>
#include <vector>
#include <webgpu/webgpu_cpp.h>
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments
namespace spd {
// Enum for selecting the downsampling filter.
enum class SPDFilter { Average, Min, Max, MinMax };
// Enum for shader scalar types.
enum class SPDScalarType { F32, F16, I32, U32 };
// Configuration for a single mipmap generation pass.
struct SPDPassConfig {
SPDFilter filter = SPDFilter::Average;
wgpu::Texture targetTexture = nullptr;
uint32_t numMips = 0; // For the public API, this is total desired mips. Internally, mips for
// the current pass.
bool halfPrecision = false;
uint32_t sourceMipLevel = 0;
};
// Holds a pipeline and its corresponding bind group layout.
struct SPDPipeline {
wgpu::BindGroupLayout mipsBindGroupLayout = nullptr;
wgpu::ComputePipeline pipeline = nullptr;
};
// Manages pipeline creation, caching, and execution for mipmap generation.
class MipmapGenerator {
public:
MipmapGenerator(const wgpu::Device& device);
// Pre-creates pipelines for specified formats and filters.
void PreparePipelines(wgpu::TextureFormat format, SPDFilter filter, bool halfPrecision = false);
// Generates a compute pass for creating mipmaps.
void Generate(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
const SPDPassConfig& config);
private:
void generatePass(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
const SPDPassConfig& passConfig, uint32_t baseArrayLayer,
uint32_t numArrayLayers); // Internal pass config might differ slightly if needed
wgpu::Device m_device;
wgpu::BindGroupLayout m_internalResourcesBindGroupLayout;
wgpu::BindGroupLayout m_internalResourcesBindGroupLayout_Advanced = nullptr;
// Maximum number of mips that can be generated in a single pass.
uint32_t m_maxMipsPerPass;
uint32_t m_maxArrayLayers;
// Key for the pipeline cache.
struct PipelineCacheKey {
wgpu::TextureFormat format;
SPDScalarType scalarType;
SPDFilter filter;
uint32_t numMips;
bool operator==(const PipelineCacheKey& other) const {
return format == other.format && scalarType == other.scalarType &&
filter == other.filter && numMips == other.numMips;
}
};
// Hash function for PipelineCacheKey.
struct PipelineCacheKeyHash {
std::size_t operator()(const PipelineCacheKey& key) const {
std::size_t h1 = std::hash<wgpu::TextureFormat>()(key.format);
std::size_t h2 = std::hash<SPDScalarType>()(key.scalarType);
std::size_t h3 = std::hash<SPDFilter>()(key.filter);
std::size_t h4 = std::hash<uint32_t>()(key.numMips);
// A simple way to combine hashes.
return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);
}
};
// Cached pipelines.
tsl::robin_map<PipelineCacheKey, SPDPipeline, PipelineCacheKeyHash> m_pipelines;
// Helper methods
SPDPipeline& GetOrCreatePipeline(const PipelineCacheKey& key);
SPDScalarType SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision);
std::string GetFilterCode(SPDFilter filter);
};
// Assuming SPD_FILTER_AVERAGE is a string constant
const std::string SPD_FILTER_AVERAGE = "value * 0.25"; // Original filter operation
// Generates the WGSL shader code dynamically.
std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
const std::string& filterOp = SPD_FILTER_AVERAGE, uint32_t numMips = 0,
SPDScalarType scalarType = SPDScalarType::F32);
} // namespace spd
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 Lukas Herzberger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -17,7 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#include <utils/Logger.h>
#include <utils/Log.h>
#include <cstdint>
@@ -32,8 +32,8 @@ constexpr size_t WEBGPU_BUFFER_SIZE_MODULUS = 4;
#define FWGPU_PRINT_SYSTEM 0x00000002
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
// to debug with print statements and want ordered logging (e.g LOG(INFO) and LOG(ERROR) will not
// appear in order of calls).
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
// order of calls).
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
// Useful default combinations
@@ -54,15 +54,15 @@ constexpr size_t WEBGPU_BUFFER_SIZE_MODULUS = 4;
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
#define FWGPU_LOGI LOG(INFO)
#define FWGPU_LOGI (utils::slog.i)
#define FWGPU_LOGD FWGPU_LOGI
#define FWGPU_LOGE FWGPU_LOGI
#define FWGPU_LOGW FWGPU_LOGI
#else
#define FWGPU_LOGE LOG(ERROR)
#define FWGPU_LOGW LOG(WARNING)
#define FWGPU_LOGD DLOG(INFO)
#define FWGPU_LOGI LOG(INFO)
#define FWGPU_LOGE (utils::slog.e)
#define FWGPU_LOGW (utils::slog.w)
#define FWGPU_LOGD (utils::slog.d)
#define FWGPU_LOGI (utils::slog.i)
#endif
constexpr uint64_t REQUEST_ADAPTER_TIMEOUT_NANOSECONDS =
@@ -15,7 +15,6 @@
*/
#include "WebGPUDescriptorSetLayout.h"
#include "WebGPUConstants.h"
#include <backend/DriverEnums.h>
@@ -118,10 +117,10 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
samplerEntryInfo.binding = samplerEntry.binding;
samplerEntry.visibility = wEntry.visibility;
wEntry.texture.multisampled = isMultiSampledTypeDescriptor(fEntry.type);
// TODO: Set once we have the filtering values
if (isDepthDescriptor(fEntry.type)) {
samplerEntry.sampler.type = wgpu::SamplerBindingType::Comparison;
} else if (isIntDescriptor(fEntry.type) ||
any(fEntry.flags & DescriptorFlags::UNFILTERABLE)) {
} else if (isIntDescriptor(fEntry.type)) {
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering;
} else {
samplerEntry.sampler.type = wgpu::SamplerBindingType::Filtering;
@@ -152,11 +151,8 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
if (isDepthDescriptor(fEntry.type)) {
wEntry.texture.sampleType = wgpu::TextureSampleType::Depth;
} else if (isFloatDescriptor(fEntry.type)) {
if (any(fEntry.flags & DescriptorFlags::UNFILTERABLE)) {
wEntry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
} else {
wEntry.texture.sampleType = wgpu::TextureSampleType::Float;
}
// TODO: Set once we have the filtering values
wEntry.texture.sampleType = wgpu::TextureSampleType::Float;
} else if (isIntDescriptor(fEntry.type)) {
wEntry.texture.sampleType = wgpu::TextureSampleType::Sint;
} else if (isUnsignedIntDescriptor(fEntry.type)) {
+56 -39
View File
@@ -28,11 +28,10 @@
#include "WebGPUVertexBuffer.h"
#include "WebGPUVertexBufferInfo.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "SpdMipmapGenerator.h"
#include "CommandStreamDispatcher.h"
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
@@ -65,14 +64,13 @@ Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverCon
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
const Platform::DriverConfig& driverConfig) noexcept
: mPlatform{ platform },
mAdapter{ mPlatform.requestAdapter(nullptr) },
mDevice{ mPlatform.requestDevice(mAdapter) },
mQueue{ mDevice.GetQueue() },
mMipMapGenerator{ mDevice },
mHandleAllocator{ "Handles", driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags } {
: mPlatform(platform),
mHandleAllocator("Handles", driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags) {
mAdapter = mPlatform.requestAdapter(nullptr);
mDevice = mPlatform.requestDevice(mAdapter);
mDevice.GetLimits(&mDeviceLimits);
mQueue = mDevice.GetQueue();
}
WebGPUDriver::~WebGPUDriver() noexcept = default;
@@ -338,15 +336,15 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
FWGPU_LOGW << "WebGPU support is highly experimental, in development, and tested for only a "
"small set of simple samples (e.g. hellotriangle and texturedquad), thus issues "
"are likely to be encountered at this stage.";
"are likely to be encountered at this stage."
<< utils::io::endl;
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
char printSystemHex[16];
snprintf(printSystemHex, sizeof(printSystemHex), "%#x", FWGPU_PRINT_SYSTEM);
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << printSystemHex
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
<< FWGPU_PRINT_SYSTEM << utils::io::dec
<< " bit flag on during build time the application would print system details "
"about the selected graphics device, surface, etc. To see this try "
"rebuilding Filament with that flag, e.g. ./build.sh -x "
<< FWGPU_PRINT_SYSTEM << " ...";
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
#endif
}
@@ -531,8 +529,8 @@ bool WebGPUDriver::isTextureSwizzleSupported() {
}
bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
return WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(
WebGPUTexture::fToWGPUTextureFormat(format));
//todo
return true;
}
bool WebGPUDriver::isRenderTargetFormatSupported(const TextureFormat format) {
@@ -745,35 +743,52 @@ void WebGPUDriver::generateMipmaps(Handle<HwTexture> textureHandle) {
wgpu::Texture wgpuTexture = texture->getTexture();
assert_invariant(wgpuTexture);
const auto usage = wgpuTexture.GetUsage();
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::TextureBinding)
<< "Texture for mipmap generation must have TextureBinding usage. "
"supportsMultipleMipLevels for texture format "
<< static_cast<uint32_t>(wgpuTexture.GetFormat()) << " is "
<< WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(wgpuTexture.GetFormat());
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::StorageBinding)
<< "Texture for mipmap generation must have StorageBinding usage. "
"supportsMultipleMipLevels for texture format "
<< static_cast<uint32_t>(wgpuTexture.GetFormat()) << " is "
<< WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(wgpuTexture.GetFormat());
// const auto usage = wgpuTexture.GetUsage();
// FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::TextureBinding)
// << "Texture for mipmap generation must have TextureBinding usage.";
// FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::StorageBinding)
// << "Texture for mipmap generation must have StorageBinding usage.";
const uint32_t totalMipLevels = wgpuTexture.GetMipLevelCount();
if (totalMipLevels <= 1) {
return;
}
// Determine the maximum number of mips we can generate in a single pass.
// The limit is on *storage* textures, and we need 1 binding for the source texture.
const uint32_t maxMipsPerPass = 2;//mDeviceLimits.maxStorageTexturesPerShaderStage - 1;
FILAMENT_CHECK_POSTCONDITION(maxMipsPerPass > 0)
<< "Device does not support enough storage textures for mipmapping.";
// The generator can be created once.
spd::MipmapGenerator mipmapGenerator(mDevice);
// We will record all passes into a single command encoder.
wgpu::CommandEncoderDescriptor encoderDesc = {};
encoderDesc.label = "Mipmap Command Encoder";
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder(&encoderDesc);
spd::SPDPassConfig spdConfig = { .filter = spd::SPDFilter::Average,
.targetTexture = wgpuTexture,
.numMips = totalMipLevels,
.halfPrecision = false,
.sourceMipLevel = 0 };
uint32_t mipsLeftToGenerate = totalMipLevels - 1;
uint32_t currentSourceMipLevel = 0;
mMipMapGenerator.Generate(encoder, wgpuTexture, spdConfig);
while (mipsLeftToGenerate > 0) {
uint32_t mipsThisPass = std::min(mipsLeftToGenerate, maxMipsPerPass);
spd::SPDPassConfig config = {};
config.filter = spd::SPDFilter::Average;
config.targetTexture = wgpuTexture;
config.numMips = mipsThisPass;
config.sourceMipLevel = currentSourceMipLevel;
// The generator needs to create pipelines for the number of mips in THIS pass.
mipmapGenerator.PreparePipelines(wgpuTexture.GetFormat(), config.filter);
// Generate one batch of mipmaps.
mipmapGenerator.Generate(encoder, wgpuTexture, config);
mipsLeftToGenerate -= mipsThisPass;
currentSourceMipLevel += mipsThisPass;
}
// Finish the encoder and submit all the passes at once.
wgpu::CommandBufferDescriptor cmdBufferDesc = {};
@@ -796,7 +811,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
wgpu::TextureView defaultColorView = nullptr;
wgpu::TextureView defaultDepthStencilView = nullptr;
wgpu::TextureFormat defaultDepthStencilFormat = wgpu::TextureFormat::Undefined;
std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
uint32_t customColorViewCount = 0;
@@ -810,7 +824,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
assert_invariant(mSwapChain && mTextureView);
defaultColorView = mTextureView;
defaultDepthStencilView = mSwapChain->getDepthTextureView();
defaultDepthStencilFormat = mSwapChain->getDepthFormat();
} else {
// Resolve views for custom render target
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
@@ -842,7 +855,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
const uint32_t depthArrayLayer = depthInfo.layer;
customDepthView =
depthTexture->getOrMakeTextureView(depthMipLevel, depthArrayLayer);
customDepthFormat = depthTexture->getViewFormat();
customDepthFormat = depthTexture->getWebGPUFormat();
}
}
@@ -859,7 +872,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
const uint32_t stencilArrayLayer = stencilInfo.layer;
customStencilView =
stencilTexture->getOrMakeTextureView(stencilMipLevel, stencilArrayLayer);
customStencilFormat = stencilTexture->getViewFormat();
customStencilFormat = stencilTexture->getWebGPUFormat();
}
}
}
@@ -868,7 +881,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
params,
defaultColorView,
defaultDepthStencilView,
defaultDepthStencilFormat,
customColorViews.data(),
customColorViewCount,
customDepthView,
@@ -913,6 +925,12 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
.label = "command_encoder"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
if (!mMipQueue.empty()) {
for (auto& handle: mMipQueue) {
generateMipmaps(handle);
}
mMipQueue.clear();
}
assert_invariant(mCommandEncoder);
}
@@ -1167,7 +1185,6 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> descriptorS
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> descriptorSetHandle,
const backend::descriptor_binding_t binding, Handle<HwTexture> textureHandle,
const SamplerParams params) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
auto texture = handleCast<WebGPUTexture>(textureHandle);
+1 -3
View File
@@ -29,8 +29,6 @@
#include <utils/compiler.h>
#include "SpdMipmapGenerator/SpdMipmapGenerator.h"
#include <tsl/robin_map.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
@@ -72,12 +70,12 @@ private:
WebGPUSwapChain* mSwapChain = nullptr;
uint64_t mNextFakeHandle = 1;
wgpu::CommandEncoder mCommandEncoder = nullptr;
std::vector<Handle<HwTexture>> mMipQueue;
wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WebGPURenderTarget* mDefaultRenderTarget = nullptr;
WebGPURenderTarget* mCurrentRenderTarget = nullptr;
spd::MipmapGenerator mMipMapGenerator;
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
@@ -29,7 +29,6 @@
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
#include <dawn/webgpu_cpp_print.h>
#include <array>
#include <cstdint>
@@ -188,7 +187,7 @@ namespace {
switch (status) {
case wgpu::CompilationInfoRequestStatus::CallbackCancelled:
FWGPU_LOGW << "Shader compilation info callback cancelled for "
<< descriptor.label << "?";
<< descriptor.label << "?" << utils::io::endl;
return;
case wgpu::CompilationInfoRequestStatus::Success:
break;
@@ -204,7 +203,8 @@ namespace {
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length;
<< " length:" << message.length
<< utils::io::endl;
break;
case wgpu::CompilationMessageType::Warning:
FWGPU_LOGW
@@ -212,7 +212,7 @@ namespace {
<< message.message << " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length;
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Error:
errorCount++;
@@ -231,7 +231,8 @@ namespace {
<< errorStream.str();
}
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
FWGPU_LOGD << descriptor.label << " compiled successfully";
FWGPU_LOGD << descriptor.label << " compiled successfully"
<< utils::io::endl;
#endif
}),
SHADER_COMPILATION_TIMEOUT_NANOSECONDS);
@@ -73,7 +73,6 @@ wgpu::StoreOp WebGPURenderTarget::getStoreOperation(RenderPassParams const& para
void WebGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& outDescriptor,
RenderPassParams const& params, wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureFormat const& defaultDepthStencilFormat,
wgpu::TextureView const* customColorTextureViews, uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView,
@@ -91,46 +90,33 @@ void WebGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor&
.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b,
params.clearColor.a } });
bool const depthReadonly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
if (defaultDepthStencilTextureView) {
mDepthStencilAttachmentDescriptor = {
.view = defaultDepthStencilTextureView,
.depthLoadOp = depthReadonly ? wgpu::LoadOp::Undefined
: WebGPURenderTarget::getLoadOperation(params,
TargetBufferFlags::DEPTH),
.depthStoreOp = depthReadonly ? wgpu::StoreOp::Undefined
: WebGPURenderTarget::getStoreOperation(params,
TargetBufferFlags::DEPTH),
.depthLoadOp = WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp =
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = depthReadonly,
.stencilLoadOp = wgpu::LoadOp::Undefined,
.stencilStoreOp = wgpu::StoreOp::Undefined,
.depthReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp =
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp =
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
};
if (defaultDepthStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
defaultDepthStencilFormat == wgpu::TextureFormat::Stencil8) {
mDepthStencilAttachmentDescriptor.stencilLoadOp =
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp =
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
}
mHasDepthStencilAttachment = true;
}
} else { // Custom Render Target
} else {// Custom Render Target
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
if (customColorTextureViews[i]) {
mColorAttachmentDescriptors.push_back({ .view = customColorTextureViews[i],
// .resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
.loadOp =
WebGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i)),
.storeOp = WebGPURenderTarget::getStoreOperation(params,
getTargetBufferFlagsAt(i)),
.loadOp = WebGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i)),
.storeOp =
WebGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i)),
.clearValue = { .r = params.clearColor.r,
.g = params.clearColor.g,
.b = params.clearColor.b,
@@ -160,19 +146,14 @@ void WebGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor&
customDepthTextureView ? customDepthTextureView : customStencilTextureView;
if (hasDepth) {
bool const depthReadonly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
mDepthStencilAttachmentDescriptor.depthLoadOp =
depthReadonly ? wgpu::LoadOp::Undefined :
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp =
depthReadonly ? wgpu::StoreOp::Undefined :
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue =
static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly = depthReadonly;
mDepthStencilAttachmentDescriptor.depthReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
} else {
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
@@ -46,7 +46,6 @@ public:
// For default render target:
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureFormat const& defaultDepthStencilFormat,
// For custom render targets:
wgpu::TextureView const* customColorTextureViews, // Array of views
uint32_t customColorTextureViewCount,
+66 -14
View File
@@ -21,27 +21,50 @@
#include <backend/DriverEnums.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <dawn/webgpu_cpp_print.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <sstream>
#include <string>
#endif
#include <string_view>
/**
* Reusable set of convenience functions for strings -- generally string views, literals, &
* strings -- used in the WebGPU backend
* streaming -- used in the WebGPU backend
*/
namespace filament::backend {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
/**
* Convenience template to print WGPU types (which don't support Filament's utils::io::ostream)
* @tparam WebGPUPrintable e.g. wgpu::FeatureName type
* @param out stream to print to
* @param printable the instance to print
* @return the stream printed to
*
* Example usage (because including the template operator override itself does not compile):
* #if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
* utils::io::ostream& operator<<(utils::io::ostream& out,
* const wgpu::FeatureName featureName) noexcept {
* return streamInsertWebGPUPrintable(out, featureName);
* }
* #endif
*/
template<typename WebGPUPrintable>
[[nodiscard]] inline std::string webGPUPrintableToString(const WebGPUPrintable printable) {
std::stringstream out;
out << printable;
return out.str();
utils::io::ostream& streamInsertWebGPUPrintable(utils::io::ostream& out,
const WebGPUPrintable printable) {
std::stringstream printableStream;
printableStream << printable;
out << printableStream.str();
return out;
}
#endif
@@ -92,17 +115,46 @@ template<typename WebGPUPrintable>
}
}
[[nodiscard]] inline std::string adapterOptionsToString(
wgpu::RequestAdapterOptions const& options) {
std::stringstream out;
/**
* Convenience template to print wgpu::RequestAdapterOptions
* @tparam STREAM_TYPE useful namely for streaming to either an utils::io::ostream OR
* utils::details::PanicStream, but also for any kind of stream
* @param out stream to print to
* @param options instance to be printed
* @return stream printed to
*
* Example usage (because including the template operator override itself does not compile):
* template<typename STREAM_TYPE>
* STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::RequestAdapterOptions const& options) noexcept {
* return streamInsertRequestAdapterOptions(out, options);
* }
*/
template<typename STREAM_TYPE>
STREAM_TYPE& streamInsertRequestAdapterOptions(STREAM_TYPE& out,
wgpu::RequestAdapterOptions const& options) noexcept {
out << "power preference " << powerPreferenceToString(options.powerPreference)
<< " force fallback adapter " << bool(options.forceFallbackAdapter) << " backend type "
<< backendTypeToString(options.backendType);
return out.str();
return out;
}
[[nodiscard]] std::string adapterInfoToString(wgpu::AdapterInfo const& info) {
std::stringstream out;
/**
* Convenience template to print wgpu::AdapterInfo
* @tparam STREAM_TYPE useful namely for streaming to either an utils::io::ostream OR
* utils::details::PanicStream, but also for any kind of stream
* @param out stream to print to
* @param options instance to be printed
* @return stream printed to
*
* Example usage (because including the template operator override itself does not compile):
* template<typename STREAM_TYPE>
* STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::AdapterInfo const& info) noexcept {
* return streamInsertRequestAdapterInfo(out, info);
* }
*/
template<class STREAM_TYPE>
STREAM_TYPE& streamInsertRequestAdapterInfo(STREAM_TYPE& out,
wgpu::AdapterInfo const& info) noexcept {
out << "vendor (" << info.vendorID << ") '" << info.vendor
<< "' device (" << info.deviceID << ") '" << info.device
<< "' adapter " << adapterTypeToString(info.adapterType)
@@ -110,7 +162,7 @@ template<typename WebGPUPrintable>
<< " architecture '" << info.architecture
<< "' subgroupMinSize " << info.subgroupMinSize
<< " subgroupMaxSize " << info.subgroupMaxSize;
return out.str();
return out;
}
[[nodiscard]] constexpr std::string_view deviceLostReasonToString(
@@ -123,7 +175,7 @@ template<typename WebGPUPrintable>
}
}
[[nodiscard]] constexpr std::string_view filamentShaderStageToString(const ShaderStage stage) {
[[nodiscard]] constexpr std::string_view filamentShaderStageToString(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return "vertex";
case ShaderStage::FRAGMENT: return "fragment";
+49 -24
View File
@@ -24,62 +24,82 @@
#include "backend/DriverEnums.h"
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstdint>
namespace filament::backend {
namespace {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
utils::io::ostream& operator<<(utils::io::ostream& out, const wgpu::TextureFormat format) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, format);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::TextureUsage textureUsage) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, textureUsage);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::PresentMode presentMode) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, presentMode);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::CompositeAlphaMode alphaMode) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, alphaMode);
}
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
FWGPU_LOGI << "WebGPU surface capabilities:";
FWGPU_LOGI << " surface usages: " << webGPUPrintableToString(capabilities.usages);
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):";
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
FWGPU_LOGI << " surface usages: " << capabilities.usages << utils::io::endl;
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
[](wgpu::TextureFormat const format) {
FWGPU_LOGI << " " << webGPUPrintableToString(format);
FWGPU_LOGI << " " << format << utils::io::endl;
});
}
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount << "):";
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
<< "):" << utils::io::endl;
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
std::for_each(capabilities.presentModes,
capabilities.presentModes + capabilities.presentModeCount,
[](wgpu::PresentMode const presentMode) {
FWGPU_LOGI << " " << webGPUPrintableToString(presentMode);
FWGPU_LOGI << " " << presentMode << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount << "):";
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
<< "):" << utils::io::endl;
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
std::for_each(capabilities.alphaModes,
capabilities.alphaModes + capabilities.alphaModeCount,
[](wgpu::CompositeAlphaMode const alphaMode) {
FWGPU_LOGI << " " << webGPUPrintableToString(alphaMode);
FWGPU_LOGI << " " << alphaMode << utils::io::endl;
});
}
}
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
wgpu::TextureFormat depthFormat) {
FWGPU_LOGI << "WebGPU surface configuration:";
FWGPU_LOGI << " surface format: " << webGPUPrintableToString(config.format);
FWGPU_LOGI << " surface usage: " << webGPUPrintableToString(config.usage);
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):";
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
FWGPU_LOGI << " surface format: " << config.format << utils::io::endl;
FWGPU_LOGI << " surface usage: " << config.usage << utils::io::endl;
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
[](wgpu::TextureFormat const viewFormat) {
FWGPU_LOGI << " " << webGPUPrintableToString(viewFormat);
FWGPU_LOGI << " " << viewFormat << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha mode: " << webGPUPrintableToString(config.alphaMode);
FWGPU_LOGI << " surface width: " << config.width;
FWGPU_LOGI << " surface height: " << config.height;
FWGPU_LOGI << " surface present mode: " << webGPUPrintableToString(config.presentMode);
FWGPU_LOGI << "WebGPU selected depth format: " << webGPUPrintableToString(depthFormat);
FWGPU_LOGI << " surface alpha mode: " << config.alphaMode << utils::io::endl;
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
FWGPU_LOGI << " surface present mode: " << config.presentMode << utils::io::endl;
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormat << utils::io::endl;
}
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
@@ -169,7 +189,8 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
"historically used these. Premultiplied alpha composite mode for "
"transparency is being selected as a fallback, but may not work as expected.";
"transparency is being selected as a fallback, but may not work as expected."
<< utils::io::endl;
return wgpu::CompositeAlphaMode::Premultiplied;
} else {
FILAMENT_CHECK_POSTCONDITION(unpremultipliedAvailable)
@@ -179,7 +200,8 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
"historically used these. Unpremultiplied alpha composite mode for "
"transparency is being selected as a fallback "
"(premulitipled is not available either), but may not work as expected.";
"(premulitipled is not available either), but may not work as expected."
<< utils::io::endl;
return wgpu::CompositeAlphaMode::Unpremultiplied;
}
}
@@ -239,6 +261,8 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
}// 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),
@@ -250,7 +274,7 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const&
mDepthTextureView(createDepthTextureView(mDepthTexture, mDepthFormat, mNeedStencil)) {
wgpu::SurfaceCapabilities capabilities = {};
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
FWGPU_LOGW << "Failed to get WebGPU surface capabilities";
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
} else {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceCapabilitiesDetails(capabilities);
@@ -273,7 +297,8 @@ void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) {
mConfig.width = currentSurfaceSize.width;
mConfig.height = currentSurfaceSize.height;
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height;
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height
<< utils::io::endl;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig, mDepthFormat);
#endif
+141 -152
View File
@@ -25,48 +25,16 @@
#include <utils/BitmaskEnum.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <webgpu/webgpu_cpp_print.h>
#include <sstream>
#endif
#include <cstdint>
namespace filament::backend {
namespace {
[[nodiscard]] constexpr wgpu::StringView getUserTextureLabel(const SamplerType target) {
// TODO will be helpful to get more useful info than this
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_map_array_user_texture";
}
}
[[nodiscard]] constexpr wgpu::StringView getUserTextureViewLabel(const SamplerType target) {
// TODO will be helpful to get more useful info than this
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_map_array_user_texture_view";
}
}
/**
* @param format texture format to potentially be used for storage binding
* @return true if the format is compatible with wgpu::TextureUsage::StorageBinding
*/
[[nodiscard]] constexpr bool isFormatStorageCompatible(const wgpu::TextureFormat format) {
[[nodiscard]] bool IsFormatStorageCompatible(wgpu::TextureFormat format) {
switch (format) {
// List of formats that support storage binding
case wgpu::TextureFormat::R32Float:
@@ -92,78 +60,98 @@ namespace {
return false;
}
}
/**
* @param viewFormat potential view format to a texture
* @return an underlying texture format that is storage binding compatible for which this view
* texture format can be used. If no such format exists, Undefined is returned.
* If the view texture is already storage binding compatible then the view texture itself
* is returned.
*/
[[nodiscard]] constexpr wgpu::TextureFormat storageBindingCompatibleFormatForViewFormat(
const wgpu::TextureFormat viewFormat) {
switch (viewFormat) {
case wgpu::TextureFormat::RGBA8UnormSrgb: return wgpu::TextureFormat::RGBA8Unorm;
case wgpu::TextureFormat::BGRA8UnormSrgb: return wgpu::TextureFormat::BGRA8Unorm;
case wgpu::TextureFormat::BC1RGBAUnormSrgb: return wgpu::TextureFormat::BC1RGBAUnorm;
case wgpu::TextureFormat::BC2RGBAUnormSrgb: return wgpu::TextureFormat::BC2RGBAUnorm;
case wgpu::TextureFormat::BC3RGBAUnormSrgb: return wgpu::TextureFormat::BC3RGBAUnorm;
case wgpu::TextureFormat::BC7RGBAUnormSrgb: return wgpu::TextureFormat::BC7RGBAUnorm;
case wgpu::TextureFormat::ETC2RGB8UnormSrgb: return wgpu::TextureFormat::ETC2RGB8Unorm;
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: return wgpu::TextureFormat::ETC2RGB8A1Unorm;
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb: return wgpu::TextureFormat::ETC2RGBA8Unorm;
case wgpu::TextureFormat::ASTC4x4UnormSrgb: return wgpu::TextureFormat::ASTC4x4Unorm;
case wgpu::TextureFormat::ASTC5x4UnormSrgb: return wgpu::TextureFormat::ASTC5x4Unorm;
case wgpu::TextureFormat::ASTC5x5UnormSrgb: return wgpu::TextureFormat::ASTC5x5Unorm;
case wgpu::TextureFormat::ASTC6x5UnormSrgb: return wgpu::TextureFormat::ASTC6x5Unorm;
case wgpu::TextureFormat::ASTC6x6UnormSrgb: return wgpu::TextureFormat::ASTC6x6Unorm;
case wgpu::TextureFormat::ASTC8x5UnormSrgb: return wgpu::TextureFormat::ASTC8x5Unorm;
case wgpu::TextureFormat::ASTC8x6UnormSrgb: return wgpu::TextureFormat::ASTC8x6Unorm;
case wgpu::TextureFormat::ASTC8x8UnormSrgb: return wgpu::TextureFormat::ASTC8x8Unorm;
case wgpu::TextureFormat::ASTC10x5UnormSrgb: return wgpu::TextureFormat::ASTC10x5Unorm;
case wgpu::TextureFormat::ASTC10x6UnormSrgb: return wgpu::TextureFormat::ASTC10x6Unorm;
case wgpu::TextureFormat::ASTC10x8UnormSrgb: return wgpu::TextureFormat::ASTC10x8Unorm;
case wgpu::TextureFormat::ASTC10x10UnormSrgb: return wgpu::TextureFormat::ASTC10x10Unorm;
case wgpu::TextureFormat::ASTC12x10UnormSrgb: return wgpu::TextureFormat::ASTC12x10Unorm;
case wgpu::TextureFormat::ASTC12x12UnormSrgb: return wgpu::TextureFormat::ASTC12x12Unorm;
wgpu::TextureFormat GetStorageCompatibleFormat(wgpu::TextureFormat srgbFormat) {
switch (srgbFormat) {
case wgpu::TextureFormat::RGBA8UnormSrgb:
return wgpu::TextureFormat::RGBA8Unorm;
case wgpu::TextureFormat::BGRA8UnormSrgb:
return wgpu::TextureFormat::BGRA8Unorm;
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
return wgpu::TextureFormat::BC1RGBAUnorm;
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
return wgpu::TextureFormat::BC2RGBAUnorm;
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
return wgpu::TextureFormat::BC3RGBAUnorm;
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
return wgpu::TextureFormat::BC7RGBAUnorm;
case wgpu::TextureFormat::ETC2RGB8UnormSrgb:
return wgpu::TextureFormat::ETC2RGB8Unorm;
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb:
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb:
return wgpu::TextureFormat::ETC2RGBA8Unorm;
case wgpu::TextureFormat::ASTC4x4UnormSrgb:
return wgpu::TextureFormat::ASTC4x4Unorm;
case wgpu::TextureFormat::ASTC5x4UnormSrgb:
return wgpu::TextureFormat::ASTC5x4Unorm;
case wgpu::TextureFormat::ASTC5x5UnormSrgb:
return wgpu::TextureFormat::ASTC5x5Unorm;
case wgpu::TextureFormat::ASTC6x5UnormSrgb:
return wgpu::TextureFormat::ASTC6x5Unorm;
case wgpu::TextureFormat::ASTC6x6UnormSrgb:
return wgpu::TextureFormat::ASTC6x6Unorm;
case wgpu::TextureFormat::ASTC8x5UnormSrgb:
return wgpu::TextureFormat::ASTC8x5Unorm;
case wgpu::TextureFormat::ASTC8x6UnormSrgb:
return wgpu::TextureFormat::ASTC8x6Unorm;
case wgpu::TextureFormat::ASTC8x8UnormSrgb:
return wgpu::TextureFormat::ASTC8x8Unorm;
case wgpu::TextureFormat::ASTC10x5UnormSrgb:
return wgpu::TextureFormat::ASTC10x5Unorm;
case wgpu::TextureFormat::ASTC10x6UnormSrgb:
return wgpu::TextureFormat::ASTC10x6Unorm;
case wgpu::TextureFormat::ASTC10x8UnormSrgb:
return wgpu::TextureFormat::ASTC10x8Unorm;
case wgpu::TextureFormat::ASTC10x10UnormSrgb:
return wgpu::TextureFormat::ASTC10x10Unorm;
case wgpu::TextureFormat::ASTC12x10UnormSrgb:
return wgpu::TextureFormat::ASTC12x10Unorm;
case wgpu::TextureFormat::ASTC12x12UnormSrgb:
return wgpu::TextureFormat::ASTC12x12Unorm;
default:
if (isFormatStorageCompatible(viewFormat)) {
return viewFormat; // view format is itself already storage binding compatible
} else {
return wgpu::TextureFormat::Undefined; // no valid format in this case
}
return srgbFormat; // If not an sRGB format, return the same input
}
}
/**
* @param fUsage Filament's requested texture usage
* @param samples How many samples to use for MSAA
* @param needsComputeStorageSupport if we need to use this texture as storage binding in something
* like a compute shader
* @return The appropriate texture usage flags for the underlying texture
*/
[[nodiscard]] wgpu::TextureUsage fToWGPUTextureUsage(TextureUsage const& fUsage,
const uint8_t samples, const bool needsComputeStorageSupport) {
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
[[nodiscard]] constexpr wgpu::StringView getUserTextureLabel(const SamplerType target) {
// TODO will be helpful to get more useful info than this
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_map_array_user_texture";
}
}
// if needsComputeStorageSupport we need to read and write to the texture in a shader and thus
// require CopySrc, CopyDst, TextureBinding, & StorageBinding
[[nodiscard]] constexpr wgpu::StringView getUserTextureViewLabel(const SamplerType target) {
// TODO will be helpful to get more useful info than this
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_map_array_user_texture_view";
}
}
[[nodiscard]] wgpu::TextureUsage fToWGPUTextureUsage(TextureUsage const& fUsage,
const uint8_t samples) {
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 (needsComputeStorageSupport || any(TextureUsage::BLIT_SRC & fUsage)) {
if (any(TextureUsage::BLIT_SRC & fUsage)) {
retUsage |= wgpu::TextureUsage::CopySrc;
}
if (needsComputeStorageSupport ||
any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
if (any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
retUsage |= wgpu::TextureUsage::CopyDst;
}
if (needsComputeStorageSupport || any(TextureUsage::SAMPLEABLE & fUsage)) {
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
retUsage |= wgpu::TextureUsage::TextureBinding;
}
if (needsComputeStorageSupport) {
retUsage |= wgpu::TextureUsage::StorageBinding;
}
// 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.
@@ -190,6 +178,7 @@ namespace {
retUsage |= wgpu::TextureUsage::TransientAttachment;
}
// NOTE: Unused wgpu flags:
// StorageBinding
// StorageAttachment
// NOTE: Unused Filament flags:
@@ -294,19 +283,16 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
const uint32_t height, const uint32_t depth, const TextureUsage usage,
wgpu::Device const& device) noexcept
: HwTexture{ samplerType, levels, samples, width, height, depth, format, usage },
mViewFormat{ fToWGPUTextureFormat(format) },
mSupportsMultipleMipLevels{ levels > 1 &&
supportsMultipleMipLevelsViaStorageBinding(mViewFormat) },
mWebGPUFormat{ mSupportsMultipleMipLevels
? storageBindingCompatibleFormatForViewFormat(mViewFormat)
: mViewFormat },
mWebGPUFormat{ fToWGPUTextureFormat(format) },
mAspect{ fToWGPUTextureViewAspect(usage, format) },
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples, mSupportsMultipleMipLevels) },
mViewUsage{ fToWGPUTextureUsage(usage, samples, false) },
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples) },
mBlockWidth{ filament::backend::getBlockWidth(format) },
mBlockHeight{ filament::backend::getBlockHeight(format) },
mDefaultMipLevel{ 0 },
mDefaultBaseArrayLayer{ 0 } {
mBlockHeight{ filament::backend::getBlockHeight(format) } {
mCompatFormat = GetStorageCompatibleFormat(mWebGPUFormat);
if(levels > 1 && (mWebGPUUsage & wgpu::TextureUsage::TextureBinding) && (mWebGPUUsage & wgpu::TextureUsage::CopyDst) && IsFormatStorageCompatible(mCompatFormat)){
mWebGPUUsage |= wgpu::TextureUsage::StorageBinding;
}
assert_invariant(
samples == 1 ||
samples == 4 &&
@@ -314,7 +300,7 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
"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");
const wgpu::TextureDescriptor textureDescriptor{
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(samplerType),
.usage = mWebGPUUsage,
.dimension = toWebGPUTextureDimension(samplerType),
@@ -322,9 +308,21 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
.format = mWebGPUFormat,
.mipLevelCount = levels,
.sampleCount = samples,
.viewFormatCount = 1,
.viewFormats = &mViewFormat,
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from
// Filament
.viewFormatCount = 0,
.viewFormats = nullptr,
};
//If our main format is not srgb, make the srgb a view into a non-srgb format
if(mCompatFormat != mWebGPUFormat){
textureDescriptor.viewFormatCount = 1;
std::swap(mCompatFormat, mWebGPUFormat);
textureDescriptor.format = mWebGPUFormat;
textureDescriptor.viewFormats = &mCompatFormat;
mWebGPUUsage|=wgpu::TextureUsage::StorageBinding;
textureDescriptor.usage = mWebGPUUsage;
}
mArrayLayerCount = toArrayLayerCount(samplerType, textureDescriptor.size.depthOrArrayLayers);
assert_invariant(textureDescriptor.format != wgpu::TextureFormat::Undefined &&
"Could not find appropriate WebGPU format");
@@ -335,40 +333,23 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
mArrayLayerCount, samplerType);
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView)
<< "Failed to create default texture view for " << textureDescriptor.label;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
if (mViewFormat != mWebGPUFormat) {
std::stringstream viewFormatStream;
viewFormatStream << mViewFormat;
std::stringstream textureFormatStream;
textureFormatStream << mWebGPUFormat;
FWGPU_LOGD << "Texture '" << textureDescriptor.label << "' has view format "
<< viewFormatStream.str() << " and texture format " << textureFormatStream.str();
}
#endif
}
WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
const uint8_t levelCount) noexcept
: HwTexture{ src->target, levelCount, src->samples, src->width, src->height, src->depth,
src->format, src->usage },
mViewFormat{ src->mViewFormat },
mSupportsMultipleMipLevels{ src->mSupportsMultipleMipLevels },
mTexture{ src->mTexture },
mWebGPUFormat{ src->mWebGPUFormat },
mAspect{ src->mAspect },
mWebGPUUsage{ src->mWebGPUUsage },
mViewUsage{ src->mViewUsage },
mArrayLayerCount{ src->mArrayLayerCount },
mBlockWidth{ src->mBlockWidth },
mBlockHeight{ src->mBlockHeight },
mArrayLayerCount{ src->mArrayLayerCount },
mTexture{ src->mTexture },
mDefaultTextureView{ makeTextureView(baseLevel, levelCount, 0, src->mArrayLayerCount,
src->target) },
mDefaultMipLevel{ baseLevel },
mDefaultBaseArrayLayer{ src->mArrayLayerCount },
mDefaultTextureView{ makeTextureView(mDefaultMipLevel, levelCount, 0, mDefaultBaseArrayLayer,
src->target) } {}
bool WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(const wgpu::TextureFormat format) {
return storageBindingCompatibleFormatForViewFormat(format) != wgpu::TextureFormat::Undefined;
}
mDefaultBaseArrayLayer{ src->mArrayLayerCount } {}
wgpu::TextureView WebGPUTexture::getOrMakeTextureView(const uint8_t mipLevel,
const uint32_t arrayLayer) {
@@ -468,43 +449,51 @@ wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFo
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and discard the alpha and lower precision.
FWGPU_LOGW << "Requested Filament texture format RGB565 but getting "
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)"
<< utils::io::endl;
return wgpu::TextureFormat::Undefined;
case TextureFormat::RGB9_E5: return wgpu::TextureFormat::RGB9E5Ufloat;
case TextureFormat::RGB5_A1:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and handle the packing/unpacking in shaders.
FWGPU_LOGW << "Requested Filament texture format RGB5_A1 but getting "
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)"
<< utils::io::endl;
return wgpu::TextureFormat::Undefined;
case TextureFormat::RGBA4:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and handle the packing/unpacking in shaders.
FWGPU_LOGW << "Requested Filament texture format RGBA4 but getting "
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)"
<< utils::io::endl;
return wgpu::TextureFormat::Undefined;
case TextureFormat::RGB8:
FWGPU_LOGW << "Requested Filament texture format RGB8 but getting "
"wgpu::TextureFormat::RGBA8Unorm (no direct sRGB equivalent in wgpu "
"without alpha)";
"without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA8Unorm;
case TextureFormat::SRGB8:
FWGPU_LOGW << "Requested Filament texture format SRGB8 but getting "
"wgpu::TextureFormat::RGBA8UnormSrgb (no direct sRGB equivalent in wgpu "
"without alpha)";
"without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA8UnormSrgb;
case TextureFormat::RGB8_SNORM:
FWGPU_LOGW
<< "Requested Filament texture format RGB8_SNORM but getting "
"wgpu::TextureFormat::RGBA8Snorm (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA8Snorm (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA8Snorm;
case TextureFormat::RGB8UI:
FWGPU_LOGW << "Requested Filament texture format RGB8UI but getting "
"wgpu::TextureFormat::RGBA8Uint (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA8Uint (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA8Uint;
case TextureFormat::RGB8I:
FWGPU_LOGW << "Requested Filament texture format RGB8I but getting "
"wgpu::TextureFormat::RGBA8Sint (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA8Sint (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA8Sint;
case TextureFormat::R11F_G11F_B10F: return wgpu::TextureFormat::RG11B10Ufloat;
case TextureFormat::UNUSED: return wgpu::TextureFormat::Undefined;
@@ -512,32 +501,38 @@ wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFo
case TextureFormat::RGB16F:
FWGPU_LOGW
<< "Requested Filament texture format RGB16F but getting "
"wgpu::TextureFormat::RGBA16Float (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA16Float (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA16Float;
case TextureFormat::RGB16UI:
FWGPU_LOGW
<< "Requested Filament texture format RGB16UI but getting "
"wgpu::TextureFormat::RGBA16Uint (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA16Uint (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA16Uint;
case TextureFormat::RGB16I:
FWGPU_LOGW
<< "Requested Filament texture format RGB16I but getting "
"wgpu::TextureFormat::RGBA16Sint (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA16Sint (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA16Sint;
case TextureFormat::RGB32F:
FWGPU_LOGW
<< "Requested Filament texture format RGB32F but getting "
"wgpu::TextureFormat::RGBA32Float (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA32Float (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA32Float;
case TextureFormat::RGB32UI:
FWGPU_LOGW
<< "Requested Filament texture format RGB32UI but getting "
"wgpu::TextureFormat::RGBA32Uint (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA32Uint (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA32Uint;
case TextureFormat::RGB32I:
FWGPU_LOGW
<< "Requested Filament texture format RGB32I but getting "
"wgpu::TextureFormat::RGBA32Sint (no direct mapping in wgpu without alpha)";
"wgpu::TextureFormat::RGBA32Sint (no direct mapping in wgpu without alpha)"
<< utils::io::endl;
return wgpu::TextureFormat::RGBA32Sint;
case TextureFormat::DXT1_RGB: return wgpu::TextureFormat::BC1RGBAUnorm;
case TextureFormat::DXT1_RGBA: return wgpu::TextureFormat::BC1RGBAUnorm;
@@ -553,23 +548,17 @@ wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFo
wgpu::TextureView WebGPUTexture::makeTextureView(const uint8_t& baseLevel,
const uint8_t& levelCount, const uint32_t& baseArrayLayer, const uint32_t& arrayLayerCount,
const SamplerType samplerType) const noexcept {
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
if (baseLevel > 0 && !mSupportsMultipleMipLevels) {
FWGPU_LOGW << "Trying to make a texture view into a level ("
<< static_cast<uint32_t>(baseLevel)
<< ") for which we cannot generate mip levels.";
}
#endif
const wgpu::TextureViewDescriptor textureViewDescriptor{
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = getUserTextureViewLabel(target),
.format = mViewFormat,
.format = mCompatFormat,
.dimension = toWebGPUTextureViewDimension(samplerType),
.baseMipLevel = baseLevel,
.mipLevelCount = levelCount,
.baseArrayLayer = baseArrayLayer,
.arrayLayerCount = arrayLayerCount,
.aspect = mAspect,
.usage = mViewUsage };
.usage = mWebGPUUsage };
textureViewDescriptor.usage &= (~wgpu::TextureUsage::StorageBinding);
wgpu::TextureView textureView = mTexture.CreateView(&textureViewDescriptor);
FILAMENT_CHECK_POSTCONDITION(textureView)
<< "Failed to create texture view " << textureViewDescriptor.label;
+8 -24
View File
@@ -47,46 +47,30 @@ public:
[[nodiscard]] wgpu::TextureView getOrMakeTextureView(uint8_t mipLevel, uint32_t arrayLayer);
[[nodiscard]] wgpu::TextureFormat getViewFormat() const { return mViewFormat; }
[[nodiscard]] wgpu::TextureFormat getWebGPUFormat() const { return mWebGPUFormat; }
[[nodiscard]] uint32_t getArrayLayerCount() const { return mArrayLayerCount; }
[[nodiscard]] bool supportsMultipleMipLevels() const { return mSupportsMultipleMipLevels; }
[[nodiscard]] static wgpu::TextureFormat fToWGPUTextureFormat(
filament::backend::TextureFormat const& fFormat);
/**
* @param format a required texture format (can be a view to a different underlying texture
* format)
* @return true if multiple mip levels can be generated via a compute shader + storage binding
* on the texture, false otherwise.
*/
[[nodiscard]] static bool supportsMultipleMipLevelsViaStorageBinding(
wgpu::TextureFormat format);
private:
// the texture view's format, which may differ from the underlying texture's format itself,
// an example of when this is needed is when certain underlying shaders don't support
// certain formats (e.g. compute shaders), but we need a view to that format elsewhere
// (e.g. another shader or render target)
wgpu::TextureFormat mViewFormat = wgpu::TextureFormat::Undefined;
// this is true IFF levels > 1 AND we can actually generate mipmaps for the texture
bool mSupportsMultipleMipLevels = false;
// 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;
// format is inherited from HwTexture. This naming is to distinguish it from Filament's format
// this is the underlying texture's format, not necessarily the "view" of it... see mViewFormat
wgpu::TextureFormat mWebGPUFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureFormat mCompatFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
// usage is inherited from HwTexture. This naming is to distinguish it from Filament's usage
wgpu::TextureUsage mWebGPUUsage = wgpu::TextureUsage::None;
wgpu::TextureUsage mViewUsage = wgpu::TextureUsage::None;
uint32_t mArrayLayerCount = 1;
size_t mBlockWidth = 0;
size_t mBlockHeight = 0;
uint32_t mArrayLayerCount = 1;
wgpu::Texture mTexture = nullptr;
wgpu::TextureView mDefaultTextureView = nullptr;
uint32_t mDefaultMipLevel = 0;
uint32_t mDefaultBaseArrayLayer = 0;
wgpu::TextureView mDefaultTextureView = nullptr;
[[nodiscard]] wgpu::TextureView makeTextureView(const uint8_t& baseLevel,
const uint8_t& levelCount, const uint32_t& baseArrayLayer,
@@ -22,6 +22,7 @@
#include <backend/DriverEnums.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
@@ -56,22 +57,26 @@ namespace {
case ElementType::BYTE3:
FWGPU_LOGW
<< "Requested Filament vertex format BYTE3 (normalized) but getting "
"wgpu::VertexFormat::Snorm8x4 (no direct mapping in wgpu for x3 byte)";
"wgpu::VertexFormat::Snorm8x4 (no direct mapping in wgpu for x3 byte)"
<< utils::io::endl;
return VertexFormat::Snorm8x4; // NOT MINSPEC
case ElementType::UBYTE3:
FWGPU_LOGW
<< "Requested Filament vertex format UBYTE3 (normalized) but getting "
"wgpu::VertexFormat::Unorm8x4 (no direct mapping in wgpu for x3 byte)";
"wgpu::VertexFormat::Unorm8x4 (no direct mapping in wgpu for x3 byte)"
<< utils::io::endl;
return VertexFormat::Unorm8x4; // NOT MINSPEC
case ElementType::SHORT3:
FWGPU_LOGW << "Requested Filament vertex format SHORT3 (normalized) but getting "
"wgpu::VertexFormat::Snorm16x4 (no direct mapping in wgpu for x3 "
"half/short)";
"half/short)"
<< utils::io::endl;
return VertexFormat::Snorm16x4; // NOT MINSPEC
case ElementType::USHORT3:
FWGPU_LOGW << "Requested Filament vertex format USHORT3 (normalized) but getting "
"wgpu::VertexFormat::Unorm16x4 (no direct mapping in wgpu for x3 "
"half/short)";
"half/short)"
<< utils::io::endl;
return VertexFormat::Unorm16x4; // NOT MINSPEC
// Four Component Types
case ElementType::BYTE4: return VertexFormat::Snorm8x4;
@@ -92,22 +97,26 @@ namespace {
case ElementType::BYTE:
if (integer) return VertexFormat::Sint8;
FWGPU_LOGW << "Requested Filament vertex format BYTE (float) but getting "
"wgpu::VertexFormat::Float16 (no direct mapping in wgpu for 8 bit float)";
"wgpu::VertexFormat::Float16 (no direct mapping in wgpu for 8 bit float)"
<< utils::io::endl;
return wgpu::VertexFormat::Float16;
case ElementType::UBYTE:
if (integer) return VertexFormat::Uint8;
FWGPU_LOGW << "Requested Filament vertex format UBYTE (float) but getting "
"wgpu::VertexFormat::Float16 (no direct mapping in wgpu for 8 bit float)";
"wgpu::VertexFormat::Float16 (no direct mapping in wgpu for 8 bit float)"
<< utils::io::endl;
return VertexFormat::Float16;
case ElementType::SHORT:
if (integer) return VertexFormat::Sint16;
FWGPU_LOGW << "Requested Filament vertex format SHORT (float) and getting "
"wgpu::VertexFormat::Float16 (potential loss of precision?)";
"wgpu::VertexFormat::Float16 (potential loss of precision?)"
<< utils::io::endl;
return VertexFormat ::Float16;
case ElementType::USHORT:
if (integer) return VertexFormat::Uint16;
FWGPU_LOGW << "Requested Filament vertex format USHORT (float) and getting "
"wgpu::VertexFormat::Float16 (potential loss of precision?)";
"wgpu::VertexFormat::Float16 (potential loss of precision?)"
<< utils::io::endl;
return VertexFormat::Float16;
case ElementType::HALF: return VertexFormat::Float16;
case ElementType::INT: return VertexFormat::Sint32;
@@ -118,49 +127,58 @@ namespace {
if (integer) return VertexFormat::Sint8x2;
FWGPU_LOGW
<< "Requested Filament vertex format BYTE2 (float) but getting "
"wgpu::VertexFormat::Float16x2 (no direct mapping in wgpu for 8 bit float)";
"wgpu::VertexFormat::Float16x2 (no direct mapping in wgpu for 8 bit float)"
<< utils::io::endl;
return VertexFormat::Float16x2;
case ElementType::UBYTE2:
if (integer) return VertexFormat::Uint8x2;
FWGPU_LOGW
<< "Requested Filament vertex format UBYTE2 (float) but getting "
"wgpu::VertexFormat::Float16x2 (no direct mapping in wgpu for 8 bit float)";
"wgpu::VertexFormat::Float16x2 (no direct mapping in wgpu for 8 bit float)"
<< utils::io::endl;
return VertexFormat::Float16x2;
case ElementType::SHORT2:
if (integer) return VertexFormat::Sint16x2;
FWGPU_LOGW << "Requested Filament vertex format SHORT2 (float) but getting "
"wgpu::VertexFormat::Float16x2 (potential loss of precision?)";
"wgpu::VertexFormat::Float16x2 (potential loss of precision?)"
<< utils::io::endl;
return VertexFormat::Float16x2;
case ElementType::USHORT2:
if (integer) return VertexFormat::Uint16x2;
FWGPU_LOGW << "Requested Filament vertex format USHORT2 (float) but getting "
"wgpu::VertexFormat::Float16x2 (potential loss of precision?)";
"wgpu::VertexFormat::Float16x2 (potential loss of precision?)"
<< utils::io::endl;
return VertexFormat::Float16x2;
case ElementType::HALF2: return VertexFormat::Float16x2;
case ElementType::FLOAT2: return VertexFormat::Float32x2;
// Three Component Types
case ElementType::BYTE3:
FWGPU_LOGW << "Requested Filament vertex format BYTE3 but getting "
"wgpu::VertexFormat::Sint8x4 (no direct mapping in wgpu for x3 byte)";
"wgpu::VertexFormat::Sint8x4 (no direct mapping in wgpu for x3 byte)"
<< utils::io::endl;
return VertexFormat::Sint8x4; // NOT MINSPEC
case ElementType::UBYTE3:
FWGPU_LOGW << "Requested Filament vertex format UBYTE3 but getting "
"wgpu::VertexFormat::Uint8x4 (no direct mapping in wgpu for x3 byte)";
"wgpu::VertexFormat::Uint8x4 (no direct mapping in wgpu for x3 byte)"
<< utils::io::endl;
return VertexFormat::Uint8x4; // NOT MINSPEC
case ElementType::SHORT3:
FWGPU_LOGW
<< "Requested Filament vertex format SHORT3 but getting "
"wgpu::VertexFormat::Sint16x4 (no direct mapping in wgpu for x3 half/short)";
"wgpu::VertexFormat::Sint16x4 (no direct mapping in wgpu for x3 half/short)"
<< utils::io::endl;
return VertexFormat::Sint16x4; // NOT MINSPEC
case ElementType::USHORT3:
FWGPU_LOGW
<< "Requested Filament vertex format USHORT3 but getting "
"wgpu::VertexFormat::Uint16x4 (no direct mapping in wgpu for x3 half/short)";
"wgpu::VertexFormat::Uint16x4 (no direct mapping in wgpu for x3 half/short)"
<< utils::io::endl;
return VertexFormat::Uint16x4; // NOT MINSPEC
case ElementType::HALF3:
FWGPU_LOGW
<< "Requested Filament vertex format HALF3 but getting "
"wgpu::VertexFormat::Float16x4 (no direct mapping in wgpu for x3 half/short)";
"wgpu::VertexFormat::Float16x4 (no direct mapping in wgpu for x3 half/short)"
<< utils::io::endl;
return VertexFormat::Float16x4; // NOT MINSPEC
case ElementType::FLOAT3: return VertexFormat::Float32x3;
// Four Component Types
@@ -168,23 +186,27 @@ namespace {
if (integer) return VertexFormat::Sint8x4;
FWGPU_LOGW
<< "Requested Filament vertex format BYTE4 (float) but getting "
"wgpu::VertexFormat::Float16x4 (no direct mapping in wgpu for 8 bit float)";
"wgpu::VertexFormat::Float16x4 (no direct mapping in wgpu for 8 bit float)"
<< utils::io::endl;
return VertexFormat::Float16x4;
case ElementType::UBYTE4:
if (integer) return VertexFormat::Uint8x4;
FWGPU_LOGW
<< "Requested Filament vertex format UBYTE4 (float) but getting "
"wgpu::VertexFormat::Float16x4 (no direct mapping in wgpu for 8 bit float)";
"wgpu::VertexFormat::Float16x4 (no direct mapping in wgpu for 8 bit float)"
<< utils::io::endl;
return VertexFormat::Float16x4;
case ElementType::SHORT4:
if (integer) return VertexFormat::Sint16x4;
FWGPU_LOGW << "Requested Filament vertex format SHORT4 (float) but getting "
"wgpu::VertexFormat::Float16x4 (potential loss of precision?)";
"wgpu::VertexFormat::Float16x4 (potential loss of precision?)"
<< utils::io::endl;
return VertexFormat::Float16x4;
case ElementType::USHORT4:
if (integer) return VertexFormat::Uint16x4;
FWGPU_LOGW << "Requested Filament vertex format USHORT4 (float) but getting "
"wgpu::VertexFormat::Float16x4 (potential loss of precision?)";
"wgpu::VertexFormat::Float16x4 (potential loss of precision?)"
<< utils::io::endl;
return VertexFormat::Float16x4;
case ElementType::HALF4: return VertexFormat::Float16x4;
case ElementType::FLOAT4: return VertexFormat::Float32x4;
@@ -24,6 +24,7 @@
#include <backend/Platform.h>
#include <utils/Hash.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
@@ -38,7 +39,6 @@
#include <utility>
#include <vector>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <string>
#include <string_view>
#include <variant>
#endif
@@ -53,9 +53,6 @@ namespace filament::backend {
namespace {
// The SPD Algorithm can make use of up to 12 storage texture attachments
constexpr uint32_t MAX_MIPMAP_STORAGE_TEXTURES_PER_STAGE = 12u;
constexpr std::array REQUIRED_FEATURES = { wgpu::FeatureName::TransientAttachments,
/*To make filtering assumptions like we want while waiting for Filament to provide that info,
float 32 needs to be filterable*/
@@ -90,6 +87,7 @@ constexpr wgpu::Limits REQUIRED_LIMITS = {
.maxBindingsPerBindGroup = filament::backend::MAX_DESCRIPTOR_COUNT,
.maxSamplersPerShaderStage = 16, // TODO should be set to filament::backend::MAX_SAMPLER_COUNT,
.maxStorageBuffersPerShaderStage = filament::backend::MAX_SSBO_COUNT,
.maxStorageTexturesPerShaderStage = 8,
.maxVertexBuffers = 8, // TODO should be set to filament::backend::MAX_VERTEX_BUFFER_COUNT,
.maxVertexAttributes = filament::backend::MAX_VERTEX_ATTRIBUTE_COUNT,
};
@@ -120,6 +118,28 @@ constexpr void forEachLimitToValidate(std::function<bool(LimitToValidate const)>
}
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::WGSLLanguageFeatureName languageFeatureName) noexcept {
return streamInsertWebGPUPrintable(out, languageFeatureName);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::FeatureName featureName) noexcept {
return streamInsertWebGPUPrintable(out, featureName);
}
#endif
template<typename STREAM_TYPE>
STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::RequestAdapterOptions const& options) noexcept {
return streamInsertRequestAdapterOptions(out, options);
}
template<class STREAM_TYPE>
STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::AdapterInfo const& info) noexcept {
return streamInsertRequestAdapterInfo(out, info);
}
constexpr bool isDefined(uint32_t limit) { return limit < wgpu::kLimitU32Undefined; }
[[maybe_unused]] constexpr bool isDefined(uint64_t limit) {
@@ -230,17 +250,18 @@ void assertLimitsAreExpressedInRequirementsStruct() {
void printInstanceDetails(wgpu::Instance const& instance) {
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features";
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
<< utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
<< supportedWGSLLanguageFeatures.featureCount << "):";
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
supportedWGSLLanguageFeatures.features != nullptr) {
std::for_each(supportedWGSLLanguageFeatures.features,
supportedWGSLLanguageFeatures.features +
supportedWGSLLanguageFeatures.featureCount,
[](wgpu::WGSLLanguageFeatureName const featureName) {
FWGPU_LOGI << " " << webGPUPrintableToString(featureName);
FWGPU_LOGI << " " << featureName << utils::io::endl;
});
}
}
@@ -252,7 +273,7 @@ void printInstanceDetails(wgpu::Instance const& instance) {
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
#if defined(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING)
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << "setting on toggle enable_immediate_error_handling";
FWGPU_LOGI << "setting on toggle enable_immediate_error_handling" << utils::io::endl;
#endif
/**
* Have the un-captured error callback invoked immediately when an error occurs, rather than
@@ -280,22 +301,23 @@ void printInstanceDetails(wgpu::Instance const& instance) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
static constexpr std::string_view indent = " ";
FWGPU_LOGI << " " << name.data() << ": ";
bool undefined = true;
if (std::holds_alternative<uint32_t>(value)) {
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
undefined = false;
FWGPU_LOGI << indent << name.data() << ": " << std::get<uint32_t>(value);
FWGPU_LOGI << std::get<uint32_t>(value);
}
} else if (std::holds_alternative<uint64_t>(value)) {
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
undefined = false;
FWGPU_LOGI << indent << name.data() << ": " << std::get<uint64_t>(value);
FWGPU_LOGI << std::get<uint64_t>(value);
}
}
if (undefined) {
FWGPU_LOGI << indent << name.data() << ": UNDEFINED";
FWGPU_LOGI << "UNDEFINED";
}
FWGPU_LOGI << utils::io::endl;
}
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
@@ -373,32 +395,32 @@ struct AdapterDetails final {
}
};
[[nodiscard]] std::string toString(AdapterDetails const& details) {
std::stringstream out;
out << adapterInfoToString(details.info)
template<class STREAM_TYPE>
STREAM_TYPE& operator<<(STREAM_TYPE& out, AdapterDetails const& details) noexcept {
out << details.info
<< " power preference " << powerPreferenceToString(details.powerPreference);
return out.str();
return out;
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printAdapterDetails(AdapterDetails const& details) {
FWGPU_LOGI << "Selected WebGPU adapter info: " << toString(details);
FWGPU_LOGI << "Selected WebGPU adapter info: " << details << utils::io::endl;
wgpu::SupportedFeatures supportedFeatures{};
details.adapter.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
<< "):";
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const featureName) {
FWGPU_LOGI << " " << webGPUPrintableToString(featureName);
FWGPU_LOGI << " " << featureName << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!details.adapter.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits";
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU adapter supported limits:";
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
@@ -424,24 +446,24 @@ struct AdapterDetailsHash final {
return details.adapter.HasFeature(featureName);
})) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << "WebGPU adapter " << toString(details)
<< " does not have the minimum required features.";
FWGPU_LOGI << "Missing required feature(s): ";
FWGPU_LOGI << "WebGPU adapter " << details
<< " does not have the minimum required features." << utils::io::endl;
FWGPU_LOGI << " Missing required feature(s): ";
for (wgpu::FeatureName const& requiredFeature: REQUIRED_FEATURES) {
if (!details.adapter.HasFeature(requiredFeature)) {
FWGPU_LOGI << webGPUPrintableToString(requiredFeature);
FWGPU_LOGI << requiredFeature << " ";
}
}
FWGPU_LOGI << "";
FWGPU_LOGI << utils::io::endl;
#endif
return false;
}
wgpu::Limits supportedLimits {};
FILAMENT_CHECK_POSTCONDITION(details.adapter.GetLimits(&supportedLimits))
<< "Failed to get limits for WebGPU adapter: " << toString(details);
<< "Failed to get limits for WebGPU adapter: " << details;
if (!satisfiesLimits(supportedLimits)) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << " (for WebGPU adapter " << toString(details) << ")";
FWGPU_LOGI << " (for WebGPU adapter " << details << ")" << utils::io::endl;
#endif
return false;
}
@@ -467,15 +489,14 @@ struct AdapterDetailsHash final {
status != wgpu::RequestAdapterStatus::CallbackCancelled)
<< "Failed to request a WebGPU adapter due to the request callback "
"being cancelled? Options: "
<< adapterOptionsToString(options) << " " << message.data;
<< options << " " << message.data;
FILAMENT_CHECK_POSTCONDITION(status != wgpu::RequestAdapterStatus::Error)
<< "Failed to request a WebGPU adapter due to an error. Options: "
<< adapterOptionsToString(options) << " Error: " << message.data;
<< options << " Error: " << message.data;
if (status == wgpu::RequestAdapterStatus::Success) {
AdapterDetails details = AdapterDetails(readyAdapter);
FILAMENT_CHECK_POSTCONDITION(readyAdapter.GetInfo(&details.info))
<< "Failed to get info for adapter (options: "
<< adapterOptionsToString(options) << ")";
<< "Failed to get info for adapter (options: " << options << ")";
const std::lock_guard<std::mutex> lock(adaptersMutex);
compatibleAdapters.emplace(std::move(details.info), details.powerPreference,
std::move(details.adapter));
@@ -490,19 +511,17 @@ struct AdapterDetailsHash final {
wgpu::Future& future = futures[i];
wgpu::WaitStatus status = instance.WaitAny(future, REQUEST_ADAPTER_TIMEOUT_NANOSECONDS);
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::TimedOut)
<< "Timed out requesting a WebGPU adapter with options "
<< adapterOptionsToString(options);
<< "Timed out requesting a WebGPU adapter with options " << options;
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::Error)
<< "Failed to request a WebGPU adapter with options "
<< adapterOptionsToString(options)
<< "Failed to request a WebGPU adapter with options " << options
<< " due to an error (as request was made synchronous)";
assert_invariant(status == wgpu::WaitStatus::Success);
}
FILAMENT_CHECK_POSTCONDITION(!compatibleAdapters.empty()) << "No WebGPU adapters found!";
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << compatibleAdapters.size() << " WebGPU adapter(s) found:";
FWGPU_LOGI << compatibleAdapters.size() << " WebGPU adapter(s) found:" << utils::io::endl;
for (auto& details: compatibleAdapters) {
FWGPU_LOGI << " WebGPU adapter: " << toString(details);
FWGPU_LOGI << " WebGPU adapter: " << details << utils::io::endl;
}
#endif
return compatibleAdapters;
@@ -564,19 +583,19 @@ void printDeviceDetails(wgpu::Device const& device) {
wgpu::SupportedFeatures supportedFeatures{};
device.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
<< "):";
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const featureName) {
FWGPU_LOGI << " " << webGPUPrintableToString(featureName);
FWGPU_LOGI << " " << featureName << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!device.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU supported device limits";
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU device supported limits:";
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
@@ -611,27 +630,14 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
deviceDescriptor.defaultQueue.label = "default_queue";
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
deviceDescriptor.requiredFeatures = enabledFeatures.data();
// It is helpful to increase maxStorageTexturesPerShaderStage if the hardware supports it, but
// not required. This allows our mipmap generation to perform as well as possible for the given
// hardware. If we have more cases of "Nice to increase" limits like this we can generalize it
// in the future.
wgpu::Limits supportedLimits{};
FILAMENT_CHECK_POSTCONDITION(
adapter.GetLimits(&supportedLimits).status == wgpu::Status::Success)
<< "Failed to get limits for WebGPU adapter";
auto limitsToRequest = REQUIRED_LIMITS;
limitsToRequest.maxStorageTexturesPerShaderStage =
std::min(MAX_MIPMAP_STORAGE_TEXTURES_PER_STAGE,
supportedLimits.maxStorageTexturesPerShaderStage);
deviceDescriptor.requiredLimits = &limitsToRequest;
deviceDescriptor.requiredLimits = &REQUIRED_LIMITS;
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
wgpu::StringView message) {
if (reason == wgpu::DeviceLostReason::Destroyed) {
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)";
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
<< utils::io::endl;
#endif
return;
}
@@ -643,7 +649,7 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
deviceDescriptor.SetUncapturedErrorCallback(
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
FWGPU_LOGE << "WebGPU device error: " << errorTypeToString(errorType) << " "
<< message.data;
<< message.data << utils::io::endl;
});
wgpu::Device device = nullptr;
wgpu::WaitStatus status = mInstance.WaitAny(
@@ -681,6 +687,7 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
"adapter should support them: %s\n",
missingFeatures, featureNamesStream.str().data());
}
wgpu::Limits supportedLimits {};
FILAMENT_CHECK_POSTCONDITION(device.GetLimits(&supportedLimits))
<< "Failed to get limits for the device?";
FILAMENT_CHECK_POSTCONDITION(satisfiesLimits(supportedLimits))
@@ -695,7 +702,8 @@ Driver* WebGPUPlatform::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
if (sharedContext) {
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
"sharedContext was provided, but it will be ignored.";
"sharedContext was provided, but it will be ignored."
<< utils::io::endl;
}
return WebGPUDriver::create(*this, driverConfig);
}
@@ -84,8 +84,6 @@ std::string ScreenshotParams::expectedFilePath() const {
}
const std::string ScreenshotParams::filePrefix() const {
// TODO(b/422804941): If there are platform specific goldens, when on those platforms append a
// unique platform identifying string to this.
return mFileName;
}
@@ -42,7 +42,6 @@ namespace test {
*/
class ScreenshotParams {
public:
// TODO(b/422804941): Add a set of environments where this test should use a different golden.
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash,
bool isSrgb = false);
-2
View File
@@ -41,8 +41,6 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
descriptors[kDescriptorSetIndex][i] = {
config.uniforms[i].name, kLayouts[i], config.uniforms[i].samplerInfo };
}
// TODO(b/422803382): When the shader language isn't GLSL dont' use the ShaderGenerator to
// compile the shader.
ShaderGenerator shaderGen(
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
BackendTest::sIsMobilePlatform, std::move(descriptors));
-2
View File
@@ -31,9 +31,7 @@ struct UniformConfig {
// All describing a shader that should be created.
struct ShaderConfig {
filament::backend::ShaderLanguage vertexLanguage;
std::string vertexShader;
filament::backend::ShaderLanguage fragmentLanguage;
std::string fragmentShader;
std::vector<UniformConfig> uniforms;
};
+7 -9
View File
@@ -215,20 +215,18 @@ Shader SharedShaders::makeShader(filament::backend::DriverApi& api, Cleanup& cle
std::optional<ShaderText> fragment;
std::optional<std::string> uniform;
if (getShaderLanguage(BackendTest::sBackend) != ShaderLanguage::GLSL) {
// TODO(b/422803382): If any shaders need backend/shader language specific shaders rather
// than transpiled versions of the GLSL shader, check here and create a shader with that
// config instead
// TODO: If any shaders need backend/shader language specific shaders rather than transpiled
// versions of the GLSL shader, check environment.
}
vertex = GetGlslVertexShader(request.mVertexType);
fragment = GetGlslFragmentShader(request.mFragmentType);
uniform = GetGlslUniform(request.mUniformType);
if (vertex.has_value() && fragment.has_value() && uniform.has_value()) {
return Shader(api, cleanup,
ShaderConfig{ .vertexLanguage = filament::backend::ShaderLanguage::ESSL3,
.vertexShader = vertex->withUniform(*uniform),
.fragmentLanguage = filament::backend::ShaderLanguage::ESSL3,
.fragmentShader = fragment->withUniform(*uniform),
.uniforms = GetUniformConfig(request.mUniformType) });
return Shader(
api, cleanup, ShaderConfig{
vertex->withUniform(*uniform), fragment->withUniform(*uniform),
GetUniformConfig(request.mUniformType)}
);
}
abort();
}
+3 -2
View File
@@ -25,7 +25,7 @@
#include <backend/Handle.h>
#include <utils/Hash.h>
#include <utils/Logger.h>
#include <utils/Log.h>
#include <fstream>
#include <string>
@@ -137,7 +137,8 @@ TEST_F(BackendTest, FeedbackLoops) {
// Create a RenderTarget for each miplevel.
Handle<HwRenderTarget> renderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
LOG(INFO) << "Level " << int(level) << ": " << (kTexWidth >> level) << "x" << (kTexHeight >> level);
slog.i << "Level " << int(level) << ": " <<
(kTexWidth >> level) << "x" << (kTexHeight >> level) << io::endl;
renderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {}));
}
+27
View File
@@ -139,6 +139,30 @@ TEST_F(ReadPixelsTest, ReadPixels) {
return bufferDimension;
}
void exportScreenshot(void* pixelData) const {
#ifndef FILAMENT_IOS
const size_t width = readRect.width, height = readRect.height;
LinearImage image(width, height, 4);
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
}
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
}
std::string png = std::string(testName) + ".png";
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
png.c_str());
#endif
}
void exportRawBytes(void* pixelData) const {
std::string out = std::string(testName) + ".raw";
std::ofstream outputStream(out.c_str(), std::ios::binary | std::ios::trunc);
outputStream.write((char*)pixelData, getBufferSizeBytes());
outputStream.close();
}
// The format and type for the readPixels call.
PixelDataFormat format = PixelDataFormat::RGBA;
PixelDataType type = PixelDataType::UBYTE;
@@ -311,6 +335,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
const auto* test = (const TestCase*)user;
assert_invariant(test);
test->exportScreenshot(buffer);
//test->exportRawBytes(buffer);
// Hash the contents of the buffer and check that they match.
uint32_t hash = utils::hash::murmur3((const uint32_t*)buffer, size / 4, 0);
-9
View File
@@ -317,15 +317,6 @@ public:
*/
size_t metalUploadBufferSizeBytes = 512 * 1024;
/**
* The action to take if a Drawable cannot be acquired.
*
* Each frame rendered requires a CAMetalDrawable texture, which is
* presented on-screen at the completion of each frame. These are
* limited and provided round-robin style by the system.
*/
bool metalDisablePanicOnDrawableFailure = false;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
+53 -3
View File
@@ -86,6 +86,9 @@ public:
std::is_same_v<math::mat3f, T>
>;
template<typename T>
using is_supported_constant_parameter_t = std::enable_if_t<std::is_same_v<bool, T>>;
/**
* Creates a new MaterialInstance using another MaterialInstance as a template for initialization.
* The new MaterialInstance is an instance of the same Material of the template instance and
@@ -237,13 +240,13 @@ public:
/**
* Gets the value of a parameter by name.
*
*
* Note: Only supports non-texture parameters such as numeric and math types.
*
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param nameLength Length in `char` of the name parameter.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*
*
* @see Material::hasParameter
*/
template<typename T>
@@ -261,6 +264,53 @@ public:
return getParameter<T>(name, strlen(name));
}
/**
* Set a mutable constant parameter by name.
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param nameLength Length in `char` of the name parameter.
* @param value Value of the parameter to set.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
void setConstant(const char* UTILS_NONNULL name, size_t nameLength, T const& value);
/** inline helper to provide the name as a null-terminated string literal */
template<typename T, typename = is_supported_constant_parameter_t<T>>
void setConstant(StringLiteral const name, T const& value) {
setConstant<T>(name.data, name.size, value);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
void setConstant(const char* UTILS_NONNULL name, T const& value) {
setConstant<T>(name, strlen(name), value);
}
/**
* Gets the value of a mutable constant parameter by name.
*
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
* @param nameLength Length in `char` of the name parameter.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*
* @see Material::hasConstant
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(const char* UTILS_NONNULL name, size_t nameLength) const;
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(StringLiteral const name) const {
return getConstant<T>(name.data, name.size);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
T getConstant(const char* UTILS_NONNULL name) const {
return getConstant<T>(name, strlen(name));
}
/**
* Set-up a custom scissor rectangle; by default it is disabled.
*
+9 -7
View File
@@ -22,10 +22,10 @@
#include <private/utils/Tracing.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Logger.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <algorithm>
@@ -112,11 +112,13 @@ void FrameInfoManager::beginFrame(DriverApi& driver, Config const& config, uint3
using namespace utils;
auto h = getFrameInfoHistory(1);
if (!h.empty()) {
DLOG(INFO) << frameId << ": " << h[0].frameId << " (" << frameId - h[0].frameId << ")"
<< ", Dm=" << h[0].endFrame - h[0].beginFrame
<< ", L =" << h[0].backendBeginFrame - h[0].beginFrame
<< ", Db=" << h[0].backendEndFrame - h[0].backendBeginFrame
<< ", T =" << h[0].frameTime;
slog.d << frameId << ": "
<< h[0].frameId << " (" << frameId - h[0].frameId << ")"
<< ", Dm=" << h[0].endFrame - h[0].beginFrame
<< ", L =" << h[0].backendBeginFrame - h[0].beginFrame
<< ", Db=" << h[0].backendEndFrame - h[0].backendBeginFrame
<< ", T =" << h[0].frameTime
<< io::endl;
}
}
}
+12 -8
View File
@@ -34,7 +34,7 @@
#include <utils/BinaryTreeArray.h>
#include <utils/JobSystem.h>
#include <utils/Logger.h>
#include <utils/Log.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
@@ -366,12 +366,14 @@ bool Froxelizer::update() noexcept {
uniformsNeedUpdating = true;
DLOG(INFO) << "Froxel: " << viewport.width << "x" << viewport.height << " / "
<< froxelDimension.x << "x" << froxelDimension.y << io::endl
<< "Froxel: " << froxelCountX << "x" << froxelCountY << "x" << froxelCountZ
<< " = " << (froxelCountX * froxelCountY * froxelCountZ) << " ("
<< getFroxelBufferEntryCount() - froxelCountX * froxelCountY * froxelCountZ
<< " lost)";
#ifndef NDEBUG
slog.d << "Froxel: " << viewport.width << "x" << viewport.height << " / "
<< froxelDimension.x << "x" << froxelDimension.y << io::endl
<< "Froxel: " << froxelCountX << "x" << froxelCountY << "x" << froxelCountZ
<< " = " << (froxelCountX * froxelCountY * froxelCountZ)
<< " (" << getFroxelBufferEntryCount() - froxelCountX * froxelCountY * froxelCountZ << " lost)"
<< io::endl;
#endif
mFroxelCountX = froxelCountX;
mFroxelCountY = froxelCountY;
@@ -710,7 +712,9 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
const size_t lightCount = entry.count();
if (UTILS_UNLIKELY(offset + lightCount >= RECORD_BUFFER_ENTRY_COUNT)) {
DLOG(INFO) << "out of space: " << i << ", at " << offset;
#ifndef NDEBUG
slog.d << "out of space: " << i << ", at " << offset << io::endl;
#endif
// note: instead of dropping froxels we could look for similar records we've already
// filed up.
do {
+40
View File
@@ -232,6 +232,46 @@ template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const ch
// ------------------------------------------------------------------------------------------------
template<>
inline void FMaterialInstance::setConstantImpl<bool>(std::string_view const name, bool const& value) {
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
mConstants.set(*id, value);
}
template<typename T, typename>
void MaterialInstance::setConstant(const char* name, size_t nameLength, T const& value) {
downcast(this)->setConstantImpl({ name, nameLength }, value);
}
// Explicit template instantiation of our supported types.
//
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
// forwards-compatible.
template UTILS_PUBLIC void MaterialInstance::setConstant<bool>(const char* name, size_t nameLength, bool const& v);
// ------------------------------------------------------------------------------------------------
template<>
inline bool FMaterialInstance::getConstantImpl<bool>(std::string_view const name) const {
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
return mConstants[*id];
}
template<typename T, typename>
T MaterialInstance::getConstant(const char* name, size_t nameLength) const {
return downcast(this)->getConstantImpl<T>({ name, nameLength });
}
// Explicit template instantiation of our supported types.
//
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
// forwards-compatible.
template UTILS_PUBLIC bool MaterialInstance::getConstant<bool>(const char* name, size_t nameLength) const;
// ------------------------------------------------------------------------------------------------
Material const* MaterialInstance::getMaterial() const noexcept {
return downcast(this)->getMaterial();
}
+37 -6
View File
@@ -229,6 +229,11 @@ bool MaterialParser::getConstants(FixedCapacityVector<MaterialConstant>* contain
return get<ChunkMaterialConstants>(container);
}
bool MaterialParser::getMutableConstants(
FixedCapacityVector<MaterialMutableConstant>* container) const noexcept {
return get<ChunkMaterialMutableConstants>(container);
}
bool MaterialParser::getPushConstants(CString* structVarName,
FixedCapacityVector<MaterialPushConstant>* value) const noexcept {
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialPushConstants);
@@ -481,7 +486,6 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
uint8_t fieldType = 0;
uint8_t fieldFormat = 0;
uint8_t fieldPrecision = 0;
bool fieldUnfilterable = false;
bool fieldMultisample = false;
if (!unflattener.read(&fieldName)) {
@@ -504,10 +508,6 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
return false;
}
if (!unflattener.read(&fieldUnfilterable)) {
return false;
}
if (!unflattener.read(&fieldMultisample)) {
return false;
}
@@ -517,7 +517,6 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
SamplerInterfaceBlock::Type(fieldType),
SamplerInterfaceBlock::Format(fieldFormat),
SamplerInterfaceBlock::Precision(fieldPrecision),
fieldUnfilterable,
fieldMultisample);
}
@@ -757,6 +756,38 @@ bool ChunkMaterialConstants::unflatten(Unflattener& unflattener,
return true;
}
bool ChunkMaterialMutableConstants::unflatten(Unflattener& unflattener,
FixedCapacityVector<MaterialMutableConstant>* materialConstants) {
assert_invariant(materialConstants);
// Read number of constants.
uint64_t numConstants = 0;
if (!unflattener.read(&numConstants)) {
return false;
}
materialConstants->reserve(numConstants);
materialConstants->resize(numConstants);
for (uint64_t i = 0; i < numConstants; i++) {
CString constantName;
bool defaultValue;
if (!unflattener.read(&constantName)) {
return false;
}
if (!unflattener.read(&defaultValue)) {
return false;
}
(*materialConstants)[i].name = std::move(constantName);
(*materialConstants)[i].defaultValue = defaultValue;
}
return true;
}
bool ChunkMaterialPushConstants::unflatten(Unflattener& unflattener,
CString* structVarName,
FixedCapacityVector<MaterialPushConstant>* materialPushConstants) {
+10
View File
@@ -49,6 +49,7 @@ class BufferInterfaceBlock;
class SamplerInterfaceBlock;
struct SubpassInfo;
struct MaterialConstant;
struct MaterialMutableConstant;
struct MaterialPushConstant;
class MaterialParser {
@@ -79,6 +80,8 @@ public:
bool getShaderModels(uint32_t* value) const noexcept;
bool getMaterialProperties(uint64_t* value) const noexcept;
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
bool getMutableConstants(utils::FixedCapacityVector<MaterialMutableConstant>* value)
const noexcept;
bool getPushConstants(utils::CString* structVarName,
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept;
@@ -237,6 +240,13 @@ struct ChunkMaterialConstants {
static filamat::ChunkType const tag = filamat::MaterialConstants;
};
struct ChunkMaterialMutableConstants {
static bool unflatten(filaflat::Unflattener& unflattener,
utils::FixedCapacityVector<MaterialMutableConstant>* materialConstants);
using Container = utils::FixedCapacityVector<MaterialMutableConstant>;
static filamat::ChunkType const tag = filamat::MaterialMutableConstants;
};
struct ChunkMaterialPushConstants {
static bool unflatten(filaflat::Unflattener& unflattener, utils::CString* structVarName,
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants);
+8 -2
View File
@@ -187,7 +187,10 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine,
if (UTILS_UNLIKELY(mSize)) {
loadMaterial(engine);
}
mMaterial->prepareProgram(Variant{ Variant::type_t(variant) });
mMaterial->prepareProgram(Variant{ Variant::type_t(variant) },
// TODO(exv): allow post process materials to use mutable spec constants?
Program::MutableSpecConstantsInfo(0)
);
return mMaterial;
}
@@ -423,7 +426,10 @@ UTILS_NOINLINE
PipelineState PostProcessManager::getPipelineState(
FMaterial const* const ma, PostProcessVariant variant) const noexcept {
return {
.program = ma->getProgram(Variant{ Variant::type_t(variant) }),
.program = ma->getProgram(Variant{ Variant::type_t(variant) },
// TODO(exv): allow post process materials to use mutable spec constants?
Program::MutableSpecConstantsInfo(0)
),
.vertexBufferInfo = mFullScreenQuadVbih,
.pipelineLayout = {
.setLayout = {
+10 -8
View File
@@ -42,12 +42,12 @@
#include <private/utils/Tracing.h>
#include <utils/JobSystem.h>
#include <utils/Panic.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/JobSystem.h>
#include <utils/Panic.h>
#include <utils/Slice.h>
#include <utils/Range.h>
#include <algorithm>
#include <functional>
@@ -246,7 +246,8 @@ void RenderPass::appendCommands(FEngine const& engine,
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
auto ma = first->info.mi->getMaterial();
ma->prepareProgram(first->info.materialVariant);
ma->prepareProgram(first->info.materialVariant,
first->info.mi->getMutableSpecConstants());
}
}
}
@@ -407,8 +408,8 @@ RenderPass::Command* RenderPass::instanceify(DriverApi& driver,
}
if (UTILS_UNLIKELY(firstSentinel)) {
// DLOG(INFO) << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
// << count;
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
// << count << io::endl;
// we have instanced primitives
// copy our instanced ubo data
driver.updateBufferObjectUnsynchronized(mInstancedUboHandle, {
@@ -1098,7 +1099,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
}
assert_invariant(ma);
pipeline.program = ma->getProgram(info.materialVariant);
pipeline.program = ma->getProgram(info.materialVariant,
info.mi->getMutableSpecConstants());
if (UTILS_UNLIKELY(memcmp(&pipeline, &currentPipeline, sizeof(PipelineState)) != 0)) {
currentPipeline = pipeline;
+8 -7
View File
@@ -27,11 +27,11 @@
#include "private/backend/DriverApi.h"
#include <utils/Logger.h>
#include <utils/algorithm.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <array>
@@ -291,16 +291,17 @@ void ResourceAllocator::gc(bool const skippedFrame) noexcept {
UTILS_NOINLINE
void ResourceAllocator::dump(bool const brief) const noexcept {
constexpr float MiB = 1.0f / float(1u << 20u);
DLOG(INFO) << "# entries=" << mTextureCache.size() << ", sz=" << (float) mCacheSize * MiB
<< " MiB"
<< ", max=" << (float) mCacheSizeHiWaterMark * MiB << " MiB";
slog.d << "# entries=" << mTextureCache.size()
<< ", sz=" << (float)mCacheSize * MiB << " MiB"
<< ", max=" << (float)mCacheSizeHiWaterMark * MiB << " MiB"
<< io::endl;
if (!brief) {
for (auto const& it : mTextureCache) {
auto w = it.first.width;
auto h = it.first.height;
auto f = FTexture::getFormatSize(it.first.format);
DLOG(INFO) << it.first.name << ": w=" << w << ", h=" << h << ", f=" << f
<< ", sz=" << (float) it.second.size * MiB;
slog.d << it.first.name << ": w=" << w << ", h=" << h << ", f=" << f << ", sz="
<< (float)it.second.size * MiB << io::endl;
}
}
}
@@ -308,7 +309,7 @@ void ResourceAllocator::dump(bool const brief) const noexcept {
ResourceAllocator::CacheContainer::iterator
ResourceAllocator::purge(
CacheContainer::iterator const& pos) {
// DLOG(INFO) << "purging " << pos->second.handle.getId() << ", age=" << pos->second.age;
//slog.d << "purging " << pos->second.handle.getId() << ", age=" << pos->second.age << io::endl;
mBackend.destroyTexture(pos->second.handle);
mCacheSize -= pos->second.size;
return mTextureCache.erase(pos);

Some files were not shown because too many files have changed in this diff Show More