Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a74936103 | ||
|
|
7967157fbb | ||
|
|
597ced13e1 | ||
|
|
c3542b135e | ||
|
|
ca3ff7e08e | ||
|
|
42c760a92f | ||
|
|
327a537bcc | ||
|
|
cfc4f34c18 | ||
|
|
6dac384bd9 | ||
|
|
d666f8a0ba | ||
|
|
d6ae3a57b5 | ||
|
|
c2155f3f98 | ||
|
|
e2a3637413 | ||
|
|
390df4e42c | ||
|
|
21dd1319df | ||
|
|
444ac8d6a6 | ||
|
|
aac1e7dc4c | ||
|
|
5b8a1e5e58 | ||
|
|
ad1b36d2b3 | ||
|
|
315c2c273f | ||
|
|
005d835dbe | ||
|
|
cade94ab2c | ||
|
|
5cd2f5626c | ||
|
|
94bbcbf1c3 | ||
|
|
52c998d2b6 | ||
|
|
527d831c15 | ||
|
|
2790f2e64c | ||
|
|
757640e850 | ||
|
|
9d9abca33a | ||
|
|
bea7a7c73f | ||
|
|
dd62f98784 | ||
|
|
395a3eda9b |
@@ -108,9 +108,21 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install python prereqs
|
- name: Cache Mesa and deps
|
||||||
run: pip install mako setuptools pyyaml
|
id: mesa-cache
|
||||||
- name: Run script
|
uses: actions/cache@v4 # Use a specific version
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
$HOME/Library/Caches/Homebrew
|
||||||
|
mesa
|
||||||
|
key: ${{ runner.os }}-mesa-deps-${{ vars.MESA_VERSION }}
|
||||||
|
- name: Get Mesa
|
||||||
|
id: mesa-prereq
|
||||||
|
env:
|
||||||
|
MESA_VERSION: ${{ vars.MESA_VERSION }}
|
||||||
|
run: |
|
||||||
|
bash test/utils/get_mesa.sh
|
||||||
|
- name: Run Test
|
||||||
run: |
|
run: |
|
||||||
bash test/renderdiff/test.sh
|
bash test/renderdiff/test.sh
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -18,3 +18,18 @@ test*.json
|
|||||||
results
|
results
|
||||||
/compile_commands.json
|
/compile_commands.json
|
||||||
/.cache
|
/.cache
|
||||||
|
build/.cmake/
|
||||||
|
build/CMakeCache.txt
|
||||||
|
build/CMakeFiles/
|
||||||
|
build/Makefile
|
||||||
|
build/SPIRV-Tools*
|
||||||
|
build/cmake_install.cmake
|
||||||
|
build/compile_commands.json
|
||||||
|
build/filament/
|
||||||
|
build/include/
|
||||||
|
build/libs/
|
||||||
|
build/mac/ninja
|
||||||
|
build/samples/
|
||||||
|
build/shaders/
|
||||||
|
build/third_party/
|
||||||
|
build/tools/
|
||||||
|
|||||||
@@ -801,6 +801,7 @@ add_subdirectory(${EXTERNAL}/draco/tnt)
|
|||||||
add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||||
add_subdirectory(${EXTERNAL}/getopt)
|
add_subdirectory(${EXTERNAL}/getopt)
|
||||||
|
add_subdirectory(${EXTERNAL}/perfetto/tnt)
|
||||||
|
|
||||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||||
add_subdirectory(${LIBRARIES}/geometry)
|
add_subdirectory(${LIBRARIES}/geometry)
|
||||||
|
|||||||
@@ -7,5 +7,3 @@ for next branch cut* header.
|
|||||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||||
|
|
||||||
## Release notes for next branch cut
|
## Release notes for next branch cut
|
||||||
|
|
||||||
- Fix build/compile errors when upgrading to MacOS 15.4
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.59.0'
|
implementation 'com.google.android.filament:filament-android:1.59.3'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -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:
|
iOS projects can use CocoaPods to install the latest release:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pod 'Filament', '~> 1.59.0'
|
pod 'Filament', '~> 1.59.3'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ 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
|
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||||
|
|
||||||
|
## v1.59.4
|
||||||
|
|
||||||
|
|
||||||
|
## v1.59.3
|
||||||
|
|
||||||
|
|
||||||
|
## v1.59.2
|
||||||
|
|
||||||
|
- Fix build/compile errors when upgrading to MacOS 15.4
|
||||||
|
|
||||||
## v1.59.1
|
## v1.59.1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ add_library(utils STATIC IMPORTED)
|
|||||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||||
|
|
||||||
|
add_library(perfetto STATIC IMPORTED)
|
||||||
|
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
|
||||||
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
|
||||||
|
|
||||||
add_library(filabridge STATIC IMPORTED)
|
add_library(filabridge STATIC IMPORTED)
|
||||||
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
|
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
|
||||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
|
||||||
@@ -40,6 +44,7 @@ set_target_properties(shaders PROPERTIES IMPORTED_LOCATION
|
|||||||
|
|
||||||
set(FILAMAT_INCLUDE_DIRS
|
set(FILAMAT_INCLUDE_DIRS
|
||||||
../../libs/utils/include
|
../../libs/utils/include
|
||||||
|
../../third_party/perfetto
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(${FILAMENT_DIR}/include)
|
include_directories(${FILAMENT_DIR}/include)
|
||||||
@@ -55,6 +60,7 @@ target_link_libraries(filamat-jni
|
|||||||
filabridge
|
filabridge
|
||||||
shaders
|
shaders
|
||||||
utils
|
utils
|
||||||
|
perfetto
|
||||||
log
|
log
|
||||||
smol-v
|
smol-v
|
||||||
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
|
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ add_library(utils STATIC IMPORTED)
|
|||||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||||
|
|
||||||
|
add_library(perfetto STATIC IMPORTED)
|
||||||
|
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
|
||||||
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
|
||||||
|
|
||||||
add_library(ibl-lite STATIC IMPORTED)
|
add_library(ibl-lite STATIC IMPORTED)
|
||||||
set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION
|
set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION
|
||||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a)
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a)
|
||||||
@@ -123,6 +127,7 @@ target_link_libraries(filament-jni
|
|||||||
PRIVATE android
|
PRIVATE android
|
||||||
PRIVATE jnigraphics
|
PRIVATE jnigraphics
|
||||||
PRIVATE utils
|
PRIVATE utils
|
||||||
|
PRIVATE perfetto
|
||||||
|
|
||||||
# libgeometry is PUBLIC because gltfio uses it.
|
# libgeometry is PUBLIC because gltfio uses it.
|
||||||
PUBLIC geometry
|
PUBLIC geometry
|
||||||
@@ -141,6 +146,7 @@ target_include_directories(filament-jni PRIVATE
|
|||||||
${FILAMENT_DIR}/include
|
${FILAMENT_DIR}/include
|
||||||
../../filament/backend/include
|
../../filament/backend/include
|
||||||
../../third_party/robin-map
|
../../third_party/robin-map
|
||||||
|
../../third_party/perfetto
|
||||||
../../libs/utils/include)
|
../../libs/utils/include)
|
||||||
|
|
||||||
# Force a relink when the version script is changed:
|
# Force a relink when the version script is changed:
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ add_library(utils STATIC IMPORTED)
|
|||||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||||
|
|
||||||
|
add_library(perfetto STATIC IMPORTED)
|
||||||
|
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
|
||||||
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
|
||||||
|
|
||||||
add_library(uberzlib STATIC IMPORTED)
|
add_library(uberzlib STATIC IMPORTED)
|
||||||
set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION
|
set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION
|
||||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a)
|
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a)
|
||||||
@@ -121,6 +125,7 @@ set(GLTFIO_INCLUDE_DIRS
|
|||||||
../../third_party/meshoptimizer/src
|
../../third_party/meshoptimizer/src
|
||||||
../../third_party/robin-map
|
../../third_party/robin-map
|
||||||
../../third_party/stb
|
../../third_party/stb
|
||||||
|
../../third_party/perfetto
|
||||||
../../libs/utils/include
|
../../libs/utils/include
|
||||||
../../libs/ktxreader/include
|
../../libs/ktxreader/include
|
||||||
)
|
)
|
||||||
@@ -129,7 +134,7 @@ add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
|
|||||||
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
|
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
|
||||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
|
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
|
||||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
|
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
|
||||||
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
|
target_link_libraries(gltfio-jni filament-jni utils perfetto uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
|
||||||
target_link_libraries(gltfio-jni dracodec meshoptimizer)
|
target_link_libraries(gltfio-jni dracodec meshoptimizer)
|
||||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
|
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
GROUP=com.google.android.filament
|
GROUP=com.google.android.filament
|
||||||
VERSION_NAME=1.59.0
|
VERSION_NAME=1.59.3
|
||||||
|
|
||||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||||
|
|
||||||
|
|||||||
@@ -194,8 +194,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
|||||||
src/vulkan/VulkanDriver.cpp
|
src/vulkan/VulkanDriver.cpp
|
||||||
src/vulkan/VulkanDriver.h
|
src/vulkan/VulkanDriver.h
|
||||||
src/vulkan/VulkanDriverFactory.h
|
src/vulkan/VulkanDriverFactory.h
|
||||||
src/vulkan/VulkanExternalImageManager.cpp
|
|
||||||
src/vulkan/VulkanExternalImageManager.h
|
|
||||||
src/vulkan/VulkanFboCache.cpp
|
src/vulkan/VulkanFboCache.cpp
|
||||||
src/vulkan/VulkanFboCache.h
|
src/vulkan/VulkanFboCache.h
|
||||||
src/vulkan/VulkanHandles.cpp
|
src/vulkan/VulkanHandles.cpp
|
||||||
@@ -257,8 +255,11 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
|||||||
src/webgpu/WebGPUConstants.h
|
src/webgpu/WebGPUConstants.h
|
||||||
src/webgpu/WebGPUDriver.cpp
|
src/webgpu/WebGPUDriver.cpp
|
||||||
src/webgpu/WebGPUDriver.h
|
src/webgpu/WebGPUDriver.h
|
||||||
|
src/webgpu/WebGPUHandles.cpp
|
||||||
|
src/webgpu/WebGPUHandles.h
|
||||||
src/webgpu/WebGPUSwapChain.cpp
|
src/webgpu/WebGPUSwapChain.cpp
|
||||||
src/webgpu/WebGPUSwapChain.h
|
src/webgpu/WebGPUSwapChain.h
|
||||||
|
src/webgpu/WGPUProgram.cpp
|
||||||
)
|
)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||||
@@ -507,8 +508,10 @@ if (APPLE OR LINUX)
|
|||||||
test/Arguments.cpp
|
test/Arguments.cpp
|
||||||
test/ImageExpectations.cpp
|
test/ImageExpectations.cpp
|
||||||
test/Lifetimes.cpp
|
test/Lifetimes.cpp
|
||||||
|
test/PlatformRunner.cpp
|
||||||
test/Shader.cpp
|
test/Shader.cpp
|
||||||
test/SharedShaders.cpp
|
test/SharedShaders.cpp
|
||||||
|
test/Skip.cpp
|
||||||
test/test_FeedbackLoops.cpp
|
test/test_FeedbackLoops.cpp
|
||||||
test/test_Blit.cpp
|
test/test_Blit.cpp
|
||||||
test/test_MissingRequiredAttributes.cpp
|
test/test_MissingRequiredAttributes.cpp
|
||||||
@@ -532,6 +535,9 @@ if (APPLE OR LINUX)
|
|||||||
filamat
|
filamat
|
||||||
SPIRV
|
SPIRV
|
||||||
spirv-cross-glsl)
|
spirv-cross-glsl)
|
||||||
|
# Create input/output directories for test result images.
|
||||||
|
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/actual_images)
|
||||||
|
file(COPY test/expected_images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# TODO: Disabling IOS test due to breakage wrt glslang update
|
# TODO: Disabling IOS test due to breakage wrt glslang update
|
||||||
|
|||||||
@@ -55,4 +55,9 @@ public:
|
|||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||||
|
const filament::backend::BufferObjectStreamDescriptor& b);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
||||||
|
|||||||
@@ -149,13 +149,6 @@ public:
|
|||||||
* - PlatformEGLAndroid
|
* - PlatformEGLAndroid
|
||||||
*/
|
*/
|
||||||
bool assertNativeWindowIsValid = false;
|
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;
|
Platform() noexcept;
|
||||||
|
|||||||
@@ -294,16 +294,6 @@ public:
|
|||||||
VkQueue getProtectedGraphicsQueue() const noexcept;
|
VkQueue getProtectedGraphicsQueue() const noexcept;
|
||||||
|
|
||||||
struct ExternalImageMetadata {
|
struct ExternalImageMetadata {
|
||||||
/**
|
|
||||||
* The Filament texture format.
|
|
||||||
*/
|
|
||||||
TextureFormat filamentFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Filament texture usage.
|
|
||||||
*/
|
|
||||||
TextureUsage filamentUsage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The width of the external image
|
* The width of the external image
|
||||||
*/
|
*/
|
||||||
@@ -314,6 +304,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The layerCount of the external image
|
||||||
|
*/
|
||||||
|
uint32_t layerCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layer count of the external image
|
* The layer count of the external image
|
||||||
*/
|
*/
|
||||||
@@ -329,6 +324,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
VkFormat format;
|
VkFormat format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An external buffer can be protected. This tells you if it is.
|
||||||
|
*/
|
||||||
|
bool isProtected;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of external format (opaque int) if used.
|
* The type of external format (opaque int) if used.
|
||||||
*/
|
*/
|
||||||
@@ -348,44 +348,20 @@ public:
|
|||||||
* Heap information
|
* Heap information
|
||||||
*/
|
*/
|
||||||
uint32_t memoryTypeBits;
|
uint32_t memoryTypeBits;
|
||||||
|
|
||||||
/**
|
|
||||||
* Ycbcr conversion components
|
|
||||||
*/
|
|
||||||
VkComponentMapping ycbcrConversionComponents;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ycbcr model
|
|
||||||
*/
|
|
||||||
VkSamplerYcbcrModelConversion ycbcrModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ycbcr range
|
|
||||||
*/
|
|
||||||
VkSamplerYcbcrRange ycbcrRange;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ycbcr x chroma offset
|
|
||||||
*/
|
|
||||||
VkChromaLocation xChromaOffset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ycbcr y chroma offset
|
|
||||||
*/
|
|
||||||
VkChromaLocation yChromaOffset;
|
|
||||||
};
|
};
|
||||||
|
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||||
|
|
||||||
// Note that the image metadata might change per-frame, hence we need a method for extracting
|
|
||||||
// it.
|
|
||||||
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
||||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
|
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||||
return { VK_NULL_HANDLE, VK_NULL_HANDLE };
|
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||||
}
|
VkImageUsageFlags usage);
|
||||||
|
|
||||||
|
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||||
|
SamplerParams sampler, uint32_t internalFormat);
|
||||||
|
|
||||||
|
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||||
|
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||||
|
VkImageViewType viewType, VkComponentMapping swizzle);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||||
@@ -398,6 +374,20 @@ private:
|
|||||||
// Platform dependent helper methods
|
// Platform dependent helper methods
|
||||||
static ExtensionSet getSwapchainInstanceExtensionsImpl();
|
static ExtensionSet getSwapchainInstanceExtensionsImpl();
|
||||||
|
|
||||||
|
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
|
||||||
|
VkDevice device);
|
||||||
|
|
||||||
|
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
|
||||||
|
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||||
|
VkImageUsageFlags usage);
|
||||||
|
static VkSampler createExternalSamplerImpl(VkDevice device,
|
||||||
|
SamplerYcbcrConversion chroma, SamplerParams sampler,
|
||||||
|
uint32_t internalFormat);
|
||||||
|
static VkImageView createExternalImageViewImpl(VkDevice device,
|
||||||
|
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||||
|
VkImageSubresourceRange range, VkImageViewType viewType,
|
||||||
|
VkComponentMapping swizzle);
|
||||||
|
|
||||||
// Platform dependent helper methods
|
// Platform dependent helper methods
|
||||||
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
|
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
|
||||||
uint64_t flags) noexcept;
|
uint64_t flags) noexcept;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace filament::backend {
|
|||||||
|
|
||||||
class VulkanPlatformAndroid : public VulkanPlatform {
|
class VulkanPlatformAndroid : public VulkanPlatform {
|
||||||
public:
|
public:
|
||||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
||||||
bool sRGB) noexcept;
|
bool sRGB) noexcept;
|
||||||
|
|
||||||
struct UTILS_PUBLIC ExternalImageDescAndroid {
|
struct UTILS_PUBLIC ExternalImageDescAndroid {
|
||||||
@@ -39,26 +39,31 @@ public:
|
|||||||
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
|
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
|
||||||
ExternalImageHandleRef externalImage) const noexcept;
|
ExternalImageHandleRef externalImage) const noexcept;
|
||||||
|
|
||||||
virtual ExternalImageMetadata extractExternalImageMetadata(
|
|
||||||
ExternalImageHandleRef image) const override;
|
|
||||||
|
|
||||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
|
|
||||||
|
|
||||||
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
|
||||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
|
||||||
uint64_t flags) const noexcept override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
|
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
|
||||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||||
bool sRGB = false;
|
bool sRGB = false;
|
||||||
|
unsigned int width; // Texture width
|
||||||
|
unsigned int height; // Texture height
|
||||||
|
TextureFormat format;// Texture format
|
||||||
|
TextureUsage usage; // Texture usage flags
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~ExternalImageVulkanAndroid() override;
|
~ExternalImageVulkanAndroid() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||||
|
|
||||||
|
using ImageData = VulkanPlatform::ImageData;
|
||||||
|
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||||
|
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||||
|
VkImageUsageFlags usage);
|
||||||
|
|
||||||
|
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||||
|
|
||||||
|
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
||||||
|
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||||
|
uint64_t flags) const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
|
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
|
||||||
|
|
||||||
|
// TODO consider that this functionality is not WebGPU-specific, and thus could be
|
||||||
|
// placed in a generic place and even reused across backends. Alternatively,
|
||||||
|
// a 3rd party library could be considered. However, this was a simple and
|
||||||
|
// quick change and works for now.
|
||||||
|
// gets the size (height and width) of the surface/window
|
||||||
|
[[nodiscard]] wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const;
|
||||||
// either returns a valid surface or panics
|
// either returns a valid surface or panics
|
||||||
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
||||||
// either returns a valid adapter or panics
|
// either returns a valid adapter or panics
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
|
|||||||
|
|
||||||
Profiler profiler;
|
Profiler profiler;
|
||||||
|
|
||||||
if (SYSTRACE_TAG) {
|
if constexpr (SYSTRACE_TAG) {
|
||||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||||
// we want to remove all this when tracing is completely disabled
|
// we want to remove all this when tracing is completely disabled
|
||||||
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
|
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
|
||||||
@@ -100,7 +100,7 @@ void CommandStream::execute(void* buffer) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (SYSTRACE_TAG) {
|
if constexpr (SYSTRACE_TAG) {
|
||||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||||
// we want to remove all this when tracing is completely disabled
|
// we want to remove all this when tracing is completely disabled
|
||||||
profiler.stop();
|
profiler.stop();
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) 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);
|
return MetalDriverFactory::create(this, driverConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
|
|||||||
delete lazyInitializationData;
|
delete lazyInitializationData;
|
||||||
|
|
||||||
ShaderCompilerService::terminate(mToken);
|
ShaderCompilerService::terminate(mToken);
|
||||||
|
assert_invariant(!mToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete [] mUniformsRecords;
|
delete [] mUniformsRecords;
|
||||||
|
|||||||
@@ -24,23 +24,23 @@
|
|||||||
#include "OpenGLBlobCache.h"
|
#include "OpenGLBlobCache.h"
|
||||||
|
|
||||||
#include <backend/CallbackHandler.h>
|
#include <backend/CallbackHandler.h>
|
||||||
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Program.h>
|
#include <backend/Program.h>
|
||||||
|
|
||||||
#include <utils/CString.h>
|
#include <utils/CString.h>
|
||||||
#include <utils/FixedCapacityVector.h>
|
#include <utils/FixedCapacityVector.h>
|
||||||
#include <utils/Invocable.h>
|
|
||||||
#include <utils/JobSystem.h>
|
#include <utils/JobSystem.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <array>
|
||||||
#include <condition_variable>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class OpenGLDriver;
|
class OpenGLDriver;
|
||||||
@@ -57,6 +57,8 @@ class ShaderCompilerService {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using program_token_t = std::shared_ptr<OpenGLProgramToken>;
|
using program_token_t = std::shared_ptr<OpenGLProgramToken>;
|
||||||
|
using shaders_t = std::array<GLuint, Program::SHADER_TYPE_COUNT>;
|
||||||
|
using shaders_source_t = std::array<utils::CString, Program::SHADER_TYPE_COUNT>;
|
||||||
|
|
||||||
explicit ShaderCompilerService(OpenGLDriver& driver);
|
explicit ShaderCompilerService(OpenGLDriver& driver);
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ public:
|
|||||||
void tick();
|
void tick();
|
||||||
|
|
||||||
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
|
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
|
||||||
|
// This function is not called if `initialize(token)` is already invoked.
|
||||||
static void terminate(program_token_t& token);
|
static void terminate(program_token_t& token);
|
||||||
|
|
||||||
// stores a user data pointer in the token
|
// stores a user data pointer in the token
|
||||||
@@ -90,6 +93,12 @@ public:
|
|||||||
// retrieves the user data pointer stored in the token
|
// retrieves the user data pointer stored in the token
|
||||||
static void* getUserData(const program_token_t& token) noexcept;
|
static void* getUserData(const program_token_t& token) noexcept;
|
||||||
|
|
||||||
|
// Issue one callback handle.
|
||||||
|
CallbackManager::Handle issueCallbackHandle() const noexcept;
|
||||||
|
|
||||||
|
// Return a callback handle to the callback manager.
|
||||||
|
void submitCallbackHandle(CallbackManager::Handle handle) noexcept;
|
||||||
|
|
||||||
// call the callback when all active programs are ready
|
// call the callback when all active programs are ready
|
||||||
void notifyWhenAllProgramsAreReady(
|
void notifyWhenAllProgramsAreReady(
|
||||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
|
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
|
||||||
@@ -97,7 +106,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
struct Job {
|
struct Job {
|
||||||
template<typename FUNC>
|
template<typename FUNC>
|
||||||
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
|
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {} // NOLINT(*-explicit-constructor)
|
||||||
Job(std::function<bool(Job const& job)> fn,
|
Job(std::function<bool(Job const& job)> fn,
|
||||||
CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
|
CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
|
||||||
: fn(std::move(fn)), handler(handler), user(user), callback(callback) {
|
: fn(std::move(fn)), handler(handler), user(user), callback(callback) {
|
||||||
@@ -126,39 +135,49 @@ private:
|
|||||||
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
|
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
|
||||||
std::vector<ContainerType> mRunAtNextTickOps;
|
std::vector<ContainerType> mRunAtNextTickOps;
|
||||||
|
|
||||||
GLuint initialize(ShaderCompilerService::program_token_t& token) noexcept;
|
GLuint initialize(program_token_t& token);
|
||||||
|
void ensureTokenIsReady(program_token_t const& token);
|
||||||
|
|
||||||
static void getProgramFromCompilerPool(program_token_t& token) noexcept;
|
void runAtNextTick(CompilerPriorityQueue priority, program_token_t const& token,
|
||||||
|
Job job) noexcept;
|
||||||
static void compileShaders(
|
|
||||||
OpenGLContext& context,
|
|
||||||
Program::ShaderSource shadersSource,
|
|
||||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
|
||||||
bool multiview,
|
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
|
||||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
|
|
||||||
|
|
||||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
|
||||||
char* source, size_t len) noexcept;
|
|
||||||
|
|
||||||
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
|
|
||||||
char* source, size_t len) noexcept;
|
|
||||||
|
|
||||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
|
||||||
|
|
||||||
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
|
|
||||||
|
|
||||||
static GLuint linkProgram(OpenGLContext& context,
|
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
|
|
||||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept;
|
|
||||||
|
|
||||||
static bool checkProgramStatus(program_token_t const& token) noexcept;
|
|
||||||
|
|
||||||
void runAtNextTick(CompilerPriorityQueue priority,
|
|
||||||
const program_token_t& token, Job job) noexcept;
|
|
||||||
void executeTickOps() noexcept;
|
void executeTickOps() noexcept;
|
||||||
bool cancelTickOp(program_token_t token) noexcept;
|
bool cancelTickOp(program_token_t const& token) noexcept;
|
||||||
// order of insertion is important
|
|
||||||
|
// Compile shaders with the given `shaderSource`. `gl.shaders` is always populated with valid
|
||||||
|
// shader IDs after this method. But this doesn't necessarily mean the shaders are successfully
|
||||||
|
// compiled. Errors can be checked by calling `checkCompileStatus` later.
|
||||||
|
static void compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource,
|
||||||
|
utils::FixedCapacityVector<Program::SpecializationConstant> const&
|
||||||
|
specializationConstants,
|
||||||
|
bool multiview, program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Check if the shader compilation is completed. You may want to call this when the extension
|
||||||
|
// `KHR_parallel_shader_compile` is enabled.
|
||||||
|
static bool isCompileCompleted(program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Check compilation status of the shaders and log errors on failure.
|
||||||
|
static void checkCompileStatus(program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Create a program by linking the compiled shaders. `gl.program` is always populated with a
|
||||||
|
// valid program ID after this method. But this doesn't necessarily mean the program is
|
||||||
|
// successfully linked. Errors can be checked by calling `checkLinkStatusAndCleanupShaders`
|
||||||
|
// later.
|
||||||
|
static void linkProgram(OpenGLContext const& context, program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Check if the program link is completed. You may want to call this when the extension
|
||||||
|
// `KHR_parallel_shader_compile` is enabled.
|
||||||
|
static bool isLinkCompleted(program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Check link status of the program and log errors on failure. Return the result of the link.
|
||||||
|
// Also cleanup shaders regardless of the result.
|
||||||
|
static bool checkLinkStatusAndCleanupShaders(program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Try caching the program if we haven't done it yet. Cache it only when the program is valid.
|
||||||
|
static void tryCachingProgram(OpenGLBlobCache& cache, OpenGLPlatform& platform,
|
||||||
|
program_token_t const& token) noexcept;
|
||||||
|
|
||||||
|
// Cleanup GL resources.
|
||||||
|
static void cleanupProgramAndShaders(program_token_t const& token) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <backend/BufferDescriptor.h>
|
#include <backend/BufferDescriptor.h>
|
||||||
|
#include <backend/BufferObjectStreamDescriptor.h>
|
||||||
#include <backend/DescriptorSetOffsetArray.h>
|
#include <backend/DescriptorSetOffsetArray.h>
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/PipelineState.h>
|
#include <backend/PipelineState.h>
|
||||||
@@ -437,6 +438,10 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
|
|||||||
<< ", user=" << b.getUser() << " }";
|
<< ", user=" << b.getUser() << " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
|
||||||
|
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
|
||||||
|
}
|
||||||
|
|
||||||
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
|
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
|
||||||
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
||||||
return out << "PixelBufferDescriptor{ " << base
|
return out << "PixelBufferDescriptor{ " << base
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_VALIDATION)
|
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
|
||||||
#else
|
#else
|
||||||
#define FVK_DEBUG_FLAGS 0
|
#define FVK_DEBUG_FLAGS 0
|
||||||
#endif
|
#endif
|
||||||
@@ -152,12 +152,12 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
|||||||
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
||||||
|
|
||||||
#include <utils/Systrace.h>
|
#include <utils/Systrace.h>
|
||||||
|
|
||||||
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
|
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
|
||||||
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
|
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
|
||||||
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
|
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
|
||||||
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
|
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL()
|
||||||
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
|
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL()
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define FVK_SYSTRACE_CONTEXT()
|
#define FVK_SYSTRACE_CONTEXT()
|
||||||
|
|||||||
@@ -200,7 +200,9 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
|||||||
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
||||||
Platform::DriverConfig const& driverConfig) noexcept
|
Platform::DriverConfig const& driverConfig) noexcept
|
||||||
: mPlatform(platform),
|
: mPlatform(platform),
|
||||||
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
|
mResourceManager(
|
||||||
|
driverConfig.handleArenaSize,
|
||||||
|
driverConfig.disableHandleUseAfterFreeCheck,
|
||||||
driverConfig.disableHeapHandleTags),
|
driverConfig.disableHeapHandleTags),
|
||||||
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
||||||
mPlatform->getDevice())),
|
mPlatform->getDevice())),
|
||||||
@@ -219,8 +221,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
|||||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
||||||
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
||||||
mQueryManager(mPlatform->getDevice()),
|
mQueryManager(mPlatform->getDevice()),
|
||||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
|
||||||
&mDescriptorSetLayoutCache),
|
|
||||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ void VulkanDriver::terminate() {
|
|||||||
|
|
||||||
mCurrentSwapChain = {};
|
mCurrentSwapChain = {};
|
||||||
mDefaultRenderTarget = {};
|
mDefaultRenderTarget = {};
|
||||||
mPipelineState = {};
|
mBoundPipeline = {};
|
||||||
|
|
||||||
mQueryManager.terminate();
|
mQueryManager.terminate();
|
||||||
|
|
||||||
@@ -325,13 +325,9 @@ void VulkanDriver::terminate() {
|
|||||||
|
|
||||||
mCommands.terminate();
|
mCommands.terminate();
|
||||||
|
|
||||||
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
|
|
||||||
// descriptorSetLayoutCache
|
|
||||||
mExternalImageManager.terminate();
|
|
||||||
|
|
||||||
mStagePool.terminate();
|
mStagePool.terminate();
|
||||||
mPipelineCache.terminate();
|
mPipelineCache.terminate();
|
||||||
mFramebufferCache.reset();
|
mFramebufferCache.terminate();
|
||||||
mSamplerCache.terminate();
|
mSamplerCache.terminate();
|
||||||
mDescriptorSetLayoutCache.terminate();
|
mDescriptorSetLayoutCache.terminate();
|
||||||
mDescriptorSetCache.terminate();
|
mDescriptorSetCache.terminate();
|
||||||
@@ -385,10 +381,6 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
|||||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
|
||||||
if (mAppState.hasExternalSamplers()) {
|
|
||||||
mExternalImageManager.onBeginFrame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
||||||
@@ -429,16 +421,12 @@ void VulkanDriver::updateDescriptorSetTexture(
|
|||||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||||
|
|
||||||
if (mExternalImageManager.isExternallySampledTexture(texture)) {
|
// TODO: YcbcrConversion?
|
||||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
VulkanSamplerCache::Params cacheParams = {
|
||||||
mAppState.hasBoundExternalImages = true;
|
.sampler = params,
|
||||||
} else {
|
};
|
||||||
VulkanSamplerCache::Params cacheParams = {
|
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||||
.sampler = params,
|
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||||
};
|
|
||||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
|
||||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::flush(int) {
|
void VulkanDriver::flush(int) {
|
||||||
@@ -575,35 +563,41 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
|||||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||||
Platform::ExternalImageHandleRef externalImage) {
|
Platform::ExternalImageHandleRef externalImage) {
|
||||||
FVK_SYSTRACE_SCOPE();
|
FVK_SYSTRACE_SCOPE();
|
||||||
auto const& metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||||
|
if (metadata.isProtected) {
|
||||||
|
usage |= backend::TextureUsage::PROTECTED;
|
||||||
|
}
|
||||||
|
|
||||||
// In theory the following are reasonable expectations, but in practice it's hard for client's
|
VkImageUsageFlags vkUsage = metadata.usage;
|
||||||
// to match up the dimensions of the texture with that of the AHB.
|
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||||
// assert_invariant(width == metadata.width);
|
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
// assert_invariant(height == metadata.height);
|
}
|
||||||
// assert_invariant(format == metadata.filamentFormat);
|
|
||||||
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||||
|
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_invariant(width == metadata.width);
|
||||||
|
assert_invariant(height == metadata.height);
|
||||||
|
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||||
|
|
||||||
|
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
|
||||||
|
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
uint32_t const memoryTypeIndex =
|
||||||
|
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||||
|
<< "failed to find a valid memory type for external image memory.";
|
||||||
|
|
||||||
VkImage vkimg;
|
VkImage vkimg;
|
||||||
VkDeviceMemory deviceMemory;
|
VkDeviceMemory deviceMemory;
|
||||||
std::tie(vkimg, deviceMemory) = mPlatform->createVkImageFromExternal(externalImage);
|
std::tie(vkimg, deviceMemory) =
|
||||||
|
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||||
VkSamplerYcbcrConversion conversion =
|
|
||||||
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
|
|
||||||
|
|
||||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
||||||
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
|
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
|
||||||
metadata.format, conversion, metadata.samples, metadata.width, metadata.height,
|
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
|
||||||
metadata.layers, usage, mStagePool);
|
metadata.layerCount, usage, mStagePool);
|
||||||
|
|
||||||
if (conversion != VK_NULL_HANDLE) {
|
|
||||||
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlike uploaded textures or swapchains, we need to explicit transition this
|
|
||||||
// texture into the read layout.
|
|
||||||
auto& commands = mCommands.get();
|
|
||||||
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
|
|
||||||
|
|
||||||
texture.inc();
|
texture.inc();
|
||||||
}
|
}
|
||||||
@@ -636,8 +630,6 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
|||||||
}
|
}
|
||||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||||
texture.dec();
|
texture.dec();
|
||||||
|
|
||||||
mExternalImageManager.removeExternallySampledTexture(texture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||||
@@ -806,11 +798,6 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
|||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||||
auto set = mDescriptorSetCache.createSet(dsh, layout);
|
auto set = mDescriptorSetCache.createSet(dsh, layout);
|
||||||
set.inc();
|
set.inc();
|
||||||
|
|
||||||
if (layout->hasExternalSamplers()) {
|
|
||||||
mAppState.hasExternalSamplerLayouts = true;
|
|
||||||
mExternalImageManager.addDescriptorSet(layout, set);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
||||||
@@ -930,19 +917,11 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
|||||||
void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh) {
|
void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh) {
|
||||||
auto layout = resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
auto layout = resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||||
layout.dec();
|
layout.dec();
|
||||||
|
|
||||||
if (layout->hasExternalSamplers()) {
|
|
||||||
mExternalImageManager.removeDescriptorSetLayout(layout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||||
set.dec();
|
set.dec();
|
||||||
|
|
||||||
if (mAppState.hasExternalSamplers()) {
|
|
||||||
mExternalImageManager.removeDescriptorSet(set);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
|
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
|
||||||
@@ -1503,6 +1482,7 @@ void VulkanDriver::endRenderPass(int) {
|
|||||||
// pipeline barrier between framebuffer writes and shader reads.
|
// pipeline barrier between framebuffer writes and shader reads.
|
||||||
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
||||||
|
|
||||||
|
mRenderPassFboInfo = {};
|
||||||
mCurrentRenderPass.renderTarget = {};
|
mCurrentRenderPass.renderTarget = {};
|
||||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||||
|
|
||||||
@@ -1543,7 +1523,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
|||||||
swapChain->acquire(resized);
|
swapChain->acquire(resized);
|
||||||
|
|
||||||
if (resized) {
|
if (resized) {
|
||||||
mFramebufferCache.reset();
|
mFramebufferCache.resetFramebuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
||||||
@@ -1562,10 +1542,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
|||||||
|
|
||||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||||
backend::PushConstantVariant value) {
|
backend::PushConstantVariant value) {
|
||||||
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
|
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||||
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
|
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
|
||||||
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||||
mPipelineState.pipelineLayout, stage, index, value);
|
mBoundPipeline.pipelineLayout, stage, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::insertEventMarker(char const* string) {
|
void VulkanDriver::insertEventMarker(char const* string) {
|
||||||
@@ -1744,27 +1724,6 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||||
// We need to determine whether to delay bindning until draw().
|
|
||||||
mPipelineState.bindInDraw.first = false;
|
|
||||||
if (mAppState.hasExternalSamplers()) {
|
|
||||||
auto& layouts = pipelineState.pipelineLayout.setLayout;
|
|
||||||
auto haveExternalSamplers = [&](auto hwHandle) {
|
|
||||||
if (!hwHandle) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto layout =
|
|
||||||
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, hwHandle);
|
|
||||||
return layout->hasExternalSamplers();
|
|
||||||
};
|
|
||||||
if (std::any_of(layouts.begin(), layouts.end(), haveExternalSamplers)) {
|
|
||||||
mPipelineState.bindInDraw = { true, pipelineState };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bindPipelineImpl(pipelineState);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState) {
|
|
||||||
FVK_SYSTRACE_SCOPE();
|
FVK_SYSTRACE_SCOPE();
|
||||||
auto commands = mCurrentRenderPass.commandBuffer;
|
auto commands = mCurrentRenderPass.commandBuffer;
|
||||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
|
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
|
||||||
@@ -1833,11 +1792,10 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState) {
|
|||||||
|
|
||||||
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
|
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
|
||||||
|
|
||||||
mPipelineState = {
|
mBoundPipeline = {
|
||||||
.program = program,
|
.program = program,
|
||||||
.pipelineLayout = pipelineLayout,
|
.pipelineLayout = pipelineLayout,
|
||||||
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
|
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
|
||||||
.bindInDraw = {false, {}},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mPipelineCache.bindLayout(pipelineLayout);
|
mPipelineCache.bindLayout(pipelineLayout);
|
||||||
@@ -1884,24 +1842,14 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
|||||||
FVK_SYSTRACE_SCOPE();
|
FVK_SYSTRACE_SCOPE();
|
||||||
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
|
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
|
||||||
|
|
||||||
if (mAppState.hasExternalSamplers()) {
|
|
||||||
auto const& [bindInDraw, pipelineSt] = mPipelineState.bindInDraw;
|
|
||||||
bool const hasUpdated =
|
|
||||||
mExternalImageManager.prepareBindSets(mDescriptorSetCache.getBoundSets());
|
|
||||||
if (bindInDraw || hasUpdated) {
|
|
||||||
bindPipelineImpl(pipelineSt);
|
|
||||||
}
|
|
||||||
mPipelineState.bindInDraw.first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
|
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
|
||||||
mPipelineState.pipelineLayout,
|
mBoundPipeline.pipelineLayout,
|
||||||
mPipelineState.descriptorSetMask);
|
mBoundPipeline.descriptorSetMask);
|
||||||
|
|
||||||
// Finally, make the actual draw call. TODO: support subranges
|
// Finally, make the actual draw call. TODO: support subranges
|
||||||
uint32_t const firstIndex = indexOffset;
|
const uint32_t firstIndex = indexOffset;
|
||||||
constexpr int32_t vertexOffset = 0;
|
const int32_t vertexOffset = 0;
|
||||||
constexpr uint32_t firstInstId = 0;
|
const uint32_t firstInstId = 0;
|
||||||
|
|
||||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "VulkanYcbcrConversionCache.h"
|
#include "VulkanYcbcrConversionCache.h"
|
||||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||||
#include "vulkan/VulkanExternalImageManager.h"
|
|
||||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||||
#include "vulkan/memory/ResourceManager.h"
|
#include "vulkan/memory/ResourceManager.h"
|
||||||
#include "vulkan/memory/ResourcePointer.h"
|
#include "vulkan/memory/ResourcePointer.h"
|
||||||
@@ -120,7 +119,6 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void collectGarbage();
|
void collectGarbage();
|
||||||
void bindPipelineImpl(PipelineState const& pipelineState);
|
|
||||||
|
|
||||||
VulkanPlatform* mPlatform = nullptr;
|
VulkanPlatform* mPlatform = nullptr;
|
||||||
fvkmemory::ResourceManager mResourceManager;
|
fvkmemory::ResourceManager mResourceManager;
|
||||||
@@ -145,29 +143,21 @@ private:
|
|||||||
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
||||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||||
VulkanQueryManager mQueryManager;
|
VulkanQueryManager mQueryManager;
|
||||||
VulkanExternalImageManager mExternalImageManager;
|
|
||||||
|
|
||||||
// This is necessary for us to write to push constants after binding a pipeline.
|
// This is necessary for us to write to push constants after binding a pipeline.
|
||||||
struct {
|
struct {
|
||||||
// For push constant
|
|
||||||
resource_ptr<VulkanProgram> program;
|
resource_ptr<VulkanProgram> program;
|
||||||
// For push commiting dynamic ubos in draw()
|
|
||||||
VkPipelineLayout pipelineLayout;
|
VkPipelineLayout pipelineLayout;
|
||||||
fvkutils::DescriptorSetMask descriptorSetMask;
|
fvkutils::DescriptorSetMask descriptorSetMask;
|
||||||
|
} mBoundPipeline = {};
|
||||||
|
|
||||||
std::pair<bool, PipelineState> bindInDraw = {false, {}};
|
// We need to store information about a render pass to enable better barriers at the end of a
|
||||||
} mPipelineState = {};
|
// renderpass.
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
// This tracks whether the app has seen external samplers bound to a the descriptor set.
|
using AttachmentArray =
|
||||||
// This will force bindPipeline to take a slow path.
|
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
|
||||||
bool hasExternalSamplerLayouts = false;
|
AttachmentArray attachments;
|
||||||
bool hasBoundExternalImages = false;
|
} mRenderPassFboInfo = {};
|
||||||
|
|
||||||
bool hasExternalSamplers() const noexcept {
|
|
||||||
return hasExternalSamplerLayouts && hasBoundExternalImages;
|
|
||||||
}
|
|
||||||
} mAppState;
|
|
||||||
|
|
||||||
bool const mIsSRGBSwapChainSupported;
|
bool const mIsSRGBSwapChainSupported;
|
||||||
backend::StereoscopicType const mStereoscopicType;
|
backend::StereoscopicType const mStereoscopicType;
|
||||||
|
|||||||
@@ -1,280 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "VulkanExternalImageManager.h"
|
|
||||||
|
|
||||||
#include "VulkanDescriptorSetCache.h"
|
|
||||||
#include "VulkanDescriptorSetLayoutCache.h"
|
|
||||||
#include "VulkanSamplerCache.h"
|
|
||||||
#include "VulkanYcbcrConversionCache.h"
|
|
||||||
#include "vulkan/memory/ResourcePointer.h"
|
|
||||||
#include "vulkan/utils/Conversion.h"
|
|
||||||
|
|
||||||
#include <backend/platforms/VulkanPlatform.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void erase(std::vector<T>& v, std::function<bool(T const&)> f) {
|
|
||||||
auto newEnd = std::remove_if(v.begin(), v.end(), f);
|
|
||||||
v.erase(newEnd, v.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous
|
|
||||||
|
|
||||||
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
|
|
||||||
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
|
|
||||||
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
|
|
||||||
: mPlatform(platform),
|
|
||||||
mSamplerCache(samplerCache),
|
|
||||||
mYcbcrConversionCache(ycbcrConversionCache),
|
|
||||||
mDescriptorSetCache(setCache),
|
|
||||||
mDescriptorSetLayoutCache(layoutCache) {
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::terminate() {
|
|
||||||
mSetAndLayouts.clear();
|
|
||||||
mSetBindings.clear();
|
|
||||||
mImages.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::onBeginFrame() {
|
|
||||||
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
|
|
||||||
image.hasBeenValidated = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VulkanExternalImageManager::prepareBindSets(SetArray const& sets) {
|
|
||||||
bool hasUpdated = false;
|
|
||||||
for (auto set: sets) {
|
|
||||||
if (!set) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (auto itr = std::find_if(mSetAndLayouts.begin(), mSetAndLayouts.end(),
|
|
||||||
[&](auto const& setAndLayout) { return setAndLayout.first == set; });
|
|
||||||
itr != mSetAndLayouts.end()) {
|
|
||||||
hasUpdated = updateSetAndLayout(itr->first, itr->second) || hasUpdated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VulkanExternalImageManager::updateSetAndLayout(
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
|
||||||
auto findImage = [&](fvkmemory::resource_ptr<VulkanTexture> texture) -> ImageData* {
|
|
||||||
auto itr = std::find_if(mImages.begin(), mImages.end(), [&](ImageData const& data) {
|
|
||||||
return data.ptr == texture;
|
|
||||||
});
|
|
||||||
assert_invariant(itr != mImages.end());
|
|
||||||
return &(*itr);
|
|
||||||
};
|
|
||||||
|
|
||||||
//std::vector<std::pair<uint8_t, ImageData*>> externalImages;
|
|
||||||
utils::FixedCapacityVector<std::pair<uint8_t, VkSampler>> samplerAndBindings;
|
|
||||||
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
|
|
||||||
|
|
||||||
bool hasImageUpdates = false;
|
|
||||||
for (auto& bindingInfo : mSetBindings) {
|
|
||||||
if (bindingInfo.set != set) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto imageData = findImage(bindingInfo.image);
|
|
||||||
hasImageUpdates = updateImage(imageData) || hasImageUpdates;
|
|
||||||
|
|
||||||
auto samplerParams = bindingInfo.samplerParams;
|
|
||||||
// according to spec, these must match chromaFilter
|
|
||||||
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
|
|
||||||
samplerParams.filterMag = SamplerMagFilter::NEAREST;
|
|
||||||
samplerParams.filterMin = SamplerMinFilter::NEAREST;
|
|
||||||
|
|
||||||
auto sampler = mSamplerCache->getSampler({
|
|
||||||
.sampler = samplerParams,
|
|
||||||
.conversion = imageData->conversion,
|
|
||||||
});
|
|
||||||
samplerAndBindings.push_back({ bindingInfo.binding, sampler });
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to sort by binding number
|
|
||||||
std::sort(samplerAndBindings.begin(), samplerAndBindings.end());
|
|
||||||
|
|
||||||
utils::FixedCapacityVector<VkSampler> outSamplers;
|
|
||||||
outSamplers.reserve(MAX_SAMPLER_COUNT);
|
|
||||||
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
|
|
||||||
[&](auto const& b) { outSamplers.push_back(b.second); });
|
|
||||||
|
|
||||||
VkDescriptorSetLayout const oldLayout = layout->getVkLayout();
|
|
||||||
VkDescriptorSetLayout const newLayout =
|
|
||||||
mDescriptorSetLayoutCache->getVkLayout(layout->bitmask, outSamplers);
|
|
||||||
bool const hasLayoutUpdate = oldLayout != newLayout;
|
|
||||||
layout->setVkLayout(newLayout);
|
|
||||||
|
|
||||||
assert_invariant(
|
|
||||||
(!hasImageUpdates && !hasLayoutUpdate) ||
|
|
||||||
(hasImageUpdates && hasLayoutUpdate));
|
|
||||||
|
|
||||||
if (!hasLayoutUpdate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto foldBitsInHalf = [](auto bitset) {
|
|
||||||
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(bitset) * 4;
|
|
||||||
decltype(bitset) outBitset;
|
|
||||||
bitset.forEachSetBit([&](size_t index) { outBitset.set(index % BITMASK_LOWER_BITS_LEN); });
|
|
||||||
return outBitset;
|
|
||||||
};
|
|
||||||
// We need to build a new descriptor set from the new layout
|
|
||||||
VkDescriptorSet oldSet = set->getVkSet();
|
|
||||||
VkDescriptorSet newSet = mDescriptorSetCache->getVkSet(layout);
|
|
||||||
|
|
||||||
using Bitmask = fvkutils::UniformBufferBitmask;
|
|
||||||
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
|
|
||||||
|
|
||||||
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
|
|
||||||
auto const samplers = layout->bitmask.sampler & (~layout->bitmask.externalSampler);
|
|
||||||
|
|
||||||
// each bitmask denotes a binding index, and separated into two stages - vertex and buffer
|
|
||||||
// We fold the two stages into just the lower half of the bits to denote a combined set of
|
|
||||||
// bindings.
|
|
||||||
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
|
|
||||||
|
|
||||||
// TODO: fix the size for better memory
|
|
||||||
std::vector<VkCopyDescriptorSet> copies;
|
|
||||||
copyBindings.forEachSetBit([&](size_t index) {
|
|
||||||
copies.push_back({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
|
|
||||||
.srcSet = oldSet,
|
|
||||||
.srcBinding = (uint32_t) index,
|
|
||||||
.dstSet = newSet,
|
|
||||||
.dstBinding = (uint32_t) index,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
vkUpdateDescriptorSets(mPlatform->getDevice(), 0, nullptr, copies.size(), copies.data());
|
|
||||||
|
|
||||||
set->setVkSet(newSet);
|
|
||||||
|
|
||||||
// We need to release the vkset, which is no longer used, back into the pool.
|
|
||||||
mDescriptorSetCache->manualRecyle(layout->count, oldLayout, oldSet);
|
|
||||||
|
|
||||||
// We need to update the external samplers in the set
|
|
||||||
for (auto& bindingInfo: mSetBindings) {
|
|
||||||
if (bindingInfo.set != set) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mDescriptorSetCache->updateSampler(set, bindingInfo.binding, bindingInfo.image,
|
|
||||||
VK_NULL_HANDLE);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
|
|
||||||
VulkanPlatform::ExternalImageMetadata const& metadata) {
|
|
||||||
// This external image does not require external sampler (YUV conversion).
|
|
||||||
if (metadata.externalFormat == 0) {
|
|
||||||
return VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
VulkanYcbcrConversionCache::Params ycbcrParams = {
|
|
||||||
.conversion = {
|
|
||||||
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
|
|
||||||
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
|
|
||||||
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
|
|
||||||
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
|
|
||||||
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
|
|
||||||
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
|
|
||||||
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
|
|
||||||
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
|
|
||||||
|
|
||||||
// Unclear where to get the chromaFilter, we just assume it's nearest.
|
|
||||||
.chromaFilter = SamplerMagFilter::NEAREST,
|
|
||||||
},
|
|
||||||
.format = metadata.filamentFormat,
|
|
||||||
.externalFormat = metadata.externalFormat,
|
|
||||||
};
|
|
||||||
return mYcbcrConversionCache->getConversion(ycbcrParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VulkanExternalImageManager::updateImage(ImageData* image) {
|
|
||||||
if (image->hasBeenValidated) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
image->hasBeenValidated = true;
|
|
||||||
|
|
||||||
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
|
|
||||||
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
|
|
||||||
if (vkYcbcr == image->conversion) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
image->ptr->setYcbcrConversion(vkYcbcr, metadata.externalFormat != 0);
|
|
||||||
image->conversion = vkYcbcr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::addDescriptorSet(
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
|
|
||||||
mSetAndLayouts.push_back({set, layout});
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::removeDescriptorSet(
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
|
|
||||||
erase<SetAndLayout>(mSetAndLayouts,
|
|
||||||
[&](auto const& setLayout) { return (setLayout.first == inSet); });
|
|
||||||
erase<SetBindingInfo>(mSetBindings,
|
|
||||||
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::removeDescriptorSetLayout(
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> inLayout) {
|
|
||||||
erase<SetAndLayout>(mSetAndLayouts,
|
|
||||||
[&](auto const& setLayout) { return (setLayout.second == inLayout); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::bindExternallySampledTexture(
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
|
|
||||||
// Should we do duplicate validation here?
|
|
||||||
mSetBindings.push_back({ bindingPoint, image, set, samplerParams });
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::addExternallySampledTexture(
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> image,
|
|
||||||
Platform::ExternalImageHandleRef platformHandleRef) {
|
|
||||||
mImages.push_back({ image, platformHandleRef, false });
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanExternalImageManager::removeExternallySampledTexture(
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> image) {
|
|
||||||
erase<SetBindingInfo>(mSetBindings,
|
|
||||||
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
|
|
||||||
erase<ImageData>(mImages, [&](auto const& imageData) { return imageData.ptr == image; });
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VulkanExternalImageManager::isExternallySampledTexture(
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> image) const {
|
|
||||||
return std::find_if(mImages.begin(), mImages.end(),
|
|
||||||
[&](auto const& imageData) { return imageData.ptr == image; }) != mImages.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namesapce filament::backend
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
|
||||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
|
||||||
|
|
||||||
#include "VulkanHandles.h"
|
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
|
||||||
|
|
||||||
class VulkanYcbcrConversionCache;
|
|
||||||
class VulkanSamplerCache;
|
|
||||||
class VulkanDescriptorSetLayoutCache;
|
|
||||||
class VulkanDescriptorSetCache;
|
|
||||||
|
|
||||||
// Manages the logic of external images and their quirks wrt Vulikan.
|
|
||||||
class VulkanExternalImageManager {
|
|
||||||
public:
|
|
||||||
|
|
||||||
VulkanExternalImageManager(
|
|
||||||
VulkanPlatform* platform,
|
|
||||||
VulkanSamplerCache* samplerCache,
|
|
||||||
VulkanYcbcrConversionCache* ycbcrConversionCache,
|
|
||||||
VulkanDescriptorSetCache* setCache,
|
|
||||||
VulkanDescriptorSetLayoutCache* layoutCache);
|
|
||||||
|
|
||||||
~VulkanExternalImageManager();
|
|
||||||
|
|
||||||
void terminate();
|
|
||||||
|
|
||||||
void onBeginFrame();
|
|
||||||
|
|
||||||
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
|
||||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
|
||||||
|
|
||||||
// This sets the currently bound layouts objects for the pipeline
|
|
||||||
bool prepareBindSets(SetArray const& layouts);
|
|
||||||
|
|
||||||
void addDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
|
||||||
|
|
||||||
void removeDescriptorSetLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
|
||||||
|
|
||||||
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
|
||||||
|
|
||||||
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
|
||||||
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
|
|
||||||
SamplerParams samplerParams);
|
|
||||||
|
|
||||||
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image,
|
|
||||||
Platform::ExternalImageHandleRef platformHandleRef);
|
|
||||||
|
|
||||||
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
|
|
||||||
|
|
||||||
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
|
|
||||||
|
|
||||||
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
|
|
||||||
VulkanPlatform::ExternalImageMetadata const& metadata);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct ImageData {
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> ptr;
|
|
||||||
Platform::ExternalImageHandle platformHandle;
|
|
||||||
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
|
|
||||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
|
||||||
bool updateImage(ImageData* imageData);
|
|
||||||
|
|
||||||
VulkanPlatform* mPlatform;
|
|
||||||
VulkanSamplerCache* mSamplerCache;
|
|
||||||
VulkanYcbcrConversionCache* mYcbcrConversionCache;
|
|
||||||
VulkanDescriptorSetCache* mDescriptorSetCache;
|
|
||||||
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
|
|
||||||
|
|
||||||
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
|
|
||||||
|
|
||||||
struct SetBindingInfo {
|
|
||||||
uint8_t binding = 0;
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> image;
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
|
|
||||||
SamplerParams samplerParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use vectors instead of hash maps because we only expect small number of entries.
|
|
||||||
std::vector<SetAndLayout> mSetAndLayouts;
|
|
||||||
std::vector<SetBindingInfo> mSetBindings;
|
|
||||||
std::vector<ImageData> mImages;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // filament::backend
|
|
||||||
|
|
||||||
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
|
||||||
@@ -340,15 +340,21 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
|||||||
return renderPass;
|
return renderPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanFboCache::reset() noexcept {
|
void VulkanFboCache::resetFramebuffers() noexcept {
|
||||||
for (auto pair : mFramebufferCache) {
|
for (const auto& pair: mFramebufferCache) {
|
||||||
mRenderPassRefCount[pair.first.renderPass]--;
|
mRenderPassRefCount[pair.first.renderPass]--;
|
||||||
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
|
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
|
||||||
}
|
}
|
||||||
mFramebufferCache.clear();
|
mFramebufferCache.clear();
|
||||||
for (auto pair : mRenderPassCache) {
|
}
|
||||||
|
|
||||||
|
void VulkanFboCache::terminate() noexcept {
|
||||||
|
resetFramebuffers();
|
||||||
|
|
||||||
|
for (const auto& pair: mRenderPassCache) {
|
||||||
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
|
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
|
||||||
}
|
}
|
||||||
|
mRenderPassRefCount.clear();
|
||||||
mRenderPassCache.clear();
|
mRenderPassCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,8 +106,11 @@ public:
|
|||||||
// Evicts old unused Vulkan objects. Call this once per frame.
|
// Evicts old unused Vulkan objects. Call this once per frame.
|
||||||
void gc() noexcept;
|
void gc() noexcept;
|
||||||
|
|
||||||
|
// Frees all Framebuffer objects. Call this every time a the swapchain is resized
|
||||||
|
void resetFramebuffers() noexcept;
|
||||||
|
|
||||||
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
|
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
|
||||||
void reset() noexcept;
|
void terminate() noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
|
|||||||
@@ -35,7 +35,12 @@ using namespace bluevk;
|
|||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
|
VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
|
||||||
: mDevice(device) {}
|
: mDevice(device) {
|
||||||
|
VkPipelineCacheCreateInfo createInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||||
|
};
|
||||||
|
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
|
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
|
||||||
mPipelineRequirements.layout = layout;
|
mPipelineRequirements.layout = layout;
|
||||||
@@ -215,7 +220,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
|||||||
PipelineCacheEntry cacheEntry = {
|
PipelineCacheEntry cacheEntry = {
|
||||||
.lastUsed = mCurrentTime,
|
.lastUsed = mCurrentTime,
|
||||||
};
|
};
|
||||||
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
|
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
|
||||||
VKALLOC, &cacheEntry.handle);
|
VKALLOC, &cacheEntry.handle);
|
||||||
assert_invariant(error == VK_SUCCESS);
|
assert_invariant(error == VK_SUCCESS);
|
||||||
if (error != VK_SUCCESS) {
|
if (error != VK_SUCCESS) {
|
||||||
@@ -271,6 +276,8 @@ void VulkanPipelineCache::terminate() noexcept {
|
|||||||
}
|
}
|
||||||
mPipelines.clear();
|
mPipelines.clear();
|
||||||
mBoundPipeline = {};
|
mBoundPipeline = {};
|
||||||
|
|
||||||
|
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanPipelineCache::gc() noexcept {
|
void VulkanPipelineCache::gc() noexcept {
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ private:
|
|||||||
// Immutable state.
|
// Immutable state.
|
||||||
VkDevice mDevice = VK_NULL_HANDLE;
|
VkDevice mDevice = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
// Vuklan Driver pipeline cache handle. In the cases a pipeline has been evicted by the `gc`,
|
||||||
|
// recreating the same pipeline is cheaper, helping with frame stalling.
|
||||||
|
VkPipelineCache mPipelineCache = VK_NULL_HANDLE;
|
||||||
|
|
||||||
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
|
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
|
||||||
PipelineKey mPipelineRequirements = {};
|
PipelineKey mPipelineRequirements = {};
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#include "VulkanConstants.h"
|
#include "VulkanConstants.h"
|
||||||
#include "vulkan/utils/Conversion.h"
|
#include "vulkan/utils/Conversion.h"
|
||||||
#include "vulkan/vulkan_core.h"
|
|
||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
|
|||||||
@@ -132,10 +132,6 @@ public:
|
|||||||
return id() == other.id() && type() == other.type();
|
return id() == other.id() && type() == other.type();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator!=(resource_ptr<D> const& other) const {
|
|
||||||
return !((*this) == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline explicit operator bool() const {
|
inline explicit operator bool() const {
|
||||||
return bool(mRef);
|
return bool(mRef);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -993,6 +993,30 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
|
|||||||
return mImpl->mProtectedGraphicsQueue;
|
return mImpl->mProtectedGraphicsQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
|
||||||
|
ExternalImageHandleRef externalImage) {
|
||||||
|
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
|
||||||
|
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||||
|
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||||
|
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
|
||||||
|
usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
|
||||||
|
SamplerParams sampler, uint32_t internalFormat) {
|
||||||
|
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
|
||||||
|
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||||
|
VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||||
|
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
|
||||||
|
viewType, swizzle);
|
||||||
|
}
|
||||||
|
|
||||||
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
|
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
|
||||||
return getSwapchainInstanceExtensionsImpl();
|
return getSwapchainInstanceExtensionsImpl();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
#include <backend/platforms/VulkanPlatformAndroid.h>
|
#include <backend/platforms/VulkanPlatformAndroid.h>
|
||||||
|
|
||||||
#include "vulkan/VulkanConstants.h"
|
|
||||||
#include "vulkan/VulkanContext.h"
|
|
||||||
#include "vulkan/vulkan_core.h"
|
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <private/backend/BackendUtilsAndroid.h>
|
#include <private/backend/BackendUtilsAndroid.h>
|
||||||
|
|
||||||
|
#include "vulkan/VulkanConstants.h"
|
||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
#include "vulkan/utils/Image.h"
|
||||||
|
#include "vulkan/utils/Conversion.h"
|
||||||
|
|
||||||
#include <bluevk/BlueVK.h>
|
#include <bluevk/BlueVK.h>
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ VkFormat transformVkFormat(VkFormat format, bool sRGB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isProtectedFromUsage(uint64_t usage) {
|
bool isProtectedFromUsage(uint64_t usage) {
|
||||||
return usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
|
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
||||||
@@ -120,9 +120,7 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
|||||||
usage = 0;
|
usage = 0;
|
||||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
|
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
|
||||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||||
// We shouldn't be using external samplers as input attachments
|
|
||||||
// usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
|
||||||
}
|
}
|
||||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
|
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
|
||||||
if (isDepthFormat) {
|
if (isDepthFormat) {
|
||||||
@@ -138,38 +136,87 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
|||||||
return { format, usage };
|
return { format, usage };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
|
||||||
bool sRGB) {
|
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
|
||||||
auto const format = mapToFilamentFormat(desc.format, sRGB);
|
VkImageUsageFlags usage) {
|
||||||
return {
|
VulkanPlatform::ImageData data;
|
||||||
format,
|
|
||||||
mapToFilamentUsage(desc.usage, format),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// if external format we need to specifiy it in the allocation
|
||||||
|
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
||||||
|
|
||||||
|
const VkExternalFormatANDROID externalFormat = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||||
|
.pNext = nullptr,
|
||||||
|
// pass down the format (external means we don't have it VK defined)
|
||||||
|
.externalFormat = metadata.externalFormat,
|
||||||
|
};
|
||||||
|
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||||
|
.pNext = useExternalFormat ? &externalFormat : nullptr,
|
||||||
|
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||||
|
imageInfo.pNext = &externalCreateInfo;
|
||||||
|
imageInfo.format = metadata.format;
|
||||||
|
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageInfo.extent = {
|
||||||
|
metadata.width,
|
||||||
|
metadata.height,
|
||||||
|
1u,
|
||||||
|
};
|
||||||
|
imageInfo.mipLevels = 1;
|
||||||
|
imageInfo.arrayLayers = metadata.layers;
|
||||||
|
imageInfo.samples = metadata.samples;
|
||||||
|
imageInfo.usage = usage;
|
||||||
|
|
||||||
|
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
|
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
||||||
|
|
||||||
|
// Allocate the memory
|
||||||
|
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.buffer = buffer,
|
||||||
|
};
|
||||||
|
VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||||
|
.pNext = &androidHardwareBufferInfo,
|
||||||
|
.image = data.first,
|
||||||
|
.buffer = VK_NULL_HANDLE,
|
||||||
|
};
|
||||||
|
VkMemoryAllocateInfo allocInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||||
|
.pNext = &memoryDedicatedAllocateInfo,
|
||||||
|
.allocationSize = metadata.allocationSize,
|
||||||
|
.memoryTypeIndex = memoryTypeIndex,
|
||||||
|
};
|
||||||
|
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
|
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
}// namespace
|
}// namespace
|
||||||
|
|
||||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
|
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
|
||||||
if (__builtin_available(android 26, *)) {
|
|
||||||
if (aHardwareBuffer) {
|
|
||||||
AHardwareBuffer_release(aHardwareBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||||
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||||
if (__builtin_available(android 26, *)) {
|
if (__builtin_available(android 26, *)) {
|
||||||
auto bufferImpl = const_cast<AHardwareBuffer*>(buffer);
|
|
||||||
AHardwareBuffer_acquire(bufferImpl);
|
|
||||||
|
|
||||||
AHardwareBuffer_Desc hardwareBufferDescription = {};
|
AHardwareBuffer_Desc hardwareBufferDescription = {};
|
||||||
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
|
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
|
||||||
|
|
||||||
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
|
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
|
||||||
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
||||||
p->sRGB = sRGB;
|
p->sRGB = sRGB;
|
||||||
|
p->height = hardwareBufferDescription.height;
|
||||||
|
p->width = hardwareBufferDescription.width;
|
||||||
|
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
|
||||||
|
p->format = textureFormat;
|
||||||
|
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
|
||||||
return Platform::ExternalImageHandle{ p };
|
return Platform::ExternalImageHandle{ p };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,20 +225,23 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
|||||||
|
|
||||||
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
|
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
|
||||||
ExternalImageHandleRef externalImage) const noexcept {
|
ExternalImageHandleRef externalImage) const noexcept {
|
||||||
auto metadata = extractExternalImageMetadata(externalImage);
|
auto const* fvkExternalImage =
|
||||||
|
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.width = metadata.width,
|
.width = fvkExternalImage->width,
|
||||||
.height = metadata.height,
|
.height = fvkExternalImage->height,
|
||||||
.format = metadata.filamentFormat,
|
.format = fvkExternalImage->format,
|
||||||
.usage = metadata.filamentUsage,
|
.usage = fvkExternalImage->usage,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImageMetadata(
|
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
|
||||||
ExternalImageHandleRef image) const {
|
ExternalImageHandleRef externalImage) {
|
||||||
auto const* fvkExternalImage = static_cast<ExternalImageVulkanAndroid const*>(image.get());
|
auto const* fvkExternalImage =
|
||||||
|
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||||
|
|
||||||
ExternalImageMetadata metadata = {};
|
ExternalImageMetadata metadata;
|
||||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
||||||
if (__builtin_available(android 26, *)) {
|
if (__builtin_available(android 26, *)) {
|
||||||
AHardwareBuffer_Desc bufferDesc;
|
AHardwareBuffer_Desc bufferDesc;
|
||||||
@@ -199,30 +249,16 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImag
|
|||||||
metadata.width = bufferDesc.width;
|
metadata.width = bufferDesc.width;
|
||||||
metadata.height = bufferDesc.height;
|
metadata.height = bufferDesc.height;
|
||||||
metadata.layers = bufferDesc.layers;
|
metadata.layers = bufferDesc.layers;
|
||||||
|
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
|
||||||
std::tie(metadata.format, metadata.usage) =
|
std::tie(metadata.format, metadata.usage) =
|
||||||
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||||
std::tie(metadata.filamentFormat, metadata.filamentUsage) =
|
|
||||||
getFilamentFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
|
||||||
|
|
||||||
if (isProtectedFromUsage(bufferDesc.usage)) {
|
|
||||||
metadata.filamentUsage |= TextureUsage::PROTECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: The following seems unnecessary. we should be able to discern directly from the
|
|
||||||
// bufferDesc.
|
|
||||||
if (any(metadata.filamentUsage & TextureUsage::BLIT_SRC)) {
|
|
||||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (any(metadata.filamentUsage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
|
||||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
|
|
||||||
|
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
|
||||||
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
|
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
|
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
|
||||||
|
.pNext = nullptr,
|
||||||
};
|
};
|
||||||
VkAndroidHardwareBufferPropertiesANDROID properties = {
|
VkAndroidHardwareBufferPropertiesANDROID properties = {
|
||||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
|
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
|
||||||
@@ -232,116 +268,138 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImag
|
|||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
<< "vkGetAndroidHardwareBufferProperties failed with error="
|
<< "vkGetAndroidHardwareBufferProperties failed with error="
|
||||||
<< static_cast<int32_t>(result);
|
<< static_cast<int32_t>(result);
|
||||||
|
|
||||||
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
|
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
|
||||||
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
|
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
|
||||||
<< "mismatched image format( " << metadata.format << ") and queried format("
|
<< "mismatched image format( " << metadata.format << ") and queried format("
|
||||||
<< bufferPropertiesFormat << ") for external image (AHB)";
|
<< bufferPropertiesFormat << ") for external image (AHB)";
|
||||||
metadata.externalFormat = formatInfo.externalFormat;
|
metadata.externalFormat = formatInfo.externalFormat;
|
||||||
|
|
||||||
// Choose either externalFormat > 0 or metadata.format and prefer the latter.
|
|
||||||
if (metadata.externalFormat > 0 && metadata.format != VK_FORMAT_UNDEFINED) {
|
|
||||||
// See VUID-VkImageCreateInfo-pNext-09457
|
|
||||||
metadata.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
||||||
metadata.externalFormat = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata.allocationSize = properties.allocationSize;
|
metadata.allocationSize = properties.allocationSize;
|
||||||
metadata.memoryTypeBits = properties.memoryTypeBits;
|
metadata.memoryTypeBits = properties.memoryTypeBits;
|
||||||
|
|
||||||
metadata.ycbcrConversionComponents = formatInfo.samplerYcbcrConversionComponents;
|
|
||||||
metadata.ycbcrModel = formatInfo.suggestedYcbcrModel;
|
|
||||||
metadata.ycbcrRange = formatInfo.suggestedYcbcrRange;
|
|
||||||
metadata.xChromaOffset = formatInfo.suggestedXChromaOffset;
|
|
||||||
metadata.yChromaOffset = formatInfo.suggestedYChromaOffset;
|
|
||||||
|
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
|
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
|
||||||
ExternalImageHandleRef externalImage) const {
|
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||||
auto const& metadata = extractExternalImageMetadata(externalImage);
|
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||||
|
|
||||||
auto const* fvkExternalImage =
|
auto const* fvkExternalImage =
|
||||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
|
||||||
|
memoryTypeIndex, usage);
|
||||||
|
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
|
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
// if external format we need to specifiy it in the allocation
|
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||||
bool const useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||||
|
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||||
VkExternalFormatANDROID const externalFormat = {
|
VkExternalFormatANDROID externalFormat = {
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||||
.pNext = nullptr,
|
.externalFormat = internalFormat,
|
||||||
.externalFormat = metadata.externalFormat,
|
|
||||||
};
|
|
||||||
VkExternalMemoryImageCreateInfo const externalCreateInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
|
||||||
.pNext = useExternalFormat ? &externalFormat : nullptr,
|
|
||||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VkImageCreateInfo const imageInfo{
|
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||||
.pNext = &externalCreateInfo,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||||
.flags = useExternalFormat ? VK_IMAGE_CREATE_ALIAS_BIT : 0u,
|
.pNext = &externalFormat,
|
||||||
.imageType = VK_IMAGE_TYPE_2D,
|
.format = VK_FORMAT_UNDEFINED,
|
||||||
.format = metadata.format,
|
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||||
.extent = {
|
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||||
metadata.width,
|
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||||
metadata.height,
|
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||||
1u,
|
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||||
},
|
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = metadata.layers,
|
|
||||||
.samples = metadata.samples,
|
|
||||||
.usage = metadata.usage,
|
|
||||||
};
|
};
|
||||||
|
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||||
VkDevice const device = getDevice();
|
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||||
|
nullptr, &conversion);
|
||||||
VkImage image;
|
|
||||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
<< "Unable to create Ycbcr Conversion."
|
||||||
|
<< " error=" << static_cast<int32_t>(result);
|
||||||
|
|
||||||
// Allocate the memory
|
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||||
VkImportAndroidHardwareBufferInfoANDROID const androidHardwareBufferInfo = {
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
.conversion = conversion,
|
||||||
.buffer = buffer,
|
|
||||||
};
|
};
|
||||||
VkMemoryDedicatedAllocateInfo const memoryDedicatedAllocateInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
VkImageViewCreateInfo viewInfo = {
|
||||||
.pNext = &androidHardwareBufferInfo,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
|
.pNext = &samplerYcbcrConversionInfo,
|
||||||
|
.flags = 0,
|
||||||
.image = image,
|
.image = image,
|
||||||
.buffer = VK_NULL_HANDLE,
|
.viewType = viewType,
|
||||||
|
.format = VK_FORMAT_UNDEFINED,
|
||||||
|
.components = swizzle,
|
||||||
|
.subresourceRange = range,
|
||||||
|
};
|
||||||
|
VkImageView imageView;
|
||||||
|
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
|
<< "Unable to create VkImageView."
|
||||||
|
<< " error=" << static_cast<int32_t>(result);
|
||||||
|
|
||||||
|
return imageView;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler VulkanPlatform::createExternalSamplerImpl(
|
||||||
|
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
|
||||||
|
uint32_t internalFormat) {
|
||||||
|
VkExternalFormatANDROID externalFormat = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||||
|
.externalFormat = internalFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||||
vkGetPhysicalDeviceMemoryProperties(getPhysicalDevice(), &memoryProperties);
|
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||||
|
.pNext = &externalFormat,
|
||||||
VkMemoryPropertyFlags const requiredMemoryFlags =
|
.format = VK_FORMAT_UNDEFINED,
|
||||||
any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
|
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||||
|
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||||
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
|
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||||
metadata.memoryTypeBits, requiredMemoryFlags);
|
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||||
|
|
||||||
VkMemoryAllocateInfo const allocInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
||||||
.pNext = &memoryDedicatedAllocateInfo,
|
|
||||||
.allocationSize = metadata.allocationSize,
|
|
||||||
.memoryTypeIndex = memoryTypeIndex,
|
|
||||||
};
|
};
|
||||||
VkDeviceMemory memory;
|
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &memory);
|
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||||
|
nullptr, &conversion);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
<< "Unable to create Ycbcr Conversion."
|
||||||
|
<< " error=" << static_cast<int32_t>(result);
|
||||||
|
|
||||||
result = vkBindImageMemory(getDevice(), image, memory, 0);
|
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.conversion = conversion,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkSamplerCreateInfo samplerInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
.pNext = &samplerYcbcrConversionInfo,
|
||||||
|
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||||
|
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||||
|
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||||
|
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||||
|
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||||
|
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||||
|
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||||
|
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||||
|
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||||
|
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||||
|
.minLod = 0.0f,
|
||||||
|
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||||
|
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||||
|
.unnormalizedCoordinates = VK_FALSE,
|
||||||
|
};
|
||||||
|
VkSampler sampler;
|
||||||
|
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
<< "Unable to create sampler."
|
||||||
return { image, memory };
|
<< " error=" << static_cast<int32_t>(result);
|
||||||
|
return sampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
|
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
|
||||||
@@ -369,9 +427,20 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
|
|||||||
// Deprecated platform dependent helper methods
|
// Deprecated platform dependent helper methods
|
||||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
|
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
|
||||||
|
|
||||||
|
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||||
|
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||||
|
return ExternalImageMetadata{};
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||||
|
ExternalImageHandleRef externalImage, VkDevice device,
|
||||||
|
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||||
|
return ImageData{};
|
||||||
|
}
|
||||||
|
|
||||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
||||||
VkInstance instance, uint64_t flags) noexcept {
|
VkInstance instance, uint64_t flags) noexcept {
|
||||||
return SurfaceBundle{};
|
return SurfaceBundle{};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -24,28 +24,12 @@
|
|||||||
#include <bluevk/BlueVK.h>
|
#include <bluevk/BlueVK.h>
|
||||||
|
|
||||||
// Platform specific includes and defines
|
// Platform specific includes and defines
|
||||||
#if defined(__APPLE__)
|
#include <Cocoa/Cocoa.h>
|
||||||
#include <Cocoa/Cocoa.h>
|
#import <Metal/Metal.h>
|
||||||
#import <Metal/Metal.h>
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
|
||||||
|
|
||||||
#ifndef VK_MVK_macos_surface
|
#ifndef VK_MVK_macos_surface
|
||||||
#error VK_MVK_macos_surface is not defined
|
#error VK_MVK_macos_surface is not defined
|
||||||
#endif
|
|
||||||
#elif defined(FILAMENT_IOS)
|
|
||||||
// Metal is not available when building for the iOS simulator on Desktop.
|
|
||||||
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
|
|
||||||
#if METAL_AVAILABLE
|
|
||||||
#import <Metal/Metal.h>
|
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef VK_MVK_ios_surface
|
|
||||||
#error VK_MVK_ios_surface is not defined
|
|
||||||
#endif
|
|
||||||
#define METALVIEW_TAG 255
|
|
||||||
#else
|
|
||||||
#error Not a supported Apple + Vulkan platform
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace bluevk;
|
using namespace bluevk;
|
||||||
@@ -54,48 +38,52 @@ namespace filament::backend {
|
|||||||
|
|
||||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
||||||
ExtensionSet const ret = {
|
ExtensionSet const ret = {
|
||||||
#if defined(__APPLE__)
|
|
||||||
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
|
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
|
||||||
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
|
|
||||||
VK_MVK_IOS_SURFACE_EXTENSION_NAME,
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||||
|
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||||
|
ExternalImageHandleRef externalImage, VkDevice device,
|
||||||
|
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||||
|
SamplerYcbcrConversion chroma,
|
||||||
|
SamplerParams sampler,
|
||||||
|
uint32_t internalFormat) {
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||||
|
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||||
|
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
||||||
VkInstance instance, uint64_t flags) noexcept {
|
VkInstance instance, uint64_t flags) noexcept {
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
#if defined(__APPLE__)
|
NSView* nsview = (__bridge NSView*) nativeWindow;
|
||||||
NSView* nsview = (__bridge NSView*) nativeWindow;
|
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
|
||||||
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
|
|
||||||
|
|
||||||
// Create the VkSurface.
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
|
||||||
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
|
||||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
|
||||||
createInfo.pView = (__bridge void*) nsview;
|
|
||||||
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
|
||||||
(VkSurfaceKHR*) &surface);
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
|
||||||
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
|
|
||||||
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
|
|
||||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
|
||||||
// Create the VkSurface.
|
// Create the VkSurface.
|
||||||
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
|
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
||||||
<< "Unable to load vkCreateIOSSurfaceMVK function.";
|
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
||||||
VkIOSSurfaceCreateInfoMVK createInfo = {};
|
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||||
createInfo.pNext = NULL;
|
createInfo.pView = (__bridge void*) nsview;
|
||||||
createInfo.flags = 0;
|
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||||
createInfo.pView = metalLayer;
|
|
||||||
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
|
||||||
(VkSurfaceKHR*) &surface);
|
(VkSurfaceKHR*) &surface);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
|
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
|
||||||
#endif
|
return std::make_tuple(surface, VkExtent2D{});
|
||||||
return std::make_tuple(surface, VkExtent2D{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -84,6 +84,30 @@ using namespace bluevk;
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||||
|
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||||
|
ExternalImageHandleRef externalImage, VkDevice device,
|
||||||
|
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||||
|
SamplerYcbcrConversion chroma,
|
||||||
|
SamplerParams sampler,
|
||||||
|
uint32_t internalFormat) {
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||||
|
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||||
|
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
||||||
VulkanPlatform::ExtensionSet const ret = {
|
VulkanPlatform::ExtensionSet const ret = {
|
||||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WebGPUHandles.h"
|
||||||
|
|
||||||
|
#include "WebGPUConstants.h"
|
||||||
|
|
||||||
|
#include "DriverBase.h"
|
||||||
|
#include <backend/DriverEnums.h>
|
||||||
|
#include <backend/Program.h>
|
||||||
|
|
||||||
|
#include <utils/Panic.h>
|
||||||
|
#include <utils/ostream.h>
|
||||||
|
|
||||||
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::string_view toString(ShaderStage stage) {
|
||||||
|
switch (stage) {
|
||||||
|
case ShaderStage::VERTEX:
|
||||||
|
return "vertex";
|
||||||
|
case ShaderStage::FRAGMENT:
|
||||||
|
return "fragment";
|
||||||
|
case ShaderStage::COMPUTE:
|
||||||
|
return "compute";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, const char* programName,
|
||||||
|
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
|
||||||
|
shaderSource,
|
||||||
|
ShaderStage stage) {
|
||||||
|
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
|
||||||
|
shaderSource[static_cast<size_t>(stage)];
|
||||||
|
if (sourceBytes.empty()) {
|
||||||
|
return nullptr;// nothing to compile, the shader was not provided
|
||||||
|
}
|
||||||
|
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||||
|
wgslDescriptor.code = wgpu::StringView(reinterpret_cast<const char*>(sourceBytes.data()));
|
||||||
|
std::stringstream labelStream;
|
||||||
|
labelStream << programName << " " << toString(stage) << " shader";
|
||||||
|
auto label = labelStream.str();
|
||||||
|
wgpu::ShaderModuleDescriptor descriptor{
|
||||||
|
.nextInChain = &wgslDescriptor,
|
||||||
|
.label = label.data()
|
||||||
|
};
|
||||||
|
wgpu::ShaderModule module = device.CreateShaderModule(&descriptor);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(module != nullptr) << "Failed to create " << descriptor.label;
|
||||||
|
module.GetCompilationInfo(wgpu::CallbackMode::AllowSpontaneous,
|
||||||
|
[&descriptor](auto const& status, wgpu::CompilationInfo const* info) {
|
||||||
|
switch (status) {
|
||||||
|
case wgpu::CompilationInfoRequestStatus::CallbackCancelled:
|
||||||
|
FWGPU_LOGW << "Shader compilation info callback cancelled for "
|
||||||
|
<< descriptor.label << "?" << utils::io::endl;
|
||||||
|
return;
|
||||||
|
case wgpu::CompilationInfoRequestStatus::Success:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (info != nullptr) {
|
||||||
|
std::stringstream errorStream;
|
||||||
|
int errorCount = 0;
|
||||||
|
for (size_t msgIndex = 0; msgIndex < info->messageCount; msgIndex++) {
|
||||||
|
wgpu::CompilationMessage const& message = info->messages[msgIndex];
|
||||||
|
switch (message.type) {
|
||||||
|
case wgpu::CompilationMessageType::Info:
|
||||||
|
FWGPU_LOGI << descriptor.label << ": " << message.message
|
||||||
|
<< " line#:" << message.lineNum
|
||||||
|
<< " linePos:" << message.linePos
|
||||||
|
<< " offset:" << message.offset
|
||||||
|
<< " length:" << message.length << utils::io::endl;
|
||||||
|
break;
|
||||||
|
case wgpu::CompilationMessageType::Warning:
|
||||||
|
FWGPU_LOGW << "Warning compiling " << descriptor.label << ": "
|
||||||
|
<< message.message << " line#:" << message.lineNum
|
||||||
|
<< " linePos:" << message.linePos
|
||||||
|
<< " offset:" << message.offset
|
||||||
|
<< " length:" << message.length << utils::io::endl;
|
||||||
|
break;
|
||||||
|
case wgpu::CompilationMessageType::Error:
|
||||||
|
errorCount++;
|
||||||
|
errorStream << "Error " << errorCount << " : "
|
||||||
|
<< std::string_view(message.message)
|
||||||
|
<< " line#:" << message.lineNum
|
||||||
|
<< " linePos:" << message.linePos
|
||||||
|
<< " offset:" << message.offset
|
||||||
|
<< " length:" << message.length << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(errorCount < 1)
|
||||||
|
<< errorCount << " error(s) compiling " << descriptor.label << ":\n"
|
||||||
|
<< errorStream.str();
|
||||||
|
}
|
||||||
|
FWGPU_LOGD << descriptor.label << " compiled successfully" << utils::io::endl;
|
||||||
|
});
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<wgpu::ConstantEntry> convertConstants(
|
||||||
|
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
|
||||||
|
constantsInfo) {
|
||||||
|
std::vector<wgpu::ConstantEntry> constants(constantsInfo.size());
|
||||||
|
for (size_t i = 0; i < constantsInfo.size(); i++) {
|
||||||
|
filament::backend::Program::SpecializationConstant const& specConstant = constantsInfo[i];
|
||||||
|
wgpu::ConstantEntry& constantEntry = constants[i];
|
||||||
|
constantEntry.key = wgpu::StringView(std::to_string(specConstant.id));
|
||||||
|
if (auto* v = std::get_if<int32_t>(&specConstant.value)) {
|
||||||
|
constantEntry.value = static_cast<double>(*v);
|
||||||
|
} else if (auto* f = std::get_if<float>(&specConstant.value)) {
|
||||||
|
constantEntry.value = static_cast<double>(*f);
|
||||||
|
} else if (auto* b = std::get_if<bool>(&specConstant.value)) {
|
||||||
|
constantEntry.value = *b ? 0.0 : 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
|
||||||
|
: HwProgram(program.getName()),
|
||||||
|
vertexShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
||||||
|
ShaderStage::VERTEX)),
|
||||||
|
fragmentShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
||||||
|
ShaderStage::FRAGMENT)),
|
||||||
|
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
||||||
|
ShaderStage::COMPUTE)),
|
||||||
|
constants(convertConstants(program.getSpecializationConstants())) {}
|
||||||
|
|
||||||
|
}// namespace filament::backend
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "webgpu/WebGPUDriver.h"
|
#include "webgpu/WebGPUDriver.h"
|
||||||
|
|
||||||
|
#include "WebGPUSwapChain.h"
|
||||||
#include "webgpu/WebGPUConstants.h"
|
#include "webgpu/WebGPUConstants.h"
|
||||||
#include <backend/platforms/WebGPUPlatform.h>
|
#include <backend/platforms/WebGPUPlatform.h>
|
||||||
|
|
||||||
@@ -227,6 +228,14 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
|
|||||||
driverConfig.disableHeapHandleTags) {
|
driverConfig.disableHeapHandleTags) {
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
printInstanceDetails(mPlatform.getInstance());
|
printInstanceDetails(mPlatform.getInstance());
|
||||||
|
#endif
|
||||||
|
mAdapter = mPlatform.requestAdapter(nullptr);
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
printAdapterDetails(mAdapter);
|
||||||
|
#endif
|
||||||
|
mDevice = mPlatform.requestDevice(mAdapter);
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
printDeviceDetails(mDevice);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +265,7 @@ void WebGPUDriver::terminate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::tick(int) {
|
void WebGPUDriver::tick(int) {
|
||||||
|
mDevice.Tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||||
@@ -285,15 +295,27 @@ void WebGPUDriver::finish(int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||||
|
if (rph) {
|
||||||
|
destructHandle<WGPURenderPrimitive>(rph);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||||
|
if (vbih) {
|
||||||
|
destructHandle<WGPUVertexBufferInfo>(vbih);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||||
|
if (vbh) {
|
||||||
|
destructHandle<WGPUVertexBuffer>(vbh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||||
|
if (ibh) {
|
||||||
|
destructHandle<WGPUIndexBuffer>(ibh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||||
@@ -303,19 +325,19 @@ void WebGPUDriver::destroyTexture(Handle<HwTexture> th) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
|
void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
|
||||||
|
if (ph) {
|
||||||
|
destructHandle<WGPUProgram>(ph);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
||||||
|
if (sch) {
|
||||||
|
destructHandle<WebGPUSwapChain>(sch);
|
||||||
|
}
|
||||||
mSwapChain = nullptr;
|
mSwapChain = nullptr;
|
||||||
// TODO: use webgpu handle allocator from
|
|
||||||
// https://github.com/google/filament/pull/8566
|
|
||||||
// if (sch) {
|
|
||||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
|
||||||
// destruct(sch, hwSwapChain);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
|
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
|
||||||
@@ -325,16 +347,16 @@ void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
|
void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
|
||||||
|
if (tqh) {
|
||||||
|
destructHandle<WebGPUDescriptorSetLayout>(tqh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
||||||
// TODO: use webgpu handle allocator from.
|
return allocHandle<WebGPUSwapChain>();
|
||||||
// https://github.com/google/filament/pull/8566
|
|
||||||
// return allocAndConstructHandle<HwSwapChain>();
|
|
||||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||||
@@ -342,7 +364,7 @@ Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||||
@@ -350,7 +372,7 @@ Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||||
return Handle<HwProgram>((Handle<HwProgram>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUProgram>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
||||||
@@ -370,15 +392,15 @@ Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||||
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUBufferObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPURenderTarget>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
||||||
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUVertexBuffer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||||
@@ -386,11 +408,11 @@ Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
||||||
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPURenderPrimitive>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||||
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUVertexBufferInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||||
@@ -398,12 +420,11 @@ Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPURenderTarget>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
||||||
return Handle<HwDescriptorSetLayout>(
|
return allocHandle<WebGPUDescriptorSetLayout>();
|
||||||
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||||
@@ -419,21 +440,15 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||||
// TODO: use webgpu handle allocator from.
|
mNativeWindow = nativeWindow;
|
||||||
// https://github.com/google/filament/pull/8566
|
assert_invariant(!mSwapChain);
|
||||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
|
||||||
mSwapChain = nullptr;
|
|
||||||
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||||
mAdapter = mPlatform.requestAdapter(surface);
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
printAdapterDetails(mAdapter);
|
|
||||||
#endif
|
|
||||||
mDevice = mPlatform.requestDevice(mAdapter);
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
printDeviceDetails(mDevice);
|
|
||||||
#endif
|
|
||||||
mQueue = mDevice.GetQueue();
|
mQueue = mDevice.GetQueue();
|
||||||
mSwapChain = std::make_unique<WebGPUSwapChain>(std::move(surface), mAdapter, mDevice, flags);
|
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||||
|
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||||
|
mDevice, flags);
|
||||||
|
assert_invariant(mSwapChain);
|
||||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||||
"background components have been instantiated/selected, such as surface/screen, "
|
"background components have been instantiated/selected, such as surface/screen, "
|
||||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||||
@@ -446,9 +461,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
|||||||
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
||||||
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
||||||
#endif
|
#endif
|
||||||
// TODO: use webgpu handle allocator from.
|
|
||||||
// https://github.com/google/filament/pull/8566
|
|
||||||
// hwSwapChain->swapChain = mSwapChain.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||||
@@ -496,9 +508,15 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType
|
|||||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
|
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
|
||||||
|
|
||||||
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
|
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||||
|
constructHandle<WGPUProgram>(ph, mDevice, program);
|
||||||
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
|
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||||
|
assert_invariant(!mDefaultRenderTarget);
|
||||||
|
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
|
||||||
|
assert_invariant(mDefaultRenderTarget);
|
||||||
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||||
@@ -509,7 +527,9 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
|||||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||||
|
|
||||||
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||||
backend::DescriptorSetLayout&& info) {}
|
backend::DescriptorSetLayout&& info) {
|
||||||
|
constructHandle<WebGPUDescriptorSetLayout>(dslh, std::move(info), mDevice);
|
||||||
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||||
Handle<HwDescriptorSetLayout> dslh) {}
|
Handle<HwDescriptorSetLayout> dslh) {}
|
||||||
@@ -659,6 +679,10 @@ void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
|
|||||||
|
|
||||||
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||||
Handle<HwBufferObject> boh) {
|
Handle<HwBufferObject> boh) {
|
||||||
|
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
|
||||||
|
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
|
||||||
|
assert_invariant(index < vertexBuffer->buffers.size());
|
||||||
|
vertexBuffer->setBuffer(bufferObject, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
||||||
@@ -691,18 +715,71 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||||
|
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||||
|
.label = "command_encoder"
|
||||||
|
};
|
||||||
|
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||||
|
assert_invariant(mCommandEncoder);
|
||||||
|
// TODO: Remove this code once WebGPU pipeline is implemented
|
||||||
|
static float red = 1.0f;
|
||||||
|
if (red - 0.01 > 0) {
|
||||||
|
red -= 0.01;
|
||||||
|
} else {
|
||||||
|
red = 1.0f;
|
||||||
|
}
|
||||||
|
assert_invariant(mTextureView);
|
||||||
|
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
|
||||||
|
.view = mTextureView,
|
||||||
|
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
|
||||||
|
.depthSlice = wgpu::kDepthSliceUndefined,
|
||||||
|
.loadOp = wgpu::LoadOp::Clear,
|
||||||
|
.storeOp = wgpu::StoreOp::Store,
|
||||||
|
.clearValue = wgpu::Color{red, 0 , 0 , 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
wgpu::RenderPassDescriptor renderPassDescriptor = {
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.colorAttachments = &renderPassColorAttachment,
|
||||||
|
.depthStencilAttachment = nullptr,
|
||||||
|
.timestampWrites = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
|
||||||
|
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::endRenderPass(int) {
|
void WebGPUDriver::endRenderPass(int) {
|
||||||
|
mRenderPassEncoder.End();
|
||||||
|
mRenderPassEncoder = nullptr;
|
||||||
|
wgpu::CommandBufferDescriptor commandBufferDescriptor {
|
||||||
|
.label = "command_buffer",
|
||||||
|
};
|
||||||
|
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
|
||||||
|
assert_invariant(mCommandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::nextSubpass(int) {
|
void WebGPUDriver::nextSubpass(int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||||
|
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
|
||||||
|
"WebGPU driver does not support distinct draw/read swap chains.");
|
||||||
|
auto* swapChain = handleCast<WebGPUSwapChain>(drawSch);
|
||||||
|
mSwapChain = swapChain;
|
||||||
|
assert_invariant(mSwapChain);
|
||||||
|
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||||
|
mTextureView = mSwapChain->getCurrentSurfaceTextureView(surfaceSize);
|
||||||
|
assert_invariant(mTextureView);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||||
|
mCommandEncoder = nullptr;
|
||||||
|
mQueue.Submit(1, &mCommandBuffer);
|
||||||
|
mCommandBuffer = nullptr;
|
||||||
|
mTextureView = nullptr;
|
||||||
|
assert_invariant(mSwapChain);
|
||||||
|
mSwapChain->present();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||||
@@ -730,7 +807,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> src,
|
|||||||
scheduleDestroy(std::move(p));
|
scheduleDestroy(std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> boh,
|
||||||
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
||||||
scheduleDestroy(std::move(p));
|
scheduleDestroy(std::move(p));
|
||||||
}
|
}
|
||||||
@@ -782,22 +859,22 @@ void WebGPUDriver::resetState(int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::updateDescriptorSetBuffer(
|
void WebGPUDriver::updateDescriptorSetBuffer(
|
||||||
backend::DescriptorSetHandle dsh,
|
Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_binding_t binding,
|
backend::descriptor_binding_t binding,
|
||||||
backend::BufferObjectHandle boh,
|
Handle<HwBufferObject> boh,
|
||||||
uint32_t offset,
|
uint32_t offset,
|
||||||
uint32_t size) {
|
uint32_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::updateDescriptorSetTexture(
|
void WebGPUDriver::updateDescriptorSetTexture(
|
||||||
backend::DescriptorSetHandle dsh,
|
Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_binding_t binding,
|
backend::descriptor_binding_t binding,
|
||||||
backend::TextureHandle th,
|
Handle<HwTexture> th,
|
||||||
SamplerParams params) {
|
SamplerParams params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::bindDescriptorSet(
|
void WebGPUDriver::bindDescriptorSet(
|
||||||
backend::DescriptorSetHandle dsh,
|
Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_set_t set,
|
backend::descriptor_set_t set,
|
||||||
backend::DescriptorSetOffsetArray&& offsets) {
|
backend::DescriptorSetOffsetArray&& offsets) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||||
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||||
|
|
||||||
#include "webgpu/WebGPUSwapChain.h"
|
#include "WebGPUHandles.h"
|
||||||
#include <backend/platforms/WebGPUPlatform.h>
|
#include <backend/platforms/WebGPUPlatform.h>
|
||||||
|
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
@@ -39,6 +39,8 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
class WebGPUSwapChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebGPU backend (driver) implementation
|
* WebGPU backend (driver) implementation
|
||||||
*/
|
*/
|
||||||
@@ -60,10 +62,14 @@ private:
|
|||||||
wgpu::Adapter mAdapter = nullptr;
|
wgpu::Adapter mAdapter = nullptr;
|
||||||
wgpu::Device mDevice = nullptr;
|
wgpu::Device mDevice = nullptr;
|
||||||
wgpu::Queue mQueue = nullptr;
|
wgpu::Queue mQueue = nullptr;
|
||||||
// TODO consider moving to handle allocator when ready
|
void* mNativeWindow = nullptr;
|
||||||
std::unique_ptr<WebGPUSwapChain> mSwapChain = nullptr;
|
WebGPUSwapChain* mSwapChain = nullptr;
|
||||||
uint64_t mNextFakeHandle = 1;
|
uint64_t mNextFakeHandle = 1;
|
||||||
|
wgpu::CommandEncoder mCommandEncoder = nullptr;
|
||||||
|
wgpu::TextureView mTextureView = nullptr;
|
||||||
|
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||||
|
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||||
|
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||||
/*
|
/*
|
||||||
* Driver interface
|
* Driver interface
|
||||||
*/
|
*/
|
||||||
@@ -93,6 +99,21 @@ private:
|
|||||||
return mHandleAllocator.allocate<D>();
|
return mHandleAllocator.allocate<D>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename D, typename B, typename ... ARGS>
|
||||||
|
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
|
||||||
|
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename D, typename B>
|
||||||
|
D* handleCast(Handle<B> handle) noexcept {
|
||||||
|
return mHandleAllocator.handle_cast<D*>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename D, typename B>
|
||||||
|
void destructHandle(Handle<B>& handle) noexcept {
|
||||||
|
auto* p = mHandleAllocator.handle_cast<D*>(handle);
|
||||||
|
return mHandleAllocator.deallocate(handle, p);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WebGPUHandles.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
wgpu::Buffer createIndexBuffer(wgpu::Device const& device, uint8_t elementSize, uint32_t indexCount) {
|
||||||
|
wgpu::BufferDescriptor descriptor{ .label = "index_buffer",
|
||||||
|
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
|
||||||
|
.size = elementSize * indexCount,
|
||||||
|
.mappedAtCreation = false };
|
||||||
|
return device.CreateBuffer(&descriptor);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
|
||||||
|
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
|
||||||
|
uint32_t indexCount)
|
||||||
|
: buffer(createIndexBuffer(device, elementSize, indexCount)) {}
|
||||||
|
|
||||||
|
|
||||||
|
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
|
||||||
|
Handle<WGPUVertexBufferInfo> vbih)
|
||||||
|
: HwVertexBuffer(vextexCount),
|
||||||
|
vbih(vbih),
|
||||||
|
buffers(bufferCount) {
|
||||||
|
wgpu::BufferDescriptor descriptor {
|
||||||
|
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex,
|
||||||
|
.size = vextexCount * bufferCount,
|
||||||
|
.mappedAtCreation = false };
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < bufferCount; ++i) {
|
||||||
|
descriptor.label = ("vertex_buffer_" + std::to_string(i)).c_str();
|
||||||
|
buffers[i] = device.CreateBuffer(&descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Empty function is a place holder for verxtex buffer updates and should be
|
||||||
|
// updated for that purpose.
|
||||||
|
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
|
||||||
|
|
||||||
|
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
|
||||||
|
: HwBufferObject(byteCount),
|
||||||
|
bufferObjectBinding(bindingType) {}
|
||||||
|
|
||||||
|
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
|
||||||
|
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
|
||||||
|
if (any(ShaderStageFlags::VERTEX & fFlags)) {
|
||||||
|
retStages |= wgpu::ShaderStage::Vertex;
|
||||||
|
}
|
||||||
|
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
|
||||||
|
retStages |= wgpu::ShaderStage::Fragment;
|
||||||
|
}
|
||||||
|
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
|
||||||
|
retStages |= wgpu::ShaderStage::Compute;
|
||||||
|
}
|
||||||
|
return retStages;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
|
||||||
|
wgpu::Device const& device) {
|
||||||
|
assert_invariant(device);
|
||||||
|
|
||||||
|
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||||
|
// debugging. For now, hack an incrementing value.
|
||||||
|
static int layoutNum = 0;
|
||||||
|
|
||||||
|
uint samplerCount =
|
||||||
|
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
|
||||||
|
return fEntry.type == DescriptorType::SAMPLER ||
|
||||||
|
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
|
||||||
|
wEntries.reserve(layout.bindings.size() + samplerCount);
|
||||||
|
|
||||||
|
for (auto fEntry: layout.bindings) {
|
||||||
|
auto& wEntry = wEntries.emplace_back();
|
||||||
|
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
|
||||||
|
wEntry.binding = fEntry.binding * 2;
|
||||||
|
|
||||||
|
switch (fEntry.type) {
|
||||||
|
// TODO Metal treats these the same. Is this fine?
|
||||||
|
case DescriptorType::SAMPLER_EXTERNAL:
|
||||||
|
case DescriptorType::SAMPLER: {
|
||||||
|
// Sampler binding is 2n+1 due to split.
|
||||||
|
auto& samplerEntry = wEntries.emplace_back();
|
||||||
|
samplerEntry.binding = fEntry.binding * 2 + 1;
|
||||||
|
samplerEntry.visibility = wEntry.visibility;
|
||||||
|
// We are simply hoping that undefined and defaults suffices here.
|
||||||
|
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
|
||||||
|
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DescriptorType::UNIFORM_BUFFER: {
|
||||||
|
wEntry.buffer.hasDynamicOffset =
|
||||||
|
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
|
||||||
|
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
|
||||||
|
// TODO: Ideally we fill minBindingSize
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DescriptorType::INPUT_ATTACHMENT: {
|
||||||
|
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
|
||||||
|
PANIC_POSTCONDITION("Input Attachment is not supported");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DescriptorType::SHADER_STORAGE_BUFFER: {
|
||||||
|
// TODO: Vulkan does not support this, can we?
|
||||||
|
PANIC_POSTCONDITION("Shader storage is not supported");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently flags are only used to specify dynamic offset.
|
||||||
|
|
||||||
|
// UNUSED
|
||||||
|
// fEntry.count
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
|
||||||
|
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||||
|
// debugging. For now, hack an incrementing value.
|
||||||
|
.label{ "layout_" + std::to_string(++layoutNum) },
|
||||||
|
.entryCount = wEntries.size(),
|
||||||
|
.entries = wEntries.data()
|
||||||
|
};
|
||||||
|
// TODO Do we need to defer this until we have more info on textures and samplers??
|
||||||
|
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
|
||||||
|
}
|
||||||
|
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
|
||||||
|
}// namespace filament::backend
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||||
|
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||||
|
|
||||||
|
#include "DriverBase.h"
|
||||||
|
|
||||||
|
#include <backend/DriverEnums.h>
|
||||||
|
#include <backend/Handle.h>
|
||||||
|
|
||||||
|
#include <utils/FixedCapacityVector.h>
|
||||||
|
|
||||||
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
class WGPUProgram final : public HwProgram {
|
||||||
|
public:
|
||||||
|
WGPUProgram(wgpu::Device&, Program&);
|
||||||
|
|
||||||
|
wgpu::ShaderModule vertexShaderModule = nullptr;
|
||||||
|
wgpu::ShaderModule fragmentShaderModule = nullptr;
|
||||||
|
wgpu::ShaderModule computeShaderModule = nullptr;
|
||||||
|
std::vector<wgpu::ConstantEntry> constants;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WGPUBufferObject;
|
||||||
|
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
|
||||||
|
// Update the struct when used by WebGPU driver.
|
||||||
|
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
||||||
|
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||||
|
AttributeArray const& attributes)
|
||||||
|
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||||
|
attributes(attributes) {}
|
||||||
|
AttributeArray attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WGPUVertexBuffer : public HwVertexBuffer {
|
||||||
|
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
|
||||||
|
Handle<WGPUVertexBufferInfo> vbih);
|
||||||
|
|
||||||
|
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
|
||||||
|
|
||||||
|
Handle<WGPUVertexBufferInfo> vbih;
|
||||||
|
utils::FixedCapacityVector<wgpu::Buffer> buffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WGPUIndexBuffer : public HwIndexBuffer {
|
||||||
|
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
|
||||||
|
uint32_t indexCount);
|
||||||
|
|
||||||
|
wgpu::Buffer buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
|
||||||
|
// Update the struct when used by WebGPU driver.
|
||||||
|
struct WGPUBufferObject : HwBufferObject {
|
||||||
|
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
|
||||||
|
|
||||||
|
wgpu::Buffer buffer;
|
||||||
|
const BufferObjectBinding bufferObjectBinding;
|
||||||
|
};
|
||||||
|
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout {
|
||||||
|
public:
|
||||||
|
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
|
||||||
|
~WebGPUDescriptorSetLayout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TODO: If this is useful elsewhere, remove it from this class
|
||||||
|
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
|
||||||
|
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
|
||||||
|
|
||||||
|
wgpu::BindGroupLayout mLayout;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
|
||||||
|
// Update the struct when used by WebGPU driver.
|
||||||
|
struct WGPUTexture : public HwTexture {
|
||||||
|
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||||
|
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||||
|
|
||||||
|
// constructors for creating texture views
|
||||||
|
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||||
|
|
||||||
|
wgpu::Texture texture = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||||
|
WGPURenderPrimitive();
|
||||||
|
|
||||||
|
void setBuffers(WGPUVertexBufferInfo const* const vbi,
|
||||||
|
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
|
||||||
|
|
||||||
|
WGPUVertexBuffer* vertexBuffer = nullptr;
|
||||||
|
WGPUIndexBuffer* indexBuffer = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
|
||||||
|
// Update the struct when used by WebGPU driver.
|
||||||
|
struct WGPURenderTarget : public HwRenderTarget {
|
||||||
|
class Attachment {
|
||||||
|
public:
|
||||||
|
friend struct WGPURenderTarget;
|
||||||
|
|
||||||
|
Attachment() = default;
|
||||||
|
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||||
|
: level(level),
|
||||||
|
layer(layer),
|
||||||
|
texture(gpuTexture->texture),
|
||||||
|
mWGPUTexture(gpuTexture) {}
|
||||||
|
|
||||||
|
uint8_t level = 0;
|
||||||
|
uint16_t layer = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
wgpu::Texture texture = nullptr;
|
||||||
|
WGPUTexture* mWGPUTexture = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
||||||
|
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
|
||||||
|
WGPURenderTarget()
|
||||||
|
: HwRenderTarget(0, 0),
|
||||||
|
defaultRenderTarget(true) {}
|
||||||
|
|
||||||
|
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
|
||||||
|
const RenderPassParams& params);
|
||||||
|
|
||||||
|
math::uint2 getAttachmentSize() noexcept;
|
||||||
|
|
||||||
|
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
|
||||||
|
uint8_t getSamples() const { return samples; }
|
||||||
|
|
||||||
|
Attachment getDrawColorAttachment(size_t index);
|
||||||
|
Attachment getReadColorAttachment(size_t index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||||
|
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||||
|
|
||||||
|
bool defaultRenderTarget = false;
|
||||||
|
uint8_t samples = 1;
|
||||||
|
|
||||||
|
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||||
|
math::uint2 attachmentSize = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace filament::backend
|
||||||
|
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||||
@@ -190,10 +190,13 @@ wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device& device,
|
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||||
wgpu::SurfaceCapabilities const& capabilities, bool useSRGBColorSpace) {
|
wgpu::SurfaceCapabilities const& capabilities, wgpu::Extent2D const& surfaceSize,
|
||||||
|
bool useSRGBColorSpace) {
|
||||||
config.device = device;
|
config.device = device;
|
||||||
config.usage = wgpu::TextureUsage::RenderAttachment;
|
config.usage = wgpu::TextureUsage::RenderAttachment;
|
||||||
|
config.width = surfaceSize.width;
|
||||||
|
config.height = surfaceSize.height;
|
||||||
config.format =
|
config.format =
|
||||||
selectColorFormat(capabilities.formatCount, capabilities.formats, useSRGBColorSpace);
|
selectColorFormat(capabilities.formatCount, capabilities.formats, useSRGBColorSpace);
|
||||||
config.presentMode =
|
config.presentMode =
|
||||||
@@ -205,8 +208,8 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device& device,
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter,
|
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||||
wgpu::Device& device, uint64_t flags)
|
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags)
|
||||||
: mSurface(surface) {
|
: mSurface(surface) {
|
||||||
wgpu::SurfaceCapabilities capabilities = {};
|
wgpu::SurfaceCapabilities capabilities = {};
|
||||||
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||||
@@ -217,33 +220,57 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
||||||
initConfig(mConfig, device, capabilities, useSRGBColorSpace);
|
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
|
||||||
|
mSurface.Configure(&mConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebGPUSwapChain::~WebGPUSwapChain() {
|
WebGPUSwapChain::~WebGPUSwapChain() {
|
||||||
if (mConfigured) {
|
mSurface.Unconfigure();
|
||||||
mSurface.Unconfigure();
|
|
||||||
mConfigured = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUSwapChain::GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture* texture) {
|
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||||
if (width < 1 || height < 1) {
|
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
|
||||||
PANIC_LOG("WebGPUSwapChain::GetCurrentTexture: Invalid width and/or height requested.");
|
<< "WebGPUSwapChain::setExtent: Invalid width " << currentSurfaceSize.width
|
||||||
return;
|
<< " and/or height " << currentSurfaceSize.height << " requested.";
|
||||||
}
|
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) {
|
||||||
if (mConfig.width != width || mConfig.height != height || !mConfigured) {
|
mConfig.width = currentSurfaceSize.width;
|
||||||
mConfig.width = width;
|
mConfig.height = currentSurfaceSize.height;
|
||||||
mConfig.height = height;
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
printSurfaceConfiguration(mConfig);
|
printSurfaceConfiguration(mConfig);
|
||||||
#endif
|
#endif
|
||||||
|
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height
|
||||||
|
<< utils::io::endl;
|
||||||
|
// TODO we may need to ensure no surface texture is flight when we do this. some
|
||||||
|
// synchronization may be necessary
|
||||||
mSurface.Configure(&mConfig);
|
mSurface.Configure(&mConfig);
|
||||||
mConfigured = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mSurface.GetCurrentTexture(texture);
|
wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
|
||||||
|
wgpu::Extent2D const& currentSurfaceSize) {
|
||||||
|
setExtent(currentSurfaceSize);
|
||||||
|
wgpu::SurfaceTexture surfaceTexture;
|
||||||
|
mSurface.GetCurrentTexture(&surfaceTexture);
|
||||||
|
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// Create a view for this surface texture
|
||||||
|
// TODO: review these initiliazations as webgpu pipeline gets mature
|
||||||
|
wgpu::TextureViewDescriptor textureViewDescriptor = {
|
||||||
|
.label = "texture_view",
|
||||||
|
.format = surfaceTexture.texture.GetFormat(),
|
||||||
|
.dimension = wgpu::TextureViewDimension::e2D,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.mipLevelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.arrayLayerCount = 1
|
||||||
|
};
|
||||||
|
return surfaceTexture.texture.CreateView(&textureViewDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebGPUSwapChain::present() {
|
||||||
|
assert_invariant(mSurface);
|
||||||
|
mSurface.Present();
|
||||||
}
|
}
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -19,24 +19,28 @@
|
|||||||
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include "DriverBase.h"
|
||||||
#include <backend/Platform.h>
|
#include <backend/Platform.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class WebGPUSwapChain : public Platform::SwapChain {
|
class WebGPUSwapChain final : public Platform::SwapChain, HwSwapChain {
|
||||||
public:
|
public:
|
||||||
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter, wgpu::Device& device,
|
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||||
uint64_t flags);
|
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags);
|
||||||
~WebGPUSwapChain();
|
~WebGPUSwapChain();
|
||||||
|
|
||||||
void GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture*);
|
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||||
|
|
||||||
|
void present();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setExtent(wgpu::Extent2D const&);
|
||||||
|
|
||||||
wgpu::Surface mSurface = {};
|
wgpu::Surface mSurface = {};
|
||||||
wgpu::SurfaceConfiguration mConfig = {};
|
wgpu::SurfaceConfiguration mConfig = {};
|
||||||
bool mConfigured = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
|
#include <android/native_window.h>
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -28,6 +29,14 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
||||||
|
return wgpu::Extent2D{
|
||||||
|
.width = static_cast<uint32_t>(ANativeWindow_getWidth(window)),
|
||||||
|
.height = static_cast<uint32_t>(ANativeWindow_getHeight(window))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||||
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
||||||
surfaceSourceAndroidWindow.window = nativeWindow;
|
surfaceSourceAndroidWindow.window = nativeWindow;
|
||||||
|
|||||||
@@ -24,21 +24,8 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
// Platform specific includes and defines
|
// Platform specific includes and defines
|
||||||
#if defined(__APPLE__)
|
#include <Cocoa/Cocoa.h>
|
||||||
#include <Cocoa/Cocoa.h>
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
|
||||||
#elif defined(FILAMENT_IOS)
|
|
||||||
// Metal is not available when building for the iOS simulator on Desktop.
|
|
||||||
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
|
|
||||||
#if METAL_AVAILABLE
|
|
||||||
#import <Metal/Metal.h>
|
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
|
||||||
#endif
|
|
||||||
// is this needed?
|
|
||||||
#define METALVIEW_TAG 255
|
|
||||||
#else
|
|
||||||
#error Not a supported Apple + WebGPU platform
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
||||||
@@ -46,14 +33,19 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
// Both IOS and MacOS expects CAMetalLayer.
|
||||||
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||||
|
return wgpu::Extent2D{
|
||||||
|
.width = static_cast<uint32_t>(metalLayer.drawableSize.width),
|
||||||
|
.height = static_cast<uint32_t>(metalLayer.drawableSize.height)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||||
wgpu::Surface surface = nullptr;
|
wgpu::Surface surface = nullptr;
|
||||||
#if defined(__APPLE__)
|
// Both IOS and MacOS expects CAMetalLayer.
|
||||||
auto nsView = (__bridge NSView*) nativeWindow;
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||||
FILAMENT_CHECK_POSTCONDITION(nsView) << "Unable to obtain Metal-backed NSView.";
|
|
||||||
[nsView setWantsLayer:YES];
|
|
||||||
id metalLayer = [CAMetalLayer layer];
|
|
||||||
[nsView setLayer:metalLayer];
|
|
||||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||||
@@ -62,19 +54,6 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
|
|||||||
};
|
};
|
||||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||||
#elif defined(FILAMENT_IOS)
|
|
||||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
|
||||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
|
||||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
|
||||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
|
||||||
.nextInChain = &surfaceSourceMetalLayer,
|
|
||||||
.label = "metal_surface",
|
|
||||||
};
|
|
||||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
|
||||||
#else
|
|
||||||
#error Not a supported Apple + WebGPU platform
|
|
||||||
#endif
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,74 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
auto surfaceExtent = wgpu::Extent2D{};
|
||||||
|
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||||
|
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||||
|
surfaceExtent.width = ptrval->width;
|
||||||
|
surfaceExtent.height = ptrval->height;
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Unable to get window size for Linux Wayland-backed surface.";
|
||||||
|
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||||
|
if (g_x11.library == nullptr) {
|
||||||
|
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||||
|
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||||
|
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||||
|
int screen = 0;
|
||||||
|
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||||
|
#endif
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||||
|
g_x11.display = g_x11.openDisplay(NULL);
|
||||||
|
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
bool useXcb = false;
|
||||||
|
#endif
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
useXcb = (SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||||
|
#else
|
||||||
|
useXcb = true;
|
||||||
|
#endif
|
||||||
|
if (useXcb) {
|
||||||
|
const xcb_setup_t* setup = xcb_get_setup(g_x11.connection);
|
||||||
|
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
|
||||||
|
xcb_screen_t* screen = screen_iter.data;
|
||||||
|
surfaceExtent.width = static_cast<uint32_t>(screen->width_in_pixels);
|
||||||
|
surfaceExtent.height = static_cast<uint32_t>(screen->height_in_pixels);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||||
|
"XCB-backed surface.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
if (!useXcb) {
|
||||||
|
int screenNumber = DefaultScreen(g_x11.display);
|
||||||
|
Screen* screen = ScreenOfDisplay(g_x11.display, screenNumber);
|
||||||
|
surfaceExtent.width = static_cast<uint32_t>(WidthOfScreen(screen));
|
||||||
|
surfaceExtent.height = static_cast<uint32_t>(HeightOfScreen(screen));
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||||
|
"XLib-backed surface.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Cannot get window surface size for X11 surface for Linux (or FreeBSD) OS "
|
||||||
|
"(not built with support for XCB or XLIB?)";
|
||||||
|
#elif defined(__linux__)
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Cannot get window surface size for Linux (or FreeBSD) OS "
|
||||||
|
"(not built with support for Wayland or X11?)";
|
||||||
|
#else
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||||
|
#endif
|
||||||
|
return surfaceExtent;
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||||
wgpu::Surface surface = nullptr;
|
wgpu::Surface surface = nullptr;
|
||||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||||
|
|||||||
@@ -30,6 +30,16 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
HWND window = static_cast<HWND>(nativeWindow);
|
||||||
|
RECT windowRect;
|
||||||
|
GetWindowRect(window, &windowRect);
|
||||||
|
return wgpu::Extent2D{
|
||||||
|
.width = static_cast<uint32_t>(windowRect.right - windowRect.left),
|
||||||
|
.height = static_cast<uint32_t>(windowRect.bottom - windowRect.top)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||||
// TODO verify this is necessary for Dawn implementation as well:
|
// TODO verify this is necessary for Dawn implementation as well:
|
||||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
||||||
|
|||||||
@@ -43,24 +43,29 @@ using namespace image;
|
|||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
Backend BackendTest::sBackend = Backend::NOOP;
|
Backend BackendTest::sBackend = Backend::NOOP;
|
||||||
|
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
|
||||||
bool BackendTest::sIsMobilePlatform = false;
|
bool BackendTest::sIsMobilePlatform = false;
|
||||||
|
|
||||||
void BackendTest::init(Backend backend, bool isMobilePlatform) {
|
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
|
||||||
sBackend = backend;
|
sBackend = backend;
|
||||||
|
sOperatingSystem = operatingSystem;
|
||||||
sIsMobilePlatform = isMobilePlatform;
|
sIsMobilePlatform = isMobilePlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
||||||
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
||||||
initializeDriver();
|
initializeDriver();
|
||||||
|
mImageExpectations.emplace(getDriverApi());
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendTest::~BackendTest() {
|
BackendTest::~BackendTest() {
|
||||||
|
// Ensure all graphics commands and callbacks are finished.
|
||||||
|
flushAndWait();
|
||||||
|
mImageExpectations->evaluate();
|
||||||
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
|
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
|
||||||
if (sBackend == Backend::OPENGL) {
|
if (sBackend == Backend::OPENGL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
flushAndWait();
|
|
||||||
driver->terminate();
|
driver->terminate();
|
||||||
delete driver;
|
delete driver;
|
||||||
}
|
}
|
||||||
@@ -154,49 +159,16 @@ void BackendTest::renderTriangle(
|
|||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
|
bool BackendTest::matchesEnvironment(Backend backend) {
|
||||||
Handle<HwRenderTarget> rt, uint32_t expectedHash, bool exportScreenshot) {
|
return sBackend == backend;
|
||||||
void* buffer = calloc(1, width * height * 4);
|
}
|
||||||
|
|
||||||
struct Capture {
|
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
|
||||||
uint32_t expectedHash;
|
return sOperatingSystem == operatingSystem;
|
||||||
char* name;
|
}
|
||||||
bool exportScreenshot;
|
|
||||||
size_t width, height;
|
|
||||||
};
|
|
||||||
auto* c = new Capture();
|
|
||||||
c->expectedHash = expectedHash;
|
|
||||||
c->name = strdup(testName);
|
|
||||||
c->exportScreenshot = exportScreenshot;
|
|
||||||
c->width = width;
|
|
||||||
c->height = height;
|
|
||||||
|
|
||||||
PixelBufferDescriptor pbd(buffer, width * height * 4, PixelDataFormat::RGBA, PixelDataType::UBYTE,
|
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) {
|
||||||
1, 0, 0, width, [](void* buffer, size_t size, void* user) {
|
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend);
|
||||||
auto* c = (Capture*)user;
|
|
||||||
|
|
||||||
// Export a screenshot, if requested.
|
|
||||||
if (c->exportScreenshot) {
|
|
||||||
#ifndef FILAMENT_IOS
|
|
||||||
LinearImage image(c->width, c->height, 4);
|
|
||||||
image = toLinearWithAlpha<uint8_t>(c->width, c->height, c->width * 4,
|
|
||||||
(uint8_t*) buffer);
|
|
||||||
const std::string png = std::string(c->name) + ".png";
|
|
||||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
|
||||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
|
||||||
png);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the contents of the buffer and check that they match.
|
|
||||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
|
||||||
ASSERT_EQ(hash, c->expectedHash) << c->name << " failed: hashes do not match." << std::endl;
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
free(c->name);
|
|
||||||
free(c);
|
|
||||||
}, (void*)c);
|
|
||||||
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Environment : public ::testing::Environment {
|
class Environment : public ::testing::Environment {
|
||||||
@@ -210,8 +182,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]) {
|
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) {
|
||||||
BackendTest::init(backend, isMobile);
|
BackendTest::init(backend, operatingSystem, isMobile);
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
::testing::AddGlobalTestEnvironment(new Environment);
|
::testing::AddGlobalTestEnvironment(new Environment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,17 @@
|
|||||||
#include "private/backend/DriverApi.h"
|
#include "private/backend/DriverApi.h"
|
||||||
|
|
||||||
#include "PlatformRunner.h"
|
#include "PlatformRunner.h"
|
||||||
|
#include "ImageExpectations.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
class BackendTest : public ::testing::Test {
|
class BackendTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static void init(Backend backend, bool isMobilePlatform);
|
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform);
|
||||||
|
|
||||||
static Backend sBackend;
|
static Backend sBackend;
|
||||||
|
static OperatingSystem sOperatingSystem;
|
||||||
static bool sIsMobilePlatform;
|
static bool sIsMobilePlatform;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -64,13 +66,14 @@ protected:
|
|||||||
filament::backend::Handle<filament::backend::HwProgram> program,
|
filament::backend::Handle<filament::backend::HwProgram> program,
|
||||||
const filament::backend::RenderPassParams& params);
|
const filament::backend::RenderPassParams& params);
|
||||||
|
|
||||||
void readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
|
|
||||||
filament::backend::Handle<filament::backend::HwRenderTarget> rt, uint32_t expectedHash,
|
|
||||||
bool exportScreenshot = false);
|
|
||||||
|
|
||||||
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
||||||
filament::backend::Driver& getDriver() { return *driver; }
|
filament::backend::Driver& getDriver() { return *driver; }
|
||||||
|
|
||||||
|
ImageExpectations& getExpectations() { return *mImageExpectations; }
|
||||||
|
|
||||||
|
static bool matchesEnvironment(Backend backend);
|
||||||
|
static bool matchesEnvironment(OperatingSystem operatingSystem);
|
||||||
|
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
filament::backend::Driver* driver = nullptr;
|
filament::backend::Driver* driver = nullptr;
|
||||||
@@ -78,6 +81,10 @@ private:
|
|||||||
std::unique_ptr<filament::backend::DriverApi> commandStream;
|
std::unique_ptr<filament::backend::DriverApi> commandStream;
|
||||||
|
|
||||||
filament::backend::Handle<filament::backend::HwBufferObject> uniform;
|
filament::backend::Handle<filament::backend::HwBufferObject> uniform;
|
||||||
|
|
||||||
|
// This isn't truly optional, it just needs to delay construction until after the driver has
|
||||||
|
// been initialized
|
||||||
|
std::optional<ImageExpectations> mImageExpectations;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "ImageExpectations.h"
|
#include "ImageExpectations.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "utils/Hash.h"
|
#include "utils/Hash.h"
|
||||||
@@ -28,14 +27,17 @@
|
|||||||
#ifndef FILAMENT_IOS
|
#ifndef FILAMENT_IOS
|
||||||
|
|
||||||
#include <imageio/ImageEncoder.h>
|
#include <imageio/ImageEncoder.h>
|
||||||
|
#include <imageio/ImageDecoder.h>
|
||||||
#include <image/ColorTransform.h>
|
#include <image/ColorTransform.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
||||||
uint32_t expectedPixelHash)
|
uint32_t expectedHash)
|
||||||
: mWidth(width), mHeight(height), mFileName(std::move(fileName)),
|
: mWidth(width),
|
||||||
mExpectedPixelHash(expectedPixelHash) {}
|
mHeight(height),
|
||||||
|
mExpectedPixelHash(expectedHash),
|
||||||
|
mFileName(std::move(fileName)) {}
|
||||||
|
|
||||||
int ScreenshotParams::width() const {
|
int ScreenshotParams::width() const {
|
||||||
return mWidth;
|
return mWidth;
|
||||||
@@ -49,24 +51,28 @@ uint32_t ScreenshotParams::expectedHash() const {
|
|||||||
return mExpectedPixelHash;
|
return mExpectedPixelHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::outputDirectoryPath() const {
|
std::string ScreenshotParams::actualDirectoryPath() {
|
||||||
return ".";
|
return "images/actual_images";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::generatedActualFileName() const {
|
std::string ScreenshotParams::actualFileName() const {
|
||||||
return absl::StrFormat("%s_actual.png", mFileName);
|
return absl::StrFormat("%s_actual.png", mFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::generatedActualFilePath() const {
|
std::string ScreenshotParams::actualFilePath() const {
|
||||||
return absl::StrFormat("%s/%s", outputDirectoryPath(), generatedActualFileName());
|
return absl::StrFormat("%s/%s", actualDirectoryPath(), actualFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::goldenFileName() const {
|
std::string ScreenshotParams::expectedDirectoryPath() {
|
||||||
return absl::StrFormat("%s_golden.png", mFileName);
|
return "images/expected_images";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::goldenFilePath() const {
|
std::string ScreenshotParams::expectedFileName() const {
|
||||||
return absl::StrFormat("%s/%s", outputDirectoryPath(), goldenFileName());
|
return absl::StrFormat("%s.png", mFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ScreenshotParams::expectedFilePath() const {
|
||||||
|
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
||||||
@@ -91,11 +97,22 @@ void ImageExpectation::evaluate() {
|
|||||||
|
|
||||||
void ImageExpectation::compareImage() const {
|
void ImageExpectation::compareImage() const {
|
||||||
bool bytesFilled = mResult.bytesFilled();
|
bool bytesFilled = mResult.bytesFilled();
|
||||||
|
// If this fails, it likely means that BackendTest::flushAndWait needs to be called before
|
||||||
|
// ImageExpectations is evaluated or destroyed.
|
||||||
EXPECT_THAT(bytesFilled, testing::IsTrue())
|
EXPECT_THAT(bytesFilled, testing::IsTrue())
|
||||||
<< "Render target wasn't copied to the buffer for " << mFileName;
|
<< "Render target wasn't copied to the buffer for " << mFileName;
|
||||||
if (bytesFilled) {
|
if (bytesFilled) {
|
||||||
|
// Rather than directly compare the two images compare their hashes because comparing very
|
||||||
|
// large arrays generates way too much debug output to be useful.
|
||||||
uint32_t actualHash = mResult.hash();
|
uint32_t actualHash = mResult.hash();
|
||||||
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash()));
|
#ifndef FILAMENT_IOS
|
||||||
|
LoadedPng loadedImage(mParams.expectedFilePath());
|
||||||
|
uint32_t loadedImageHash = loadedImage.hash();
|
||||||
|
EXPECT_THAT(actualHash, testing::Eq(loadedImageHash)) << mParams.expectedFileName();
|
||||||
|
#endif
|
||||||
|
// For builds that can't load PNGs (currently iOS only) use the expected hash.
|
||||||
|
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash())) << mParams.expectedFileName();
|
||||||
|
// TODO: Add better debug output, such as generating a diff image.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +126,13 @@ ImageExpectations::~ImageExpectations() {
|
|||||||
|
|
||||||
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
|
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
|
||||||
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
|
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
|
||||||
mExpectations.emplace_back(fileName, lineNumber, mApi, std::move(params), renderTarget);
|
mExpectations.emplace_back(std::make_unique<ImageExpectation>(fileName, lineNumber, mApi,
|
||||||
|
std::move(params), renderTarget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageExpectations::evaluate() {
|
void ImageExpectations::evaluate() {
|
||||||
for (auto& expectation: mExpectations) {
|
for (auto& expectation: mExpectations) {
|
||||||
expectation.evaluate();
|
expectation->evaluate();
|
||||||
}
|
}
|
||||||
mExpectations.clear();
|
mExpectations.clear();
|
||||||
}
|
}
|
||||||
@@ -122,32 +140,28 @@ void ImageExpectations::evaluate() {
|
|||||||
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
|
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
|
||||||
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
|
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
|
||||||
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
|
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
|
||||||
#ifdef FILAMENT_IOS
|
|
||||||
bytesFilled_ = true;
|
|
||||||
bytes_.resize(size);
|
|
||||||
std::fill(bytes_.begin(), bytes_.end(), 0);
|
|
||||||
#else
|
|
||||||
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
|
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
|
||||||
mInternal->bytes.resize(size);
|
mInternal->bytes.resize(size);
|
||||||
|
|
||||||
auto cb = [](void* buffer, size_t size, void* user) {
|
auto cb = [](void* buffer, size_t size, void* user) {
|
||||||
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
||||||
|
internal->bytesFilled = true;
|
||||||
|
#ifndef FILAMENT_IOS
|
||||||
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
|
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
|
||||||
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
|
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
|
||||||
internal->params.height(),
|
internal->params.height(),
|
||||||
internal->params.width() * 4, (uint8_t*)buffer);
|
internal->params.width() * 4, (uint8_t*)buffer);
|
||||||
std::string filePath = internal->params.generatedActualFilePath();
|
std::string filePath = internal->params.actualFilePath();
|
||||||
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
||||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
|
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
|
||||||
filePath);
|
filePath);
|
||||||
internal->bytesFilled = true;
|
#endif
|
||||||
};
|
};
|
||||||
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
|
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
|
||||||
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
|
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
|
||||||
(void*)mInternal.get());
|
(void*)mInternal.get());
|
||||||
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
|
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
|
||||||
std::move(pb));
|
std::move(pb));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderTargetDump::~RenderTargetDump() {
|
RenderTargetDump::~RenderTargetDump() {
|
||||||
@@ -169,4 +183,30 @@ bool RenderTargetDump::bytesFilled() const {
|
|||||||
return mInternal->bytesFilled;
|
return mInternal->bytesFilled;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
|
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
|
||||||
|
|
||||||
|
LoadedPng::LoadedPng(std::string filePath) : mFilePath(std::move(filePath)) {
|
||||||
|
#ifndef FILAMENT_IOS
|
||||||
|
std::ifstream pngStream(mFilePath, std::ios::binary);
|
||||||
|
image::LinearImage loadedImage = image::ImageDecoder::decode(pngStream, filePath,
|
||||||
|
image::ImageDecoder::ColorSpace::LINEAR);
|
||||||
|
size_t valuesInImage = loadedImage.getWidth() * loadedImage.getHeight() *
|
||||||
|
loadedImage.getChannels();
|
||||||
|
// The linear image is loaded with each component as [0.0, 1.0] but should be [0, 255], so
|
||||||
|
// convert them.
|
||||||
|
mBytes = std::vector<unsigned char>(valuesInImage);
|
||||||
|
for (int i = 0; i < valuesInImage; ++i) {
|
||||||
|
mBytes[i] = static_cast<uint8_t>(loadedImage.get<float>()[i] * 255.0f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// For platforms that don't support the image loading library, leave the loaded data blank.
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t LoadedPng::hash() const {
|
||||||
|
EXPECT_THAT(mBytes, testing::Not(testing::IsEmpty()))
|
||||||
|
<< "Failed to load expected test result: " << mFilePath;
|
||||||
|
if (mBytes.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return utils::hash::murmur3((uint32_t*)mBytes.data(), mBytes.size() / 4, 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,11 +46,12 @@ public:
|
|||||||
int height() const;
|
int height() const;
|
||||||
uint32_t expectedHash() const;
|
uint32_t expectedHash() const;
|
||||||
|
|
||||||
std::string outputDirectoryPath() const;
|
static std::string actualDirectoryPath();
|
||||||
std::string generatedActualFileName() const;
|
std::string actualFileName() const;
|
||||||
std::string generatedActualFilePath() const;
|
std::string actualFilePath() const;
|
||||||
std::string goldenFileName() const;
|
static std::string expectedDirectoryPath();
|
||||||
std::string goldenFilePath() const;
|
std::string expectedFileName() const;
|
||||||
|
std::string expectedFilePath() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mWidth;
|
int mWidth;
|
||||||
@@ -98,6 +99,17 @@ private:
|
|||||||
std::unique_ptr<Internal> mInternal;
|
std::unique_ptr<Internal> mInternal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LoadedPng {
|
||||||
|
public:
|
||||||
|
explicit LoadedPng(std::string filePath);
|
||||||
|
|
||||||
|
uint32_t hash() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mFilePath;
|
||||||
|
std::vector<unsigned char> mBytes;
|
||||||
|
};
|
||||||
|
|
||||||
class ImageExpectation {
|
class ImageExpectation {
|
||||||
public:
|
public:
|
||||||
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
|
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
|
||||||
@@ -130,7 +142,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
filament::backend::DriverApi& mApi;
|
filament::backend::DriverApi& mApi;
|
||||||
std::vector<ImageExpectation> mExpectations;
|
// Store expectations in unique pointers because they are self referential.
|
||||||
|
std::vector<std::unique_ptr<ImageExpectation>> mExpectations;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //TNT_IMAGE_EXPECTATIONS_H
|
#endif //TNT_IMAGE_EXPECTATIONS_H
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PlatformRunner.h"
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
template<>
|
||||||
|
CString to_string<test::Backend>(test::Backend backend) noexcept {
|
||||||
|
switch (backend) {
|
||||||
|
case test::Backend::OPENGL: {
|
||||||
|
return "OpenGL";
|
||||||
|
}
|
||||||
|
case test::Backend::VULKAN: {
|
||||||
|
return "Vulkan";
|
||||||
|
}
|
||||||
|
case test::Backend::METAL: {
|
||||||
|
return "Metal";
|
||||||
|
}
|
||||||
|
case test::Backend::WEBGPU: {
|
||||||
|
return "WebGPU";
|
||||||
|
}
|
||||||
|
case test::Backend::NOOP:
|
||||||
|
default: {
|
||||||
|
return "No-op";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
CString to_string(test::OperatingSystem os) noexcept {
|
||||||
|
switch (os) {
|
||||||
|
case test::OperatingSystem::LINUX: {
|
||||||
|
return "Linux";
|
||||||
|
}
|
||||||
|
case test::OperatingSystem::APPLE: {
|
||||||
|
return "Apple";
|
||||||
|
}
|
||||||
|
case test::OperatingSystem::OTHER:
|
||||||
|
default: {
|
||||||
|
return "Other";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include "utils/CString.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
@@ -34,6 +35,15 @@ enum class Backend : uint8_t {
|
|||||||
NOOP = 5,
|
NOOP = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OperatingSystem: uint8_t {
|
||||||
|
OTHER = 1,
|
||||||
|
// Also represents android phones.
|
||||||
|
LINUX = 2,
|
||||||
|
// Also represents iOS phones.
|
||||||
|
APPLE = 3,
|
||||||
|
// TODO: When tests support windows add it here.
|
||||||
|
};
|
||||||
|
|
||||||
struct NativeView {
|
struct NativeView {
|
||||||
void* ptr = nullptr;
|
void* ptr = nullptr;
|
||||||
size_t width = 0, height = 0;
|
size_t width = 0, height = 0;
|
||||||
@@ -51,9 +61,10 @@ NativeView getNativeView();
|
|||||||
* No tests will be run yet.
|
* No tests will be run yet.
|
||||||
*
|
*
|
||||||
* @param backend The backend to run the tests on.
|
* @param backend The backend to run the tests on.
|
||||||
|
* @param operatingSystem The operating system the tests are being run on.
|
||||||
* @param isMobile True if the platform is a mobile platform (iOS or Android).
|
* @param isMobile True if the platform is a mobile platform (iOS or Android).
|
||||||
*/
|
*/
|
||||||
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]);
|
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test runners should call runTests when they are ready for tests to be run.
|
* Test runners should call runTests when they are ready for tests to be run.
|
||||||
@@ -68,6 +79,6 @@ int runTests();
|
|||||||
*/
|
*/
|
||||||
Backend parseArgumentsForBackend(int argc, char* argv[]);
|
Backend parseArgumentsForBackend(int argc, char* argv[]);
|
||||||
|
|
||||||
}
|
} // namespace test
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Skip.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
SkipEnvironment::SkipEnvironment(test::Backend backend) : backend(backend) {}
|
||||||
|
SkipEnvironment::SkipEnvironment(test::OperatingSystem os) : os(os) {}
|
||||||
|
SkipEnvironment::SkipEnvironment(test::OperatingSystem os, test::Backend backend)
|
||||||
|
: backend(backend),
|
||||||
|
os(os) {}
|
||||||
|
|
||||||
|
bool SkipEnvironment::matches() {
|
||||||
|
bool backendMatches = !backend.has_value() || *backend == BackendTest::sBackend;
|
||||||
|
bool osMatches = !os.has_value() || *os == BackendTest::sOperatingSystem;
|
||||||
|
bool isMobileMatches = !isMobile.has_value() || *isMobile == BackendTest::sIsMobilePlatform;
|
||||||
|
return backendMatches && osMatches && isMobileMatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SkipEnvironment::describe() {
|
||||||
|
std::stringstream result;
|
||||||
|
if (matches()) {
|
||||||
|
result << "environment matches because " << describe_actual_environment() << ".";
|
||||||
|
} else {
|
||||||
|
result << "environment does not match because " << describe_requirements() << " but "
|
||||||
|
<< describe_actual_environment() << ".";
|
||||||
|
}
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SkipEnvironment::describe_actual_environment() {
|
||||||
|
bool resultWritten = false;
|
||||||
|
std::stringstream reality;
|
||||||
|
if (backend.has_value()) {
|
||||||
|
reality << "backend was " << utils::to_string(BackendTest::sBackend).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (os.has_value()) {
|
||||||
|
if (resultWritten) {
|
||||||
|
reality << ", and ";
|
||||||
|
}
|
||||||
|
reality << "operating system was "
|
||||||
|
<< utils::to_string(BackendTest::sOperatingSystem).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (isMobile.has_value()) {
|
||||||
|
if (resultWritten) {
|
||||||
|
reality << ", and ";
|
||||||
|
}
|
||||||
|
reality << "device " << (BackendTest::sIsMobilePlatform ? "was" : "was not") << " mobile";
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
return reality.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SkipEnvironment::describe_requirements() {
|
||||||
|
bool resultWritten = false;
|
||||||
|
std::stringstream requirement;
|
||||||
|
if (backend.has_value()) {
|
||||||
|
requirement << "backend needs to be " << utils::to_string(*backend).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (os.has_value()) {
|
||||||
|
if (resultWritten) {
|
||||||
|
requirement << ", and ";
|
||||||
|
}
|
||||||
|
requirement << "operating system needs to be " << utils::to_string(*os).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (isMobile.has_value() && BackendTest::sIsMobilePlatform != isMobile) {
|
||||||
|
if (resultWritten) {
|
||||||
|
requirement << ", and ";
|
||||||
|
}
|
||||||
|
requirement << "device needs to " << (*isMobile ? "be" : "not be") << " mobile";
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
return requirement.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TNT_SKIP_H
|
||||||
|
#define TNT_SKIP_H
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
// skipEnvironment must be a test::SkipEnvironment
|
||||||
|
#define SKIP_IF(skipEnvironment) \
|
||||||
|
do { \
|
||||||
|
SkipEnvironment skip(skipEnvironment); \
|
||||||
|
if (skip.matches()) { \
|
||||||
|
GTEST_SKIP() << "Skipping test as the " << skip.describe(); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
struct SkipEnvironment {
|
||||||
|
SkipEnvironment(const SkipEnvironment&) = default;
|
||||||
|
explicit SkipEnvironment(test::Backend backend);
|
||||||
|
explicit SkipEnvironment(test::OperatingSystem os);
|
||||||
|
SkipEnvironment(test::OperatingSystem os, test::Backend backend);
|
||||||
|
|
||||||
|
std::optional<test::Backend> backend;
|
||||||
|
std::optional<test::OperatingSystem> os;
|
||||||
|
std::optional<bool> isMobile;
|
||||||
|
|
||||||
|
bool matches();
|
||||||
|
// Describes the current state of either matching or mismatching.
|
||||||
|
std::string describe();
|
||||||
|
// Describe all the non-null requirements.
|
||||||
|
std::string describe_requirements();
|
||||||
|
// Describes the environment's status for all the attributes that are non-null.
|
||||||
|
std::string describe_actual_environment();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
#endif// TNT_SKIP_H
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
@@ -51,6 +51,6 @@ int main(int argc, char* argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
test::initTests(backend, false, argc, argv);
|
test::initTests(backend, test::OperatingSystem::LINUX, false, argc, argv);
|
||||||
return test::runTests();
|
return test::runTests();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ test::NativeView getNativeView() {
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
auto backend = test::parseArgumentsForBackend(argc, argv);
|
auto backend = test::parseArgumentsForBackend(argc, argv);
|
||||||
test::initTests(backend, false, argc, argv);
|
test::initTests(backend, test::OperatingSystem::APPLE, false, argc, argv);
|
||||||
AppDelegate* delegate = [AppDelegate new];
|
AppDelegate* delegate = [AppDelegate new];
|
||||||
delegate.backend = backend;
|
delegate.backend = backend;
|
||||||
NSApplication* app = [NSApplication sharedApplication];
|
NSApplication* app = [NSApplication sharedApplication];
|
||||||
|
|||||||
@@ -197,16 +197,10 @@ TEST_F(BlitTest, ColorMagnify) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ImageExpectations expectations(api);
|
RenderFrame frame(api);
|
||||||
|
EXPECT_IMAGE(dstRenderTargets[0], getExpectations(),
|
||||||
{
|
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
|
||||||
RenderFrame frame(api);
|
api.commit(swapChain);
|
||||||
EXPECT_IMAGE(dstRenderTargets[0], expectations,
|
|
||||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
|
|
||||||
api.commit(swapChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,14 +254,8 @@ TEST_F(BlitTest, ColorMinify) {
|
|||||||
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
|
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
|
||||||
SamplerMagFilter::LINEAR);
|
SamplerMagFilter::LINEAR);
|
||||||
|
|
||||||
{
|
EXPECT_IMAGE(dstRenderTargets[0], getExpectations(),
|
||||||
ImageExpectations expectations(api);
|
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
|
||||||
|
|
||||||
EXPECT_IMAGE(dstRenderTargets[0], expectations,
|
|
||||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
|
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BlitTest, ColorResolve) {
|
TEST_F(BlitTest, ColorResolve) {
|
||||||
@@ -351,14 +339,8 @@ TEST_F(BlitTest, ColorResolve) {
|
|||||||
srcRenderTarget, { 0, 0, kSrcTexWidth, kSrcTexHeight },
|
srcRenderTarget, { 0, 0, kSrcTexWidth, kSrcTexHeight },
|
||||||
SamplerMagFilter::NEAREST);
|
SamplerMagFilter::NEAREST);
|
||||||
|
|
||||||
{
|
EXPECT_IMAGE(dstRenderTarget, getExpectations(),
|
||||||
ImageExpectations expectations(api);
|
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
|
||||||
|
|
||||||
EXPECT_IMAGE(dstRenderTarget, expectations,
|
|
||||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
|
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BlitTest, Blit2DTextureArray) {
|
TEST_F(BlitTest, Blit2DTextureArray) {
|
||||||
@@ -423,17 +405,11 @@ TEST_F(BlitTest, Blit2DTextureArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ImageExpectations expectations(api);
|
RenderFrame frame(api);
|
||||||
|
EXPECT_IMAGE(dstRenderTarget, getExpectations(),
|
||||||
{
|
ScreenshotParams(kDstTexWidth, kDstTexHeight, "Blit2DTextureArray",
|
||||||
RenderFrame frame(api);
|
0x8de7d55b));
|
||||||
EXPECT_IMAGE(dstRenderTarget, expectations,
|
api.commit(swapChain);
|
||||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "Blit2DTextureArray",
|
|
||||||
0x8de7d55b));
|
|
||||||
api.commit(swapChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,18 +479,12 @@ TEST_F(BlitTest, BlitRegion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ImageExpectations expectations(api);
|
RenderFrame frame(api);
|
||||||
|
// TODO: for some reason, this test has very, very slight (as in one pixel) differences
|
||||||
{
|
// between OpenGL and Metal. So disable golden checking for now.
|
||||||
RenderFrame frame(api);
|
// EXPECT_IMAGE(dstRenderTarget, expectations, ScreenshotParams(kDstTexWidth,
|
||||||
// TODO: for some reason, this test has very, very slight (as in one pixel) differences
|
// kDstTexHeight, "BlitRegion", 0x74fa34ed));
|
||||||
// between OpenGL and Metal. So disable golden checking for now.
|
api.commit(swapChain);
|
||||||
// EXPECT_IMAGE(dstRenderTarget, expectations, ScreenshotParams(kDstTexWidth,
|
|
||||||
// kDstTexHeight, "BlitRegion", 0x74fa34ed));
|
|
||||||
api.commit(swapChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,25 +537,19 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
ImageExpectations expectations(api);
|
RenderFrame frame(api);
|
||||||
{
|
|
||||||
{
|
|
||||||
RenderFrame frame(api);
|
|
||||||
|
|
||||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||||
dstRect, srcRenderTargets[srcLevel],
|
dstRect, srcRenderTargets[srcLevel],
|
||||||
srcRect, SamplerMagFilter::LINEAR);
|
srcRect, SamplerMagFilter::LINEAR);
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: for some reason, this test has been disabled. It needs to be tested on all
|
|
||||||
// machines.
|
|
||||||
// EXPECT_IMAGE(dstRenderTarget, expectations,
|
|
||||||
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
|
|
||||||
}
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: for some reason, this test has been disabled. It needs to be tested on all
|
||||||
|
// machines.
|
||||||
|
// EXPECT_IMAGE(dstRenderTarget, expectations,
|
||||||
|
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
#include "ImageExpectations.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "SharedShaders.h"
|
#include "SharedShaders.h"
|
||||||
@@ -220,9 +221,9 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
|||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
renderTarget, swapChain, shader.getProgram(), params);
|
renderTarget, swapChain, shader.getProgram(), params);
|
||||||
|
|
||||||
static const uint32_t expectedHash = 91322442;
|
|
||||||
readPixelsAndAssertHash(
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
"BufferObjectUpdateWithOffset", 512, 512, renderTarget, expectedHash, true);
|
ScreenshotParams(512, 512, "BufferObjectUpdateWithOffset", 91322442));
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
|
#include "Skip.h"
|
||||||
|
|
||||||
using namespace filament;
|
using namespace filament;
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
@@ -24,6 +25,8 @@ using namespace filament::backend;
|
|||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
TEST_F(BackendTest, FrameScheduledCallback) {
|
TEST_F(BackendTest, FrameScheduledCallback) {
|
||||||
|
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
|
||||||
|
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
@@ -81,6 +84,8 @@ TEST_F(BackendTest, FrameScheduledCallback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BackendTest, FrameCompletedCallback) {
|
TEST_F(BackendTest, FrameCompletedCallback) {
|
||||||
|
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
|
||||||
|
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
|
|||||||
@@ -71,8 +71,6 @@ void main() {
|
|||||||
fragColor = textureLod(test_tex, uv, params.sourceLevel);
|
fragColor = textureLod(test_tex, uv, params.sourceLevel);
|
||||||
})";
|
})";
|
||||||
|
|
||||||
static uint32_t sPixelHashResult = 0;
|
|
||||||
|
|
||||||
// Selecting a NPOT texture size seems to exacerbate the bug seen with Intel GPU's.
|
// Selecting a NPOT texture size seems to exacerbate the bug seen with Intel GPU's.
|
||||||
// Note that Filament uses a higher precision format (R11F_G11F_B10F) but this does not seem
|
// Note that Filament uses a higher precision format (R11F_G11F_B10F) but this does not seem
|
||||||
// necessary to trigger the bug.
|
// necessary to trigger the bug.
|
||||||
@@ -96,25 +94,6 @@ struct MaterialParams {
|
|||||||
float unused;
|
float unused;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
|
||||||
const size_t size = kTexWidth * kTexHeight * 4;
|
|
||||||
void* buffer = calloc(1, size);
|
|
||||||
auto cb = [](void* buffer, size_t size, void* user) {
|
|
||||||
int w = kTexWidth, h = kTexHeight;
|
|
||||||
const uint32_t* texels = (uint32_t*) buffer;
|
|
||||||
sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
|
|
||||||
#ifndef FILAMENT_IOS
|
|
||||||
LinearImage image(w, h, 4);
|
|
||||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
|
||||||
std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc);
|
|
||||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png");
|
|
||||||
#endif
|
|
||||||
free(buffer);
|
|
||||||
};
|
|
||||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
|
||||||
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This test needs work to get Metal and OpenGL to agree on results.
|
// TODO: This test needs work to get Metal and OpenGL to agree on results.
|
||||||
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
|
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
|
||||||
// backend's readPixels does not work correctly with textures that have image data uploaded.
|
// backend's readPixels does not work correctly with textures that have image data uploaded.
|
||||||
@@ -259,7 +238,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
||||||
// seems to be un-reliable on some GPU's.
|
// seems to be un-reliable on some GPU's.
|
||||||
if (frame == kNumFrames - 1) {
|
if (frame == kNumFrames - 1) {
|
||||||
dumpScreenshot(api, renderTargets[0]);
|
EXPECT_IMAGE(renderTargets[0], getExpectations(),
|
||||||
|
ScreenshotParams(kTexWidth, kTexHeight, "FeedbackLoops", 0x70695aa1));
|
||||||
}
|
}
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
@@ -270,10 +250,6 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t expected = 0x70695aa1;
|
|
||||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sPixelHashResult, expected);
|
|
||||||
EXPECT_TRUE(sPixelHashResult == expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -246,42 +246,42 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
|||||||
std::vector<TestCase> testCases;
|
std::vector<TestCase> testCases;
|
||||||
|
|
||||||
// Test basic upload.
|
// Test basic upload.
|
||||||
testCases.emplace_back("RGBA, UBYTE -> RGBA8", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8);
|
testCases.emplace_back("RGBA UBYTE to RGBA8", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8);
|
||||||
|
|
||||||
// Test format conversion.
|
// Test format conversion.
|
||||||
// TODO: Vulkan crashes with `Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)'
|
// TODO: Vulkan crashes with `Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)'
|
||||||
testCases.emplace_back("RGBA, FLOAT -> RGBA16F", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F);
|
testCases.emplace_back("RGBA FLOAT to RGBA16F", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F);
|
||||||
|
|
||||||
// Test texture formats not all backends support natively.
|
// Test texture formats not all backends support natively.
|
||||||
// TODO: Vulkan crashes with "VK_FORMAT_R32G32B32_SFLOAT is not supported"
|
// TODO: Vulkan crashes with "VK_FORMAT_R32G32B32_SFLOAT is not supported"
|
||||||
testCases.emplace_back("RGB, FLOAT -> RGB32F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F);
|
testCases.emplace_back("RGB FLOAT to RGB32F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F);
|
||||||
testCases.emplace_back("RGB, FLOAT -> RGB16F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB16F);
|
testCases.emplace_back("RGB FLOAT to RGB16F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB16F);
|
||||||
|
|
||||||
// Test packed format uploads.
|
// Test packed format uploads.
|
||||||
// TODO: Vulkan crashes with "Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)"
|
// TODO: Vulkan crashes with "Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)"
|
||||||
testCases.emplace_back("RGBA, UINT_2_10_10_10_REV -> RGB10_A2", PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV, TextureFormat::RGB10_A2);
|
testCases.emplace_back("RGBA UINT_2_10_10_10_REV to RGB10_A2", PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV, TextureFormat::RGB10_A2);
|
||||||
testCases.emplace_back("RGB, UINT_10F_11F_11F_REV -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::UINT_10F_11F_11F_REV, TextureFormat::R11F_G11F_B10F);
|
testCases.emplace_back("RGB UINT_10F_11F_11F_REV to R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::UINT_10F_11F_11F_REV, TextureFormat::R11F_G11F_B10F);
|
||||||
testCases.emplace_back("RGB, HALF -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::HALF, TextureFormat::R11F_G11F_B10F);
|
testCases.emplace_back("RGB HALF to R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::HALF, TextureFormat::R11F_G11F_B10F);
|
||||||
|
|
||||||
// Test integer format uploads.
|
// Test integer format uploads.
|
||||||
// TODO: These cases fail on OpenGL and Vulkan.
|
// TODO: These cases fail on OpenGL and Vulkan.
|
||||||
// TODO: These cases now also fail on Metal, but at some point previously worked.
|
// TODO: These cases now also fail on Metal, but at some point previously worked.
|
||||||
testCases.emplace_back("RGB_INTEGER, UBYTE -> RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI);
|
testCases.emplace_back("RGB_INTEGER UBYTE to RGB8UI", PixelDataFormat::RGB_INTEGER, PixelDataType::UBYTE, TextureFormat::RGB8UI);
|
||||||
testCases.emplace_back("RGB_INTEGER, USHORT -> RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI);
|
testCases.emplace_back("RGB_INTEGER USHORT to RGB16UI", PixelDataFormat::RGB_INTEGER, PixelDataType::USHORT, TextureFormat::RGB16UI);
|
||||||
testCases.emplace_back("RGB_INTEGER, INT -> RGB32I", PixelDataFormat::RGB_INTEGER, PixelDataType::INT, TextureFormat::RGB32I);
|
testCases.emplace_back("RGB_INTEGER INT to RGB32I", PixelDataFormat::RGB_INTEGER, PixelDataType::INT, TextureFormat::RGB32I);
|
||||||
|
|
||||||
// Test uploads with buffer padding.
|
// Test uploads with buffer padding.
|
||||||
// TODO: Vulkan crashes with "Assertion failed: (offset + size <= allocationSize)"
|
// TODO: Vulkan crashes with "Assertion failed: (offset + size <= allocationSize)"
|
||||||
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (with buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u);
|
testCases.emplace_back("RGBA UBYTE to RGBA8 (with buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u);
|
||||||
testCases.emplace_back("RGBA, FLOAT -> RGBA16F (with buffer padding)", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F, 64u);
|
testCases.emplace_back("RGBA FLOAT to RGBA16F (with buffer padding)", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F, 64u);
|
||||||
testCases.emplace_back("RGB, FLOAT -> RGB32F (with buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u);
|
testCases.emplace_back("RGB FLOAT to RGB32F (with buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u);
|
||||||
|
|
||||||
// Upload subregions separately.
|
// Upload subregions separately.
|
||||||
// TODO: Vulkan crashes with "Offsets not yet supported"
|
// TODO: Vulkan crashes with "Offsets not yet supported"
|
||||||
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 0u, true);
|
testCases.emplace_back("RGBA UBYTE to RGBA8 (subregions)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 0u, true);
|
||||||
testCases.emplace_back("RGBA, FLOAT -> RGBA16F (subregions)", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F, 0u, true);
|
testCases.emplace_back("RGBA FLOAT to RGBA16F (subregions)", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F, 0u, true);
|
||||||
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions, buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
|
testCases.emplace_back("RGBA UBYTE to RGBA8 (subregions and buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
|
||||||
testCases.emplace_back("RGB, FLOAT -> RGB32F (subregions, buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u, true);
|
testCases.emplace_back("RGB FLOAT to RGB32F (subregions and buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u, true);
|
||||||
|
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
|
||||||
@@ -339,16 +339,14 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
|||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
|
ScreenshotParams(512, 512, t.name, expectedHash));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
api.finish();
|
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||||
@@ -372,7 +370,7 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
|||||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
getSamplerTypeName(textureFormat), fragmentTemplate);
|
||||||
Shader shader(api, cleanup, ShaderConfig{
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
||||||
"text_tex", DescriptorType::SAMPLER, samplerInfo
|
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||||
}}});
|
}}});
|
||||||
|
|
||||||
// Create a texture.
|
// Create a texture.
|
||||||
@@ -416,15 +414,12 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
|||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
static const uint32_t expectedHash = 359858623;
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
|
ScreenshotParams(512, 512, "UpdateImageSRGB", 359858623));
|
||||||
|
|
||||||
api.flush();
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
// This ensures all driver commands have finished before exiting the test.
|
|
||||||
api.finish();
|
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,15 +473,12 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
|||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
static const uint32_t expectedHash = 3644679986;
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash);
|
ScreenshotParams(512, 512, "UpdateImageMipLevel", 3644679986));
|
||||||
|
|
||||||
api.flush();
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
// This ensures all driver commands have finished before exiting the test.
|
|
||||||
api.finish();
|
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,29 +530,24 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
|||||||
|
|
||||||
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 4, std::move(descriptor));
|
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 4, std::move(descriptor));
|
||||||
|
|
||||||
api.beginFrame(0, 0, 0);
|
{
|
||||||
|
RenderFrame frame(api);
|
||||||
|
|
||||||
// Update samplers.
|
// Update samplers.
|
||||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
api.updateDescriptorSetTexture(descriptorSet, 0, texture,
|
||||||
.filterMag = SamplerMagFilter::LINEAR,
|
{ .filterMag = SamplerMagFilter::LINEAR,
|
||||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST });
|
||||||
});
|
|
||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||||
|
|
||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
renderTriangle({ { DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() } },
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
static const uint32_t expectedHash = 3644679986;
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash);
|
ScreenshotParams(512, 512, "UpdateImage3D", 3644679986));
|
||||||
|
}
|
||||||
|
|
||||||
api.flush();
|
|
||||||
api.commit(swapChain);
|
|
||||||
api.endFrame(0);
|
|
||||||
|
|
||||||
// This ensures all driver commands have finished before exiting the test.
|
|
||||||
api.finish();
|
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
#include "ImageExpectations.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "ShaderGenerator.h"
|
||||||
|
#include "Skip.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <utils/Hash.h>
|
#include <utils/Hash.h>
|
||||||
@@ -78,6 +80,8 @@ void main() {
|
|||||||
})";
|
})";
|
||||||
|
|
||||||
TEST_F(BackendTest, PushConstants) {
|
TEST_F(BackendTest, PushConstants) {
|
||||||
|
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
|
||||||
|
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
|
||||||
api.startCapture(0);
|
api.startCapture(0);
|
||||||
@@ -148,19 +152,14 @@ TEST_F(BackendTest, PushConstants) {
|
|||||||
|
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|
||||||
readPixelsAndAssertHash("pushConstants", 512, 512, renderTarget, 1957275826, true);
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
|
ScreenshotParams(512, 512, "pushConstants", 1957275826));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
api.stopCapture(0);
|
api.stopCapture(0);
|
||||||
|
|
||||||
// Wait for the ReadPixels result to come back.
|
|
||||||
api.finish();
|
|
||||||
|
|
||||||
executeCommands();
|
|
||||||
getDriver().purge();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
#include "ImageExpectations.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "SharedShaders.h"
|
#include "SharedShaders.h"
|
||||||
@@ -200,17 +201,16 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|
||||||
readPixelsAndAssertHash("RenderExternalImage", 512, 512, defaultRenderTarget, 267229901, true);
|
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
|
ScreenshotParams(512, 512, "RenderExternalImage", 267229901));
|
||||||
|
|
||||||
api.stopCapture(0);
|
api.stopCapture(0);
|
||||||
|
|
||||||
api.finish();
|
api.finish();
|
||||||
|
flushAndWait();
|
||||||
|
|
||||||
executeCommands();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
#include "ImageExpectations.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "SharedShaders.h"
|
#include "SharedShaders.h"
|
||||||
@@ -130,20 +131,14 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
|||||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|
||||||
readPixelsAndAssertHash("scissor", kSrcTexWidth >> 1, kSrcTexHeight >> 1, fullRenderTarget,
|
EXPECT_IMAGE(fullRenderTarget, getExpectations(),
|
||||||
0xAB3D1C53, true);
|
ScreenshotParams(kSrcTexWidth >> 1, kSrcTexHeight >> 1, "scissor", 0xAB3D1C53));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
api.stopCapture(0);
|
api.stopCapture(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the ReadPixels result to come back.
|
|
||||||
api.finish();
|
|
||||||
|
|
||||||
executeCommands();
|
|
||||||
getDriver().purge();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that a negative Viewport origin works with scissor.
|
// Verify that a negative Viewport origin works with scissor.
|
||||||
@@ -226,20 +221,14 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
|||||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|
||||||
readPixelsAndAssertHash(
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
"ScissorViewportEdgeCases", 512, 512, renderTarget, 0x6BF00F31, true);
|
ScreenshotParams(512, 512, "ScissorViewportEdgeCases", 0x6BF00F31));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
api.stopCapture(0);
|
api.stopCapture(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the ReadPixels result to come back.
|
|
||||||
api.finish();
|
|
||||||
|
|
||||||
executeCommands();
|
|
||||||
getDriver().purge();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
#include "ImageExpectations.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "SharedShaders.h"
|
#include "SharedShaders.h"
|
||||||
@@ -129,7 +130,8 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
|||||||
|
|
||||||
RunTest(renderTarget);
|
RunTest(renderTarget);
|
||||||
|
|
||||||
readPixelsAndAssertHash("StencilBuffer", 512, 512, renderTarget, 0x3B1AEF0F, true);
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
|
ScreenshotParams(512, 512, "StencilBuffer", 0x3B1AEF0F));
|
||||||
|
|
||||||
flushAndWait();
|
flushAndWait();
|
||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
@@ -151,7 +153,8 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
|||||||
|
|
||||||
RunTest(renderTarget);
|
RunTest(renderTarget);
|
||||||
|
|
||||||
readPixelsAndAssertHash("DepthAndStencilBuffer", 512, 512, renderTarget, 0x3B1AEF0F, true);
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
|
ScreenshotParams(512, 512, "DepthAndStencilBuffer", 0x3B1AEF0F));
|
||||||
|
|
||||||
flushAndWait();
|
flushAndWait();
|
||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
@@ -233,7 +236,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
api.stopCapture(0);
|
api.stopCapture(0);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
readPixelsAndAssertHash("StencilBufferAutoResolve", 512, 512, renderTarget1, 0x6CEFAC8F, true);
|
EXPECT_IMAGE(renderTarget1, getExpectations(),
|
||||||
|
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 0x6CEFAC8F));
|
||||||
|
|
||||||
flushAndWait();
|
flushAndWait();
|
||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
|
|||||||
@@ -317,15 +317,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
size_t metalUploadBufferSizeBytes = 512 * 1024;
|
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.
|
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||||
* Currently only honored by the GL and Metal backends.
|
* Currently only honored by the GL and Metal backends.
|
||||||
|
|||||||