Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f04387651 | ||
|
|
7b13964363 | ||
|
|
44b6c63fed | ||
|
|
6908f1b8af | ||
|
|
ad1b36d2b3 | ||
|
|
315c2c273f | ||
|
|
005d835dbe | ||
|
|
cade94ab2c | ||
|
|
5cd2f5626c | ||
|
|
94bbcbf1c3 | ||
|
|
52c998d2b6 | ||
|
|
527d831c15 | ||
|
|
2790f2e64c | ||
|
|
757640e850 | ||
|
|
9d9abca33a | ||
|
|
bea7a7c73f | ||
|
|
dd62f98784 | ||
|
|
395a3eda9b | ||
|
|
9d18cc35e0 | ||
|
|
2e37ebe70c | ||
|
|
63ef412be1 | ||
|
|
f0996296c1 | ||
|
|
a60d570b3b | ||
|
|
3fdb9f311a |
@@ -19,6 +19,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- name: Run build script
|
- name: Run build script
|
||||||
run: |
|
run: |
|
||||||
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
||||||
@@ -44,6 +46,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
@@ -60,6 +64,8 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- name: Run build script
|
- name: Run build script
|
||||||
run: |
|
run: |
|
||||||
cd build/ios && printf "y" | ./build.sh presubmit
|
cd build/ios && printf "y" | ./build.sh presubmit
|
||||||
@@ -122,3 +128,31 @@ jobs:
|
|||||||
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
||||||
- name: Run test
|
- name: Run test
|
||||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||||
|
|
||||||
|
code-correcteness:
|
||||||
|
name: code-correctness
|
||||||
|
runs-on: 'macos-14-xlarge'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4.1.6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up Homebrew
|
||||||
|
id: set-up-homebrew
|
||||||
|
uses: Homebrew/actions/setup-homebrew@master
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Install prerequisites
|
||||||
|
run: |
|
||||||
|
pip install pyyaml
|
||||||
|
brew install llvm
|
||||||
|
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
|
||||||
|
- name: Run build script
|
||||||
|
# We need to build before clang-tidy can run analysis
|
||||||
|
run: |
|
||||||
|
# This will build for all three desktop backends on mac
|
||||||
|
./build.sh -p desktop debug gltf_viewer
|
||||||
|
- name: Run test
|
||||||
|
run: |
|
||||||
|
bash test/code-correctness/test.sh
|
||||||
|
|||||||
@@ -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.1'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
|||||||
iOS projects can use CocoaPods to install the latest release:
|
iOS projects can use CocoaPods to install the latest release:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pod 'Filament', '~> 1.59.0'
|
pod 'Filament', '~> 1.59.1'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ 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.2
|
||||||
|
|
||||||
|
- Fix build/compile errors when upgrading to MacOS 15.4
|
||||||
|
|
||||||
## v1.59.1
|
## v1.59.1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -142,16 +142,13 @@ abstract class MaterialCompiler extends TaskWithBinary {
|
|||||||
if (!exclude_vulkan) {
|
if (!exclude_vulkan) {
|
||||||
matcArgs += ['-a', 'vulkan']
|
matcArgs += ['-a', 'vulkan']
|
||||||
}
|
}
|
||||||
|
|
||||||
def include_webgpu = providers
|
def include_webgpu = providers
|
||||||
.gradleProperty("com.google.android.filament.include-webgpu")
|
.gradleProperty("com.google.android.filament.include-webgpu")
|
||||||
.forUseAtConfigurationTime().present
|
.forUseAtConfigurationTime().present
|
||||||
|
if (include_webgpu) {
|
||||||
if (!include_webgpu) {
|
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
|
||||||
matcArgs += ['-a', 'webgpu']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def mat_no_opt = providers
|
def mat_no_opt = providers
|
||||||
.gradleProperty("com.google.android.filament.matnopt")
|
.gradleProperty("com.google.android.filament.matnopt")
|
||||||
.forUseAtConfigurationTime().present
|
.forUseAtConfigurationTime().present
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
GROUP=com.google.android.filament
|
GROUP=com.google.android.filament
|
||||||
VERSION_NAME=1.59.0
|
VERSION_NAME=1.59.1
|
||||||
|
|
||||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||||
|
|
||||||
|
|||||||
@@ -177,29 +177,6 @@ endif()
|
|||||||
if (FILAMENT_SUPPORTS_VULKAN)
|
if (FILAMENT_SUPPORTS_VULKAN)
|
||||||
list(APPEND SRCS
|
list(APPEND SRCS
|
||||||
include/backend/platforms/VulkanPlatform.h
|
include/backend/platforms/VulkanPlatform.h
|
||||||
src/vulkan/VulkanDescriptorSetCache.cpp
|
|
||||||
src/vulkan/VulkanDescriptorSetCache.h
|
|
||||||
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
|
|
||||||
src/vulkan/VulkanDescriptorSetLayoutCache.h
|
|
||||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
|
||||||
src/vulkan/VulkanPipelineLayoutCache.h
|
|
||||||
src/vulkan/memory/ResourceManager.cpp
|
|
||||||
src/vulkan/memory/ResourceManager.h
|
|
||||||
src/vulkan/memory/ResourcePointer.h
|
|
||||||
src/vulkan/memory/Resource.cpp
|
|
||||||
src/vulkan/memory/Resource.h
|
|
||||||
src/vulkan/platform/VulkanPlatform.cpp
|
|
||||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
|
||||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
|
||||||
src/vulkan/utils/Conversion.cpp
|
|
||||||
src/vulkan/utils/Conversion.h
|
|
||||||
src/vulkan/utils/Definitions.h
|
|
||||||
src/vulkan/utils/Helper.h
|
|
||||||
src/vulkan/utils/Image.h
|
|
||||||
src/vulkan/utils/Image.cpp
|
|
||||||
src/vulkan/utils/Spirv.h
|
|
||||||
src/vulkan/utils/Spirv.cpp
|
|
||||||
src/vulkan/utils/StaticVector.h
|
|
||||||
src/vulkan/VulkanAsyncHandles.h
|
src/vulkan/VulkanAsyncHandles.h
|
||||||
src/vulkan/VulkanBlitter.cpp
|
src/vulkan/VulkanBlitter.cpp
|
||||||
src/vulkan/VulkanBlitter.h
|
src/vulkan/VulkanBlitter.h
|
||||||
@@ -210,6 +187,10 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
|||||||
src/vulkan/VulkanConstants.h
|
src/vulkan/VulkanConstants.h
|
||||||
src/vulkan/VulkanContext.cpp
|
src/vulkan/VulkanContext.cpp
|
||||||
src/vulkan/VulkanContext.h
|
src/vulkan/VulkanContext.h
|
||||||
|
src/vulkan/VulkanDescriptorSetCache.cpp
|
||||||
|
src/vulkan/VulkanDescriptorSetCache.h
|
||||||
|
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
|
||||||
|
src/vulkan/VulkanDescriptorSetLayoutCache.h
|
||||||
src/vulkan/VulkanDriver.cpp
|
src/vulkan/VulkanDriver.cpp
|
||||||
src/vulkan/VulkanDriver.h
|
src/vulkan/VulkanDriver.h
|
||||||
src/vulkan/VulkanDriverFactory.h
|
src/vulkan/VulkanDriverFactory.h
|
||||||
@@ -217,24 +198,43 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
|||||||
src/vulkan/VulkanFboCache.h
|
src/vulkan/VulkanFboCache.h
|
||||||
src/vulkan/VulkanHandles.cpp
|
src/vulkan/VulkanHandles.cpp
|
||||||
src/vulkan/VulkanHandles.h
|
src/vulkan/VulkanHandles.h
|
||||||
src/vulkan/VulkanMemory.h
|
|
||||||
src/vulkan/VulkanMemory.cpp
|
src/vulkan/VulkanMemory.cpp
|
||||||
|
src/vulkan/VulkanMemory.h
|
||||||
src/vulkan/VulkanPipelineCache.cpp
|
src/vulkan/VulkanPipelineCache.cpp
|
||||||
src/vulkan/VulkanPipelineCache.h
|
src/vulkan/VulkanPipelineCache.h
|
||||||
|
src/vulkan/VulkanPipelineLayoutCache.cpp
|
||||||
|
src/vulkan/VulkanPipelineLayoutCache.h
|
||||||
src/vulkan/VulkanQueryManager.cpp
|
src/vulkan/VulkanQueryManager.cpp
|
||||||
src/vulkan/VulkanQueryManager.h
|
src/vulkan/VulkanQueryManager.h
|
||||||
|
src/vulkan/VulkanReadPixels.cpp
|
||||||
|
src/vulkan/VulkanReadPixels.h
|
||||||
src/vulkan/VulkanSamplerCache.cpp
|
src/vulkan/VulkanSamplerCache.cpp
|
||||||
src/vulkan/VulkanSamplerCache.h
|
src/vulkan/VulkanSamplerCache.h
|
||||||
src/vulkan/VulkanStagePool.cpp
|
src/vulkan/VulkanStagePool.cpp
|
||||||
src/vulkan/VulkanStagePool.h
|
src/vulkan/VulkanStagePool.h
|
||||||
src/vulkan/VulkanSwapChain.cpp
|
src/vulkan/VulkanSwapChain.cpp
|
||||||
src/vulkan/VulkanSwapChain.h
|
src/vulkan/VulkanSwapChain.h
|
||||||
src/vulkan/VulkanReadPixels.cpp
|
|
||||||
src/vulkan/VulkanReadPixels.h
|
|
||||||
src/vulkan/VulkanTexture.cpp
|
src/vulkan/VulkanTexture.cpp
|
||||||
src/vulkan/VulkanTexture.h
|
src/vulkan/VulkanTexture.h
|
||||||
src/vulkan/VulkanYcbcrConversionCache.cpp
|
src/vulkan/VulkanYcbcrConversionCache.cpp
|
||||||
src/vulkan/VulkanYcbcrConversionCache.h
|
src/vulkan/VulkanYcbcrConversionCache.h
|
||||||
|
src/vulkan/memory/Resource.cpp
|
||||||
|
src/vulkan/memory/Resource.h
|
||||||
|
src/vulkan/memory/ResourceManager.cpp
|
||||||
|
src/vulkan/memory/ResourceManager.h
|
||||||
|
src/vulkan/memory/ResourcePointer.h
|
||||||
|
src/vulkan/platform/VulkanPlatform.cpp
|
||||||
|
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||||
|
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||||
|
src/vulkan/utils/Conversion.cpp
|
||||||
|
src/vulkan/utils/Conversion.h
|
||||||
|
src/vulkan/utils/Definitions.h
|
||||||
|
src/vulkan/utils/Helper.h
|
||||||
|
src/vulkan/utils/Image.cpp
|
||||||
|
src/vulkan/utils/Image.h
|
||||||
|
src/vulkan/utils/Spirv.cpp
|
||||||
|
src/vulkan/utils/Spirv.h
|
||||||
|
src/vulkan/utils/StaticVector.h
|
||||||
)
|
)
|
||||||
if (LINUX OR WIN32)
|
if (LINUX OR WIN32)
|
||||||
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
|
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
|
||||||
@@ -255,10 +255,10 @@ 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/WebGPUSwapChain.cpp
|
|
||||||
src/webgpu/WebGPUSwapChain.h
|
|
||||||
src/webgpu/WebGPUHandles.cpp
|
src/webgpu/WebGPUHandles.cpp
|
||||||
src/webgpu/WebGPUHandles.h
|
src/webgpu/WebGPUHandles.h
|
||||||
|
src/webgpu/WebGPUSwapChain.cpp
|
||||||
|
src/webgpu/WebGPUSwapChain.h
|
||||||
)
|
)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||||
@@ -508,6 +508,7 @@ if (APPLE OR LINUX)
|
|||||||
test/ImageExpectations.cpp
|
test/ImageExpectations.cpp
|
||||||
test/Lifetimes.cpp
|
test/Lifetimes.cpp
|
||||||
test/Shader.cpp
|
test/Shader.cpp
|
||||||
|
test/SharedShaders.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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1139,6 +1139,7 @@ struct ExternalSamplerDatum {
|
|||||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||||
|
|
||||||
struct DescriptorSetLayout {
|
struct DescriptorSetLayout {
|
||||||
|
std::string label;
|
||||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||||
|
|
||||||
// TODO: uncomment when needed
|
// TODO: uncomment when needed
|
||||||
|
|||||||
@@ -104,10 +104,6 @@ public:
|
|||||||
|
|
||||||
// Semaphore to be signaled once the image is available.
|
// Semaphore to be signaled once the image is available.
|
||||||
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
|
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
|
||||||
|
|
||||||
// A function called right before vkQueueSubmit. After this call, the image must be
|
|
||||||
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
|
|
||||||
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VulkanPlatform();
|
VulkanPlatform();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -54,31 +54,27 @@ using namespace utils;
|
|||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
static void logCompilationError(utils::io::ostream& out,
|
static inline std::string to_string(bool b) noexcept { return b ? "true" : "false"; }
|
||||||
ShaderStage shaderType, const char* name,
|
static inline std::string to_string(int i) noexcept { return std::to_string(i); }
|
||||||
|
static inline std::string to_string(float f) noexcept { return "float(" + std::to_string(f) + ")"; }
|
||||||
|
|
||||||
|
static void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
|
||||||
GLuint shaderId, CString const& sourceCode) noexcept;
|
GLuint shaderId, CString const& sourceCode) noexcept;
|
||||||
|
static void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept;
|
||||||
|
|
||||||
static void logProgramLinkError(utils::io::ostream& out,
|
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source,
|
||||||
const char* name, GLuint program) noexcept;
|
size_t len) noexcept;
|
||||||
|
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source,
|
||||||
static inline std::string to_string(bool b) noexcept {
|
size_t len) noexcept;
|
||||||
return b ? "true" : "false";
|
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 inline std::string to_string(int i) noexcept {
|
|
||||||
return std::to_string(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::string to_string(float f) noexcept {
|
|
||||||
return "float(" + std::to_string(f) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||||
struct ProgramData {
|
struct ProgramData {
|
||||||
GLuint program{};
|
GLuint program{};
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
shaders_t shaders{};
|
||||||
};
|
};
|
||||||
|
|
||||||
~OpenGLProgramToken() override;
|
~OpenGLProgramToken() override;
|
||||||
@@ -90,10 +86,10 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
|||||||
ShaderCompilerService& compiler;
|
ShaderCompilerService& compiler;
|
||||||
utils::CString const& name;
|
utils::CString const& name;
|
||||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> attributes;
|
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> attributes;
|
||||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
|
shaders_source_t shaderSourceCode;
|
||||||
void* user = nullptr;
|
void* user = nullptr;
|
||||||
struct {
|
struct {
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
shaders_t shaders{};
|
||||||
GLuint program = 0;
|
GLuint program = 0;
|
||||||
} gl; // 12 bytes
|
} gl; // 12 bytes
|
||||||
|
|
||||||
@@ -140,11 +136,12 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
|||||||
|
|
||||||
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() = default;
|
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() = default;
|
||||||
|
|
||||||
void ShaderCompilerService::setUserData(const program_token_t& token, void* user) noexcept {
|
/* static */ void ShaderCompilerService::setUserData(const program_token_t& token,
|
||||||
|
void* user) noexcept {
|
||||||
token->user = user;
|
token->user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept {
|
/* static */ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept {
|
||||||
return token->user;
|
return token->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,101 +265,111 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
|||||||
token->handle = mCallbackManager.get();
|
token->handle = mCallbackManager.get();
|
||||||
|
|
||||||
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
||||||
if (mMode == Mode::THREAD_POOL) {
|
switch (mMode) {
|
||||||
// queue a compile job
|
case Mode::THREAD_POOL: {
|
||||||
mCompilerThreadPool.queue(priorityQueue, token,
|
// queue a compile job
|
||||||
[this, &gl, program = std::move(program), token]() mutable {
|
mCompilerThreadPool.queue(priorityQueue, token,
|
||||||
// compile the shaders
|
[this, &gl, program = std::move(program), token]() mutable {
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
// compile the shaders
|
||||||
compileShaders(gl,
|
shaders_t shaders{};
|
||||||
std::move(program.getShadersSource()),
|
compileShaders(gl,
|
||||||
program.getSpecializationConstants(),
|
std::move(program.getShadersSource()),
|
||||||
program.isMultiview(),
|
program.getSpecializationConstants(),
|
||||||
shaders,
|
program.isMultiview(),
|
||||||
token->shaderSourceCode);
|
shaders,
|
||||||
|
token->shaderSourceCode);
|
||||||
|
|
||||||
// link the program
|
// link the program
|
||||||
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
||||||
|
|
||||||
OpenGLProgramToken::ProgramData programData;
|
OpenGLProgramToken::ProgramData programData;
|
||||||
programData.shaders = shaders;
|
programData.shaders = shaders;
|
||||||
|
|
||||||
// We need to query the link status here to guarantee that the
|
// We need to query the link status here to guarantee that the
|
||||||
// program is compiled and linked now (we don't want this to be
|
// program is compiled and linked now (we don't want this to be
|
||||||
// deferred to later). We don't care about the result at this point.
|
// deferred to later). We don't care about the result at this point.
|
||||||
GLint status = GL_FALSE;
|
GLint status = GL_FALSE;
|
||||||
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
|
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
|
||||||
programData.program = glProgram;
|
programData.program = glProgram;
|
||||||
|
|
||||||
// we don't need to check for success here, it'll be done on the
|
// we don't need to check for success here, it'll be done on the
|
||||||
// main thread side.
|
// main thread side.
|
||||||
token->set(programData);
|
token->set(programData);
|
||||||
|
|
||||||
mCallbackManager.put(token->handle);
|
mCallbackManager.put(token->handle);
|
||||||
|
|
||||||
// caching must be the last thing we do
|
// caching must be the last thing we do
|
||||||
if (token->key && status == GL_TRUE) {
|
if (token->key && status == GL_TRUE) {
|
||||||
// Attempt to cache. This calls glGetProgramBinary.
|
// Attempt to cache. This calls glGetProgramBinary.
|
||||||
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
|
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
case Mode::SYNCHRONOUS:
|
||||||
// this cannot fail because we check compilation status after linking the program
|
case Mode::ASYNCHRONOUS: {
|
||||||
// shaders[] is filled with id of shader stages present.
|
// this cannot fail because we check compilation status after linking the program
|
||||||
compileShaders(gl,
|
// shaders[] is filled with id of shader stages present.
|
||||||
std::move(program.getShadersSource()),
|
compileShaders(gl,
|
||||||
program.getSpecializationConstants(),
|
std::move(program.getShadersSource()),
|
||||||
program.isMultiview(),
|
program.getSpecializationConstants(),
|
||||||
token->gl.shaders,
|
program.isMultiview(),
|
||||||
token->shaderSourceCode);
|
token->gl.shaders,
|
||||||
|
token->shaderSourceCode);
|
||||||
|
|
||||||
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
|
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
|
||||||
assert_invariant(mMode != Mode::THREAD_POOL);
|
assert_invariant(mMode != Mode::THREAD_POOL);
|
||||||
if (mMode == Mode::ASYNCHRONOUS) {
|
if (mMode == Mode::ASYNCHRONOUS) {
|
||||||
// don't attempt to link this program if all shaders are not done compiling
|
// don't attempt to link this program if all shaders are not done compiling
|
||||||
GLint status;
|
GLint status;
|
||||||
if (token->gl.program) {
|
if (token->gl.program) {
|
||||||
glGetProgramiv(token->gl.program, GL_COMPLETION_STATUS, &status);
|
glGetProgramiv(token->gl.program, GL_COMPLETION_STATUS, &status);
|
||||||
if (status == GL_FALSE) {
|
if (status == GL_FALSE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto shader: token->gl.shaders) {
|
for (auto shader: token->gl.shaders) {
|
||||||
if (shader) {
|
if (shader) {
|
||||||
glGetShaderiv(shader, GL_COMPLETION_STATUS, &status);
|
glGetShaderiv(shader, GL_COMPLETION_STATUS, &status);
|
||||||
if (status == GL_FALSE) {
|
if (status == GL_FALSE) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!token->gl.program) {
|
if (!token->gl.program) {
|
||||||
// link the program, this also cannot fail because status is checked later.
|
// link the program, this also cannot fail because status is checked later.
|
||||||
token->gl.program = linkProgram(mDriver.getContext(),
|
token->gl.program = linkProgram(mDriver.getContext(),
|
||||||
token->gl.shaders, token->attributes);
|
token->gl.shaders, token->attributes);
|
||||||
if (mMode == Mode::ASYNCHRONOUS) {
|
if (mMode == Mode::ASYNCHRONOUS) {
|
||||||
// wait until the link finishes...
|
// wait until the link finishes...
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
assert_invariant(token->gl.program);
|
assert_invariant(token->gl.program);
|
||||||
|
|
||||||
mCallbackManager.put(token->handle);
|
mCallbackManager.put(token->handle);
|
||||||
|
|
||||||
if (token->key) {
|
if (token->key) {
|
||||||
// TODO: technically we don't have to cache right now. Is it advantageous to
|
// TODO: technically we don't have to cache right now. Is it advantageous to
|
||||||
// do this later, maybe depending on CPU usage?
|
// do this later, maybe depending on CPU usage?
|
||||||
// attempt to cache if we don't have a thread pool (otherwise it's done
|
// attempt to cache if we don't have a thread pool (otherwise it's done
|
||||||
// by the pool).
|
// by the pool).
|
||||||
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Mode::UNDEFINED: {
|
||||||
|
assert_invariant(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
@@ -377,12 +384,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
|
|||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderCompilerService::terminate(program_token_t& token) {
|
/* static */ void ShaderCompilerService::terminate(program_token_t& token) {
|
||||||
assert_invariant(token);
|
assert_invariant(token);
|
||||||
|
|
||||||
token->canceled = true;
|
token->canceled = true;
|
||||||
|
|
||||||
bool const canceled = token->compiler.cancelTickOp(token);
|
bool const isTickOpCanceled = token->compiler.cancelTickOp(token);
|
||||||
|
|
||||||
if (token->compiler.mMode == Mode::THREAD_POOL) {
|
if (token->compiler.mMode == Mode::THREAD_POOL) {
|
||||||
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
|
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
|
||||||
@@ -395,7 +402,7 @@ void ShaderCompilerService::terminate(program_token_t& token) {
|
|||||||
// order for future callbacks to be successfully called.
|
// order for future callbacks to be successfully called.
|
||||||
token->compiler.mCallbackManager.put(token->handle);
|
token->compiler.mCallbackManager.put(token->handle);
|
||||||
}
|
}
|
||||||
} else if (canceled) {
|
} else if (isTickOpCanceled) {
|
||||||
// Since the tick op was canceled, we need to .put the token here.
|
// Since the tick op was canceled, we need to .put the token here.
|
||||||
token->compiler.mCallbackManager.put(token->handle);
|
token->compiler.mCallbackManager.put(token->handle);
|
||||||
}
|
}
|
||||||
@@ -432,7 +439,8 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
|
|||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) noexcept {
|
/* static */ void ShaderCompilerService::getProgramFromCompilerPool(
|
||||||
|
program_token_t& token) noexcept {
|
||||||
OpenGLProgramToken::ProgramData const& programData{ token->get() };
|
OpenGLProgramToken::ProgramData const& programData{ token->get() };
|
||||||
if (!token->canceled) {
|
if (!token->canceled) {
|
||||||
token->gl.shaders = programData.shaders;
|
token->gl.shaders = programData.shaders;
|
||||||
@@ -443,40 +451,53 @@ void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) n
|
|||||||
GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||||
SYSTRACE_CALL();
|
SYSTRACE_CALL();
|
||||||
if (!token->gl.program) {
|
if (!token->gl.program) {
|
||||||
if (mMode == Mode::THREAD_POOL) {
|
switch (mMode) {
|
||||||
// we need this program right now, remove it from the queue
|
case Mode::THREAD_POOL: {
|
||||||
auto job = mCompilerThreadPool.dequeue(token);
|
// we need this program right now, remove it from the queue
|
||||||
if (job) {
|
auto job = mCompilerThreadPool.dequeue(token);
|
||||||
// if we were able to remove it, we execute the job now, otherwise it means
|
if (job) {
|
||||||
// it's being executed right now.
|
// if we were able to remove it, we execute the job now, otherwise it means
|
||||||
job();
|
// it's being executed right now.
|
||||||
|
job();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token->canceled) {
|
||||||
|
token->compiler.cancelTickOp(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until we get the program from the pool. Generally this wouldn't block
|
||||||
|
// because we just compiled the program above, when executing job.
|
||||||
|
ShaderCompilerService::getProgramFromCompilerPool(token);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!token->canceled) {
|
case Mode::ASYNCHRONOUS: {
|
||||||
|
// we force the program link -- which might stall, either here or below in
|
||||||
|
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
|
||||||
token->compiler.cancelTickOp(token);
|
token->compiler.cancelTickOp(token);
|
||||||
|
|
||||||
|
token->gl.program =
|
||||||
|
linkProgram(mDriver.getContext(), token->gl.shaders, token->attributes);
|
||||||
|
|
||||||
|
assert_invariant(token->gl.program);
|
||||||
|
|
||||||
|
mCallbackManager.put(token->handle);
|
||||||
|
|
||||||
|
if (token->key) {
|
||||||
|
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block until we get the program from the pool. Generally this wouldn't block
|
case Mode::SYNCHRONOUS: {
|
||||||
// because we just compiled the program above, when executing job.
|
// if we don't have a program yet, block until we get it.
|
||||||
ShaderCompilerService::getProgramFromCompilerPool(token);
|
tick();
|
||||||
} else if (mMode == Mode::ASYNCHRONOUS) {
|
break;
|
||||||
// we force the program link -- which might stall, either here or below in
|
}
|
||||||
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
|
|
||||||
token->compiler.cancelTickOp(token);
|
case Mode::UNDEFINED: {
|
||||||
|
assert_invariant(false);
|
||||||
token->gl.program = linkProgram(mDriver.getContext(),
|
|
||||||
token->gl.shaders, token->attributes);
|
|
||||||
|
|
||||||
assert_invariant(token->gl.program);
|
|
||||||
|
|
||||||
mCallbackManager.put(token->handle);
|
|
||||||
|
|
||||||
if (token->key) {
|
|
||||||
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// if we don't have a program yet, block until we get it.
|
|
||||||
tick();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,12 +540,12 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
|||||||
* checked until after the program is linked.
|
* checked until after the program is linked.
|
||||||
* This always returns the GL shader IDs or zero a shader stage is not present.
|
* This always returns the GL shader IDs or zero a shader stage is not present.
|
||||||
*/
|
*/
|
||||||
void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
/* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||||
Program::ShaderSource shadersSource,
|
Program::ShaderSource shadersSource,
|
||||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||||
bool multiview,
|
bool multiview,
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
shaders_t& outShaders,
|
||||||
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
UTILS_UNUSED_IN_RELEASE shaders_source_t& outShaderSourceCode) noexcept {
|
||||||
|
|
||||||
SYSTRACE_CALL();
|
SYSTRACE_CALL();
|
||||||
|
|
||||||
@@ -635,209 +656,13 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If usages of the Google-style line directive are present, remove them, as some
|
|
||||||
// drivers don't allow the quotation marks. This source modification happens in-place.
|
|
||||||
void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
|
||||||
char* source, size_t len) noexcept {
|
|
||||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
|
||||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
|
||||||
removeGoogleLineDirectives(source, len); // length is unaffected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
|
|
||||||
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
|
|
||||||
// the number of views, which is assumed as a single digit, for multiview.
|
|
||||||
// This source modification happens in-place.
|
|
||||||
void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
|
|
||||||
int32_t eyeCount, char* source, size_t len) noexcept {
|
|
||||||
// We don't use regular expression in favor of performance.
|
|
||||||
if (context.ext.OVR_multiview2) {
|
|
||||||
const std::string_view shader{ source, len };
|
|
||||||
const std::string_view layout = "layout";
|
|
||||||
const std::string_view num_views = "num_views";
|
|
||||||
size_t found = 0;
|
|
||||||
while (true) {
|
|
||||||
found = shader.find(layout, found);
|
|
||||||
if (found == std::string_view::npos) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
found = shader.find_first_not_of(' ', found + layout.size());
|
|
||||||
if (found == std::string_view::npos || shader[found] != '(') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
found = shader.find_first_not_of(' ', found + 1);
|
|
||||||
if (found == std::string_view::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (shader.compare(found, num_views.size(), num_views) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
found = shader.find_first_not_of(' ', found + num_views.size());
|
|
||||||
if (found == std::string_view::npos || shader[found] != '=') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
found = shader.find_first_not_of(' ', found + 1);
|
|
||||||
if (found == std::string_view::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// We assume the value should be one-digit number.
|
|
||||||
assert_invariant(eyeCount < 10);
|
|
||||||
assert_invariant(!::isdigit(source[found + 1]));
|
|
||||||
source[found] = '0' + eyeCount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
|
||||||
// macOS doesn't support GL_ARB_shading_language_packing
|
|
||||||
// Also GLES3.0 didn't have the full set of packing/unpacking functions
|
|
||||||
std::string_view ShaderCompilerService::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
|
|
||||||
using namespace std::literals;
|
|
||||||
#ifdef BACKEND_OPENGL_VERSION_GL
|
|
||||||
if (!context.isAtLeastGL<4, 2>() && !context.ext.ARB_shading_language_packing) {
|
|
||||||
return R"(
|
|
||||||
|
|
||||||
// these don't handle denormals, NaNs or inf
|
|
||||||
float u16tofp32(highp uint v) {
|
|
||||||
v <<= 16u;
|
|
||||||
highp uint s = v & 0x80000000u;
|
|
||||||
highp uint n = v & 0x7FFFFFFFu;
|
|
||||||
highp uint nz = (n == 0u) ? 0u : 0xFFFFFFFFu;
|
|
||||||
return uintBitsToFloat(s | ((((n >> 3u) + (0x70u << 23u))) & nz));
|
|
||||||
}
|
|
||||||
vec2 unpackHalf2x16(highp uint v) {
|
|
||||||
return vec2(u16tofp32(v&0xFFFFu), u16tofp32(v>>16u));
|
|
||||||
}
|
|
||||||
uint fp32tou16(float val) {
|
|
||||||
uint f32 = floatBitsToUint(val);
|
|
||||||
uint f16 = 0u;
|
|
||||||
uint sign = (f32 >> 16u) & 0x8000u;
|
|
||||||
int exponent = int((f32 >> 23u) & 0xFFu) - 127;
|
|
||||||
uint mantissa = f32 & 0x007FFFFFu;
|
|
||||||
if (exponent > 15) {
|
|
||||||
f16 = sign | (0x1Fu << 10u);
|
|
||||||
} else if (exponent > -15) {
|
|
||||||
exponent += 15;
|
|
||||||
mantissa >>= 13;
|
|
||||||
f16 = sign | uint(exponent << 10) | mantissa;
|
|
||||||
} else {
|
|
||||||
f16 = sign;
|
|
||||||
}
|
|
||||||
return f16;
|
|
||||||
}
|
|
||||||
highp uint packHalf2x16(vec2 v) {
|
|
||||||
highp uint x = fp32tou16(v.x);
|
|
||||||
highp uint y = fp32tou16(v.y);
|
|
||||||
return (y << 16u) | x;
|
|
||||||
}
|
|
||||||
highp uint packUnorm4x8(mediump vec4 v) {
|
|
||||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
|
||||||
highp uint a = uint(v.x);
|
|
||||||
highp uint b = uint(v.y) << 8;
|
|
||||||
highp uint c = uint(v.z) << 16;
|
|
||||||
highp uint d = uint(v.w) << 24;
|
|
||||||
return (a|b|c|d);
|
|
||||||
}
|
|
||||||
highp uint packSnorm4x8(mediump vec4 v) {
|
|
||||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
|
||||||
highp uint a = uint((int(v.x) & 0xff));
|
|
||||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
|
||||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
|
||||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
|
||||||
return (a|b|c|d);
|
|
||||||
}
|
|
||||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
|
||||||
return vec4(float((v & 0x000000ffu) ),
|
|
||||||
float((v & 0x0000ff00u) >> 8),
|
|
||||||
float((v & 0x00ff0000u) >> 16),
|
|
||||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
|
||||||
}
|
|
||||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
|
||||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
|
||||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
|
||||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
|
||||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
|
||||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
|
||||||
}
|
|
||||||
)"sv;
|
|
||||||
}
|
|
||||||
#endif // BACKEND_OPENGL_VERSION_GL
|
|
||||||
|
|
||||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
|
||||||
if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
|
|
||||||
return R"(
|
|
||||||
|
|
||||||
highp uint packUnorm4x8(mediump vec4 v) {
|
|
||||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
|
||||||
highp uint a = uint(v.x);
|
|
||||||
highp uint b = uint(v.y) << 8;
|
|
||||||
highp uint c = uint(v.z) << 16;
|
|
||||||
highp uint d = uint(v.w) << 24;
|
|
||||||
return (a|b|c|d);
|
|
||||||
}
|
|
||||||
highp uint packSnorm4x8(mediump vec4 v) {
|
|
||||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
|
||||||
highp uint a = uint((int(v.x) & 0xff));
|
|
||||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
|
||||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
|
||||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
|
||||||
return (a|b|c|d);
|
|
||||||
}
|
|
||||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
|
||||||
return vec4(float((v & 0x000000ffu) ),
|
|
||||||
float((v & 0x0000ff00u) >> 8),
|
|
||||||
float((v & 0x00ff0000u) >> 16),
|
|
||||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
|
||||||
}
|
|
||||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
|
||||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
|
||||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
|
||||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
|
||||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
|
||||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
|
||||||
}
|
|
||||||
)"sv;
|
|
||||||
}
|
|
||||||
#endif // BACKEND_OPENGL_VERSION_GLES
|
|
||||||
return ""sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// split shader source code in three:
|
|
||||||
// - the version line
|
|
||||||
// - extensions
|
|
||||||
// - everything else
|
|
||||||
std::array<std::string_view, 3> ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
|
|
||||||
auto version_start = source.find("#version");
|
|
||||||
assert_invariant(version_start != std::string_view::npos);
|
|
||||||
|
|
||||||
auto version_eol = source.find('\n', version_start) + 1;
|
|
||||||
assert_invariant(version_eol != std::string_view::npos);
|
|
||||||
|
|
||||||
auto prolog_start = version_eol;
|
|
||||||
auto prolog_eol = source.rfind("\n#extension"); // last #extension line
|
|
||||||
if (prolog_eol == std::string_view::npos) {
|
|
||||||
prolog_eol = prolog_start;
|
|
||||||
} else {
|
|
||||||
prolog_eol = source.find('\n', prolog_eol + 1) + 1;
|
|
||||||
}
|
|
||||||
auto body_start = prolog_eol;
|
|
||||||
|
|
||||||
std::string_view const version = source.substr(version_start, version_eol - version_start);
|
|
||||||
std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
|
|
||||||
std::string_view const body = source.substr(body_start, source.length() - body_start);
|
|
||||||
return { version, prolog, body };
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a program from the given shader IDs and links it. This cannot fail because errors
|
* Create a program from the given shader IDs and links it. This cannot fail because errors
|
||||||
* are checked later. This always returns a valid GL program ID (which doesn't mean the
|
* are checked later. This always returns a valid GL program ID (which doesn't mean the
|
||||||
* program itself is valid).
|
* program itself is valid).
|
||||||
*/
|
*/
|
||||||
GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
|
/* static */ GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
|
||||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
|
shaders_t const& shaders,
|
||||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept {
|
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept {
|
||||||
|
|
||||||
SYSTRACE_CALL();
|
SYSTRACE_CALL();
|
||||||
@@ -913,7 +738,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
|||||||
* Checks a program link status and logs errors and frees resources on failure.
|
* Checks a program link status and logs errors and frees resources on failure.
|
||||||
* Returns true on success.
|
* Returns true on success.
|
||||||
*/
|
*/
|
||||||
bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noexcept {
|
/* static */ bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noexcept {
|
||||||
|
|
||||||
SYSTRACE_CALL();
|
SYSTRACE_CALL();
|
||||||
|
|
||||||
@@ -948,20 +773,24 @@ bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noe
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
/* static */ void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
|
||||||
const char* name, GLuint shaderId,
|
GLuint shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
||||||
UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
|
||||||
|
|
||||||
auto to_string = [](ShaderStage type) -> const char* {
|
auto to_string = [](ShaderStage type) -> const char* {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ShaderStage::VERTEX: return "vertex";
|
case ShaderStage::VERTEX:
|
||||||
case ShaderStage::FRAGMENT: return "fragment";
|
return "vertex";
|
||||||
case ShaderStage::COMPUTE: return "compute";
|
case ShaderStage::FRAGMENT:
|
||||||
|
return "fragment";
|
||||||
|
case ShaderStage::COMPUTE:
|
||||||
|
return "compute";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
{ // scope for the temporary string storage
|
{// scope for the temporary string storage
|
||||||
GLint length = 0;
|
GLint length = 0;
|
||||||
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);
|
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);
|
||||||
|
|
||||||
@@ -969,8 +798,7 @@ void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
|||||||
glGetShaderInfoLog(shaderId, length, nullptr, infoLog.data());
|
glGetShaderInfoLog(shaderId, length, nullptr, infoLog.data());
|
||||||
|
|
||||||
out << "Compilation error in " << to_string(shaderType) << " shader \"" << name << "\":\n"
|
out << "Compilation error in " << to_string(shaderType) << " shader \"" << name << "\":\n"
|
||||||
<< "\"" << infoLog.c_str() << "\""
|
<< "\"" << infoLog.c_str() << "\"" << io::endl;
|
||||||
<< io::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@@ -996,7 +824,7 @@ void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
/* static */ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
||||||
GLint length = 0;
|
GLint length = 0;
|
||||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
|
||||||
|
|
||||||
@@ -1004,9 +832,204 @@ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noe
|
|||||||
glGetProgramInfoLog(program, length, nullptr, infoLog.data());
|
glGetProgramInfoLog(program, length, nullptr, infoLog.data());
|
||||||
|
|
||||||
out << "Link error in \"" << name << "\":\n"
|
out << "Link error in \"" << name << "\":\n"
|
||||||
<< "\"" << infoLog.c_str() << "\""
|
<< "\"" << infoLog.c_str() << "\"" << io::endl;
|
||||||
<< io::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If usages of the Google-style line directive are present, remove them, as some
|
||||||
|
// drivers don't allow the quotation marks. This source modification happens in-place.
|
||||||
|
/* static */ void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source,
|
||||||
|
size_t len) noexcept {
|
||||||
|
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||||
|
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
||||||
|
removeGoogleLineDirectives(source, len);// length is unaffected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
|
||||||
|
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
|
||||||
|
// the number of views, which is assumed as a single digit, for multiview.
|
||||||
|
// This source modification happens in-place.
|
||||||
|
/* static */ void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source,
|
||||||
|
size_t len) noexcept {
|
||||||
|
// We don't use regular expression in favor of performance.
|
||||||
|
if (context.ext.OVR_multiview2) {
|
||||||
|
const std::string_view shader{ source, len };
|
||||||
|
const std::string_view layout = "layout";
|
||||||
|
const std::string_view num_views = "num_views";
|
||||||
|
size_t found = 0;
|
||||||
|
while (true) {
|
||||||
|
found = shader.find(layout, found);
|
||||||
|
if (found == std::string_view::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
found = shader.find_first_not_of(' ', found + layout.size());
|
||||||
|
if (found == std::string_view::npos || shader[found] != '(') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
found = shader.find_first_not_of(' ', found + 1);
|
||||||
|
if (found == std::string_view::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (shader.compare(found, num_views.size(), num_views) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
found = shader.find_first_not_of(' ', found + num_views.size());
|
||||||
|
if (found == std::string_view::npos || shader[found] != '=') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
found = shader.find_first_not_of(' ', found + 1);
|
||||||
|
if (found == std::string_view::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// We assume the value should be one-digit number.
|
||||||
|
assert_invariant(eyeCount < 10);
|
||||||
|
assert_invariant(!::isdigit(source[found + 1]));
|
||||||
|
source[found] = '0' + eyeCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||||
|
// macOS doesn't support GL_ARB_shading_language_packing
|
||||||
|
// Also GLES3.0 didn't have the full set of packing/unpacking functions
|
||||||
|
/* static */ std::string_view process_ARB_shading_language_packing(
|
||||||
|
OpenGLContext& context) noexcept {
|
||||||
|
using namespace std::literals;
|
||||||
|
#ifdef BACKEND_OPENGL_VERSION_GL
|
||||||
|
if (!context.isAtLeastGL<4, 2>() && !context.ext.ARB_shading_language_packing) {
|
||||||
|
return R"(
|
||||||
|
|
||||||
|
// these don't handle denormals, NaNs or inf
|
||||||
|
float u16tofp32(highp uint v) {
|
||||||
|
v <<= 16u;
|
||||||
|
highp uint s = v & 0x80000000u;
|
||||||
|
highp uint n = v & 0x7FFFFFFFu;
|
||||||
|
highp uint nz = (n == 0u) ? 0u : 0xFFFFFFFFu;
|
||||||
|
return uintBitsToFloat(s | ((((n >> 3u) + (0x70u << 23u))) & nz));
|
||||||
|
}
|
||||||
|
vec2 unpackHalf2x16(highp uint v) {
|
||||||
|
return vec2(u16tofp32(v&0xFFFFu), u16tofp32(v>>16u));
|
||||||
|
}
|
||||||
|
uint fp32tou16(float val) {
|
||||||
|
uint f32 = floatBitsToUint(val);
|
||||||
|
uint f16 = 0u;
|
||||||
|
uint sign = (f32 >> 16u) & 0x8000u;
|
||||||
|
int exponent = int((f32 >> 23u) & 0xFFu) - 127;
|
||||||
|
uint mantissa = f32 & 0x007FFFFFu;
|
||||||
|
if (exponent > 15) {
|
||||||
|
f16 = sign | (0x1Fu << 10u);
|
||||||
|
} else if (exponent > -15) {
|
||||||
|
exponent += 15;
|
||||||
|
mantissa >>= 13;
|
||||||
|
f16 = sign | uint(exponent << 10) | mantissa;
|
||||||
|
} else {
|
||||||
|
f16 = sign;
|
||||||
|
}
|
||||||
|
return f16;
|
||||||
|
}
|
||||||
|
highp uint packHalf2x16(vec2 v) {
|
||||||
|
highp uint x = fp32tou16(v.x);
|
||||||
|
highp uint y = fp32tou16(v.y);
|
||||||
|
return (y << 16u) | x;
|
||||||
|
}
|
||||||
|
highp uint packUnorm4x8(mediump vec4 v) {
|
||||||
|
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||||
|
highp uint a = uint(v.x);
|
||||||
|
highp uint b = uint(v.y) << 8;
|
||||||
|
highp uint c = uint(v.z) << 16;
|
||||||
|
highp uint d = uint(v.w) << 24;
|
||||||
|
return (a|b|c|d);
|
||||||
|
}
|
||||||
|
highp uint packSnorm4x8(mediump vec4 v) {
|
||||||
|
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||||
|
highp uint a = uint((int(v.x) & 0xff));
|
||||||
|
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||||
|
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||||
|
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||||
|
return (a|b|c|d);
|
||||||
|
}
|
||||||
|
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||||
|
return vec4(float((v & 0x000000ffu) ),
|
||||||
|
float((v & 0x0000ff00u) >> 8),
|
||||||
|
float((v & 0x00ff0000u) >> 16),
|
||||||
|
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||||
|
}
|
||||||
|
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||||
|
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||||
|
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||||
|
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||||
|
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||||
|
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||||
|
}
|
||||||
|
)"sv;
|
||||||
|
}
|
||||||
|
#endif// BACKEND_OPENGL_VERSION_GL
|
||||||
|
|
||||||
|
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||||
|
if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
|
||||||
|
return R"(
|
||||||
|
|
||||||
|
highp uint packUnorm4x8(mediump vec4 v) {
|
||||||
|
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||||
|
highp uint a = uint(v.x);
|
||||||
|
highp uint b = uint(v.y) << 8;
|
||||||
|
highp uint c = uint(v.z) << 16;
|
||||||
|
highp uint d = uint(v.w) << 24;
|
||||||
|
return (a|b|c|d);
|
||||||
|
}
|
||||||
|
highp uint packSnorm4x8(mediump vec4 v) {
|
||||||
|
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||||
|
highp uint a = uint((int(v.x) & 0xff));
|
||||||
|
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||||
|
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||||
|
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||||
|
return (a|b|c|d);
|
||||||
|
}
|
||||||
|
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||||
|
return vec4(float((v & 0x000000ffu) ),
|
||||||
|
float((v & 0x0000ff00u) >> 8),
|
||||||
|
float((v & 0x00ff0000u) >> 16),
|
||||||
|
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||||
|
}
|
||||||
|
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||||
|
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||||
|
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||||
|
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||||
|
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||||
|
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||||
|
}
|
||||||
|
)"sv;
|
||||||
|
}
|
||||||
|
#endif// BACKEND_OPENGL_VERSION_GLES
|
||||||
|
return ""sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split shader source code in three:
|
||||||
|
// - the version line
|
||||||
|
// - extensions
|
||||||
|
// - everything else
|
||||||
|
/* static */ std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept {
|
||||||
|
auto version_start = source.find("#version");
|
||||||
|
assert_invariant(version_start != std::string_view::npos);
|
||||||
|
|
||||||
|
auto version_eol = source.find('\n', version_start) + 1;
|
||||||
|
assert_invariant(version_eol != std::string_view::npos);
|
||||||
|
|
||||||
|
auto prolog_start = version_eol;
|
||||||
|
auto prolog_eol = source.rfind("\n#extension");// last #extension line
|
||||||
|
if (prolog_eol == std::string_view::npos) {
|
||||||
|
prolog_eol = prolog_start;
|
||||||
|
} else {
|
||||||
|
prolog_eol = source.find('\n', prolog_eol + 1) + 1;
|
||||||
|
}
|
||||||
|
auto body_start = prolog_eol;
|
||||||
|
|
||||||
|
std::string_view const version = source.substr(version_start, version_eol - version_start);
|
||||||
|
std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
|
||||||
|
std::string_view const body = source.substr(body_start, source.length() - body_start);
|
||||||
|
return { version, prolog, body };
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -134,22 +136,9 @@ private:
|
|||||||
OpenGLContext& context,
|
OpenGLContext& context,
|
||||||
Program::ShaderSource shadersSource,
|
Program::ShaderSource shadersSource,
|
||||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||||
bool multiview,
|
bool multiview, shaders_t& outShaders, shaders_source_t& outShaderSourceCode) noexcept;
|
||||||
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,
|
static GLuint linkProgram(OpenGLContext& context, shaders_t const& shaders,
|
||||||
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;
|
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept;
|
||||||
|
|
||||||
static bool checkProgramStatus(program_token_t const& token) noexcept;
|
static bool checkProgramStatus(program_token_t const& token) noexcept;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -70,13 +70,11 @@ struct VulkanRenderPass {
|
|||||||
// context are stored in VulkanPlatform.
|
// context are stored in VulkanPlatform.
|
||||||
struct VulkanContext {
|
struct VulkanContext {
|
||||||
public:
|
public:
|
||||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties,
|
||||||
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
uint32_t flags, VkFlags reqs) {
|
||||||
assert_invariant(isProtectedMemorySupported() == true);
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
||||||
if (flags & 1) {
|
if (flags & 1) {
|
||||||
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
if ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +83,13 @@ public:
|
|||||||
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
||||||
|
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
||||||
|
assert_invariant(isProtectedMemorySupported());
|
||||||
|
}
|
||||||
|
return selectMemoryType(mMemoryProperties, flags, reqs);
|
||||||
|
}
|
||||||
|
|
||||||
inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const {
|
inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const {
|
||||||
return mDepthStencilFormats;
|
return mDepthStencilFormats;
|
||||||
}
|
}
|
||||||
@@ -118,7 +123,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool isMultiviewEnabled() const noexcept {
|
inline bool isMultiviewEnabled() const noexcept {
|
||||||
return mMultiviewEnabled;
|
return mPhysicalDeviceVk11Features.multiview == VK_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isClipDistanceSupported() const noexcept {
|
inline bool isClipDistanceSupported() const noexcept {
|
||||||
@@ -142,6 +147,9 @@ private:
|
|||||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||||
};
|
};
|
||||||
|
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||||
|
};
|
||||||
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
|
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
|
||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||||
};
|
};
|
||||||
@@ -154,7 +162,6 @@ private:
|
|||||||
};
|
};
|
||||||
bool mDebugMarkersSupported = false;
|
bool mDebugMarkersSupported = false;
|
||||||
bool mDebugUtilsSupported = false;
|
bool mDebugUtilsSupported = false;
|
||||||
bool mMultiviewEnabled = false;
|
|
||||||
bool mLazilyAllocatedMemorySupported = false;
|
bool mLazilyAllocatedMemorySupported = false;
|
||||||
bool mProtectedMemorySupported = false;
|
bool mProtectedMemorySupported = false;
|
||||||
|
|
||||||
|
|||||||
@@ -282,18 +282,25 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
|||||||
fvkutils::DescriptorSetMask curMask = setMask;
|
fvkutils::DescriptorSetMask curMask = setMask;
|
||||||
|
|
||||||
auto& updateSets = mStashedSets;
|
auto& updateSets = mStashedSets;
|
||||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
|
||||||
|
|
||||||
setMask.forEachSetBit([&](size_t index) {
|
if (pipelineLayoutIsSame) {
|
||||||
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
|
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||||
curMask.unset(index);
|
setMask.forEachSetBit([&](size_t index) {
|
||||||
|
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
|
||||||
|
curMask.unset(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (curMask.none() &&
|
||||||
|
mLastBoundInfo.setMask == setMask && mLastBoundInfo.boundSets == updateSets) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
setMask.forEachSetBit([&](size_t index) {
|
||||||
if (curMask.none() &&
|
if (!updateSets[index]) {
|
||||||
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
|
curMask.unset(index);
|
||||||
mLastBoundInfo.boundSets == updateSets)) {
|
}
|
||||||
return;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
|
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
|
||||||
@@ -301,7 +308,7 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
|||||||
auto set = updateSets[index];
|
auto set = updateSets[index];
|
||||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||||
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
1, &set->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||||
commands->acquire(set);
|
commands->acquire(set);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -329,7 +336,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
|||||||
}
|
}
|
||||||
VkWriteDescriptorSet const descriptorWrite = {
|
VkWriteDescriptorSet const descriptorWrite = {
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
.dstSet = set->vkSet,
|
.dstSet = set->getVkSet(),
|
||||||
.dstBinding = binding,
|
.dstBinding = binding,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.descriptorType = type,
|
.descriptorType = type,
|
||||||
@@ -342,10 +349,6 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
|||||||
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||||
VkSampler sampler) noexcept {
|
VkSampler sampler) noexcept {
|
||||||
VkDescriptorImageInfo info{
|
|
||||||
.sampler = sampler,
|
|
||||||
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
|
||||||
};
|
|
||||||
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
||||||
VkImageViewType const expectedType = texture->getViewType();
|
VkImageViewType const expectedType = texture->getViewType();
|
||||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||||
@@ -355,12 +358,16 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
|
|||||||
range.levelCount = 1;
|
range.levelCount = 1;
|
||||||
range.layerCount = 1;
|
range.layerCount = 1;
|
||||||
}
|
}
|
||||||
info.imageView = texture->getView(range);
|
VkDescriptorImageInfo info{
|
||||||
|
.sampler = sampler,
|
||||||
|
.imageView = texture->getView(range),
|
||||||
|
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||||
|
};
|
||||||
|
|
||||||
VkWriteDescriptorSet const descriptorWrite = {
|
VkWriteDescriptorSet const descriptorWrite = {
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.dstSet = set->vkSet,
|
.dstSet = set->getVkSet(),
|
||||||
.dstBinding = binding,
|
.dstBinding = binding,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
@@ -378,10 +385,10 @@ void VulkanDescriptorSetCache::updateInputAttachment(
|
|||||||
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
|
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
|
||||||
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||||
auto const vkSet = mDescriptorPool->obtainSet(layout);
|
auto const vkSet = getVkSet(layout);
|
||||||
auto const& count = layout->count;
|
auto const& count = layout->count;
|
||||||
auto const vklayout = layout->getVkLayout();
|
auto const vklayout = layout->getVkLayout();
|
||||||
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
|
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle,
|
||||||
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
||||||
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
||||||
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
||||||
@@ -390,10 +397,20 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
|
|||||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
set->setVkSet(vkSet);
|
||||||
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDescriptorSetCache::gc() {
|
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(
|
||||||
mStashedSets = {};
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||||
|
return mDescriptorPool->obtainSet(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanDescriptorSetCache::manualRecyle(VulkanDescriptorSetLayout::Count const& count,
|
||||||
|
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
|
||||||
|
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ public:
|
|||||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||||
|
|
||||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||||
|
using DescriptorSetArray =
|
||||||
|
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||||
|
|
||||||
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||||
~VulkanDescriptorSetCache();
|
~VulkanDescriptorSetCache();
|
||||||
@@ -68,14 +70,21 @@ public:
|
|||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||||
|
|
||||||
|
// This method is only meant to be used with external samplers (or internally within this
|
||||||
|
// class).
|
||||||
|
VkDescriptorSet getVkSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||||
|
|
||||||
|
// This method is only meant to be used with external samplers.
|
||||||
|
void manualRecyle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
|
||||||
|
VkDescriptorSet vkSet);
|
||||||
|
|
||||||
|
DescriptorSetArray const& getBoundSets() const { return mStashedSets; }
|
||||||
|
|
||||||
void gc();
|
void gc();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class DescriptorInfinitePool;
|
class DescriptorInfinitePool;
|
||||||
|
|
||||||
using DescriptorSetArray =
|
|
||||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
|
||||||
|
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
fvkmemory::ResourceManager* mResourceManager;
|
fvkmemory::ResourceManager* mResourceManager;
|
||||||
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#include "VulkanHandles.h"
|
#include "VulkanHandles.h"
|
||||||
|
|
||||||
|
#include <utils/Hash.h>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -58,6 +60,56 @@ uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType t
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
|
||||||
|
fvkutils::SamplerBitmask const& mask, fvkutils::SamplerBitmask const& external,
|
||||||
|
utils::FixedCapacityVector<VkSampler> const& immutableSamplers) {
|
||||||
|
using Bitmask = fvkutils::SamplerBitmask;
|
||||||
|
uint32_t count = 0;
|
||||||
|
Bitmask alreadySeen;
|
||||||
|
uint8_t immutableIndex = 0;
|
||||||
|
size_t const immutableSamplerCount = immutableSamplers.size();
|
||||||
|
mask.forEachSetBit([&](size_t index) {
|
||||||
|
VkShaderStageFlags stages = 0;
|
||||||
|
uint32_t binding = 0;
|
||||||
|
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
|
||||||
|
binding = (uint32_t) index;
|
||||||
|
stages |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
|
||||||
|
if (mask.test(fragIndex)) {
|
||||||
|
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
alreadySeen.set(fragIndex);
|
||||||
|
}
|
||||||
|
} else if (!alreadySeen.test(index)) {
|
||||||
|
// We are in fragment stage bits
|
||||||
|
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
|
||||||
|
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stages) {
|
||||||
|
toBind[count++] = {
|
||||||
|
.binding = binding,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = stages,
|
||||||
|
.pImmutableSamplers = external[index] && immutableSamplerCount > immutableIndex
|
||||||
|
? &immutableSamplers[immutableIndex++]
|
||||||
|
: nullptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
|
||||||
|
size_t const size = samplers.size();
|
||||||
|
if (size == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (size == 1) {
|
||||||
|
return (uint64_t) samplers[0];
|
||||||
|
}
|
||||||
|
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
||||||
@@ -73,37 +125,44 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
|
||||||
|
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||||
|
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
|
||||||
|
LayoutKey key = {
|
||||||
|
.bitmask = bitmasks,
|
||||||
|
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
|
||||||
|
};
|
||||||
|
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
|
||||||
|
uint32_t count = 0;
|
||||||
|
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||||
|
bitmasks.dynamicUbo);
|
||||||
|
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
|
||||||
|
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, bitmasks.externalSampler,
|
||||||
|
immutableSamplers);
|
||||||
|
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||||
|
bitmasks.inputAttachment);
|
||||||
|
|
||||||
|
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
|
||||||
|
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||||
|
.bindingCount = count,
|
||||||
|
.pBindings = toBind,
|
||||||
|
};
|
||||||
|
VkDescriptorSetLayout vklayout;
|
||||||
|
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
||||||
|
mVkLayouts[key] = vklayout;
|
||||||
|
return vklayout;
|
||||||
|
}
|
||||||
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
|
||||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
|
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
|
||||||
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
|
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
|
||||||
info);
|
info);
|
||||||
VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
|
layout->setVkLayout(getVkLayout(layout->bitmask));
|
||||||
auto const& bitmasks = layout->bitmask;
|
|
||||||
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
|
|
||||||
vklayout = itr->second;
|
|
||||||
} else {
|
|
||||||
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
|
|
||||||
uint32_t count = 0;
|
|
||||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
|
||||||
bitmasks.dynamicUbo);
|
|
||||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
|
|
||||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
bitmasks.sampler);
|
|
||||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
|
||||||
bitmasks.inputAttachment);
|
|
||||||
|
|
||||||
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
|
|
||||||
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.bindingCount = count,
|
|
||||||
.pBindings = toBind,
|
|
||||||
};
|
|
||||||
|
|
||||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
|
||||||
mVkLayouts[bitmasks] = vklayout;
|
|
||||||
}
|
|
||||||
layout->setVkLayout(vklayout);
|
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,11 @@
|
|||||||
#include <backend/TargetBufferInfo.h>
|
#include <backend/TargetBufferInfo.h>
|
||||||
|
|
||||||
#include <utils/bitset.h>
|
#include <utils/bitset.h>
|
||||||
|
#include <utils/FixedCapacityVector.h>
|
||||||
|
|
||||||
#include <bluevk/BlueVK.h>
|
#include <bluevk/BlueVK.h>
|
||||||
#include <tsl/robin_map.h>
|
#include <tsl/robin_map.h>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class VulkanDescriptorSetLayoutCache {
|
class VulkanDescriptorSetLayoutCache {
|
||||||
@@ -41,21 +40,34 @@ public:
|
|||||||
|
|
||||||
void terminate() noexcept;
|
void terminate() noexcept;
|
||||||
|
|
||||||
|
// Just a wrapper around getVkLayout()
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
|
||||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
|
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
|
||||||
|
|
||||||
|
// This method is meant to be used with external samplers
|
||||||
|
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||||
|
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
fvkmemory::ResourceManager* mResourceManager;
|
fvkmemory::ResourceManager* mResourceManager;
|
||||||
|
|
||||||
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
|
struct LayoutKey {
|
||||||
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
|
// this describes the layout using bitset.
|
||||||
struct BitmaskGroupEqual {
|
VulkanDescriptorSetLayout::Bitmask bitmask = {};
|
||||||
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
|
// number of immutable samplers can be arbitrary; so we hash them into 64-bit.
|
||||||
|
uint64_t immutableSamplerHash = 0;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(LayoutKey) == 48);
|
||||||
|
|
||||||
|
using LayoutKeyHashFn = utils::hash::MurmurHashFn<LayoutKey>;
|
||||||
|
struct LayoutKeyEqual {
|
||||||
|
bool operator()(LayoutKey const& k1, LayoutKey const& k2) const {
|
||||||
|
return k1.bitmask == k2.bitmask && k1.immutableSamplerHash == k2.immutableSamplerHash;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
|
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
|
||||||
mVkLayouts;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ void VulkanDriver::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();
|
||||||
@@ -1523,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)) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include <backend/platforms/VulkanPlatform.h>
|
#include <backend/platforms/VulkanPlatform.h>
|
||||||
|
|
||||||
|
#include <utils/compiler.h> // UTILS_FALLTHROUGH
|
||||||
#include <utils/Panic.h> // ASSERT_POSTCONDITION
|
#include <utils/Panic.h> // ASSERT_POSTCONDITION
|
||||||
|
|
||||||
using namespace bluevk;
|
using namespace bluevk;
|
||||||
@@ -89,8 +90,9 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// TODO: properly handle external sampler
|
|
||||||
case DescriptorType::SAMPLER_EXTERNAL:
|
case DescriptorType::SAMPLER_EXTERNAL:
|
||||||
|
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
|
||||||
|
UTILS_FALLTHROUGH;
|
||||||
case DescriptorType::SAMPLER: {
|
case DescriptorType::SAMPLER: {
|
||||||
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -66,18 +66,21 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
|||||||
|
|
||||||
// The bitmask representation of a set layout.
|
// The bitmask representation of a set layout.
|
||||||
struct Bitmask {
|
struct Bitmask {
|
||||||
// TODO: better utiltize the space below and use bitset instead.
|
|
||||||
fvkutils::UniformBufferBitmask ubo; // 8 bytes
|
fvkutils::UniformBufferBitmask ubo; // 8 bytes
|
||||||
fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes
|
fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes
|
||||||
fvkutils::SamplerBitmask sampler; // 8 bytes
|
fvkutils::SamplerBitmask sampler; // 8 bytes
|
||||||
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
|
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
|
||||||
|
|
||||||
|
// This is a subset of the bitmask.sampler field.
|
||||||
|
fvkutils::SamplerBitmask externalSampler; // 8 bytes
|
||||||
|
|
||||||
bool operator==(Bitmask const& right) const {
|
bool operator==(Bitmask const& right) const {
|
||||||
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
|
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
|
||||||
inputAttachment == right.inputAttachment;
|
inputAttachment == right.inputAttachment &&
|
||||||
|
externalSampler == right.externalSampler;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Bitmask) == 32);
|
static_assert(sizeof(Bitmask) == 40);
|
||||||
|
|
||||||
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set
|
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set
|
||||||
// pools.
|
// pools.
|
||||||
@@ -119,10 +122,16 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
|||||||
|
|
||||||
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
|
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
|
||||||
|
|
||||||
|
// Note that we don't destroy the vklayout. This is done by the layout cache.
|
||||||
~VulkanDescriptorSetLayout() = default;
|
~VulkanDescriptorSetLayout() = default;
|
||||||
|
|
||||||
VkDescriptorSetLayout getVkLayout() const { return mVkLayout; }
|
VkDescriptorSetLayout const& getVkLayout() const noexcept { return mVkLayout; }
|
||||||
void setVkLayout(VkDescriptorSetLayout vklayout) { mVkLayout = vklayout; }
|
|
||||||
|
// It is possible to have the layout switch out due to AHardwarebuffer (external image) format
|
||||||
|
// changes.
|
||||||
|
void setVkLayout(VkDescriptorSetLayout vklayout) noexcept { mVkLayout = vklayout; }
|
||||||
|
|
||||||
|
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
|
||||||
|
|
||||||
Bitmask const bitmask;
|
Bitmask const bitmask;
|
||||||
Count const count;
|
Count const count;
|
||||||
@@ -137,12 +146,11 @@ public:
|
|||||||
// can use to repackage the vk handle.
|
// can use to repackage the vk handle.
|
||||||
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
||||||
|
|
||||||
VulkanDescriptorSet(VkDescriptorSet rawSet,
|
VulkanDescriptorSet(
|
||||||
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
||||||
uint8_t uniqueDynamicUboCount,
|
uint8_t uniqueDynamicUboCount,
|
||||||
OnRecycle&& onRecycleFn)
|
OnRecycle&& onRecycleFn)
|
||||||
: vkSet(rawSet),
|
: dynamicUboMask(dynamicUboMask),
|
||||||
dynamicUboMask(dynamicUboMask),
|
|
||||||
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
||||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||||
|
|
||||||
@@ -152,6 +160,13 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkDescriptorSet const& getVkSet() const noexcept {
|
||||||
|
return mVkSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the only case where you'd set it more than once is with external images/samplers.
|
||||||
|
void setVkSet(VkDescriptorSet vkset) noexcept { mVkSet = vkset; }
|
||||||
|
|
||||||
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
|
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
|
||||||
mOffsets = std::move(offsets);
|
mOffsets = std::move(offsets);
|
||||||
}
|
}
|
||||||
@@ -163,11 +178,11 @@ public:
|
|||||||
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
||||||
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
||||||
|
|
||||||
VkDescriptorSet const vkSet;
|
|
||||||
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
||||||
uint8_t const uniqueDynamicUboCount;
|
uint8_t const uniqueDynamicUboCount;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
VkDescriptorSet mVkSet = VK_NULL_HANDLE;
|
||||||
backend::DescriptorSetOffsetArray mOffsets;
|
backend::DescriptorSetOffsetArray mOffsets;
|
||||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||||
OnRecycle mOnRecycleFn;
|
OnRecycle mOnRecycleFn;
|
||||||
|
|||||||
@@ -33,8 +33,15 @@ VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
|
|||||||
if (UTILS_LIKELY(iter != mCache.end())) {
|
if (UTILS_LIKELY(iter != mCache.end())) {
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
VkSamplerYcbcrConversionInfo ycbcrConversion = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||||
|
.conversion = params.conversion,
|
||||||
|
};
|
||||||
|
|
||||||
auto const& samplerParams = params.sampler;
|
auto const& samplerParams = params.sampler;
|
||||||
VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
VkSamplerCreateInfo samplerInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
.pNext = params.conversion != VK_NULL_HANDLE ? &ycbcrConversion : VK_NULL_HANDLE,
|
||||||
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
|
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
|
||||||
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
|
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
|
||||||
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
|
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
|
||||||
@@ -48,7 +55,8 @@ VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
|
|||||||
.minLod = 0.0f,
|
.minLod = 0.0f,
|
||||||
.maxLod = fvkutils::getMaxLod(samplerParams.filterMin),
|
.maxLod = fvkutils::getMaxLod(samplerParams.filterMin),
|
||||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||||
.unnormalizedCoordinates = VK_FALSE };
|
.unnormalizedCoordinates = VK_FALSE,
|
||||||
|
};
|
||||||
VkSampler sampler;
|
VkSampler sampler;
|
||||||
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
||||||
|
|||||||
@@ -108,11 +108,6 @@ void VulkanSwapChain::present() {
|
|||||||
|
|
||||||
mCommands->flush();
|
mCommands->flush();
|
||||||
|
|
||||||
// call the image ready wait function
|
|
||||||
if (mExplicitImageReadyWait != nullptr) {
|
|
||||||
mExplicitImageReadyWait(swapChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only present if it is not headless. No-op for headless.
|
// We only present if it is not headless. No-op for headless.
|
||||||
if (!mHeadless) {
|
if (!mHeadless) {
|
||||||
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
|
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
|
||||||
@@ -146,7 +141,6 @@ void VulkanSwapChain::acquire(bool& resized) {
|
|||||||
VulkanPlatform::ImageSyncData imageSyncData;
|
VulkanPlatform::ImageSyncData imageSyncData;
|
||||||
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
||||||
mCurrentSwapIndex = imageSyncData.imageIndex;
|
mCurrentSwapIndex = imageSyncData.imageIndex;
|
||||||
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||||
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
||||||
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
|
|||||||
|
|
||||||
void present();
|
void present();
|
||||||
|
|
||||||
void acquire(bool& reized);
|
// Acquire a new image from the swapchain. If the image is not available it would wait until it
|
||||||
|
// is.
|
||||||
|
void acquire(bool& resized);
|
||||||
|
|
||||||
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
|
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
|
||||||
uint32_t const imageIndex = mCurrentSwapIndex;
|
uint32_t const imageIndex = mCurrentSwapIndex;
|
||||||
@@ -99,7 +101,6 @@ private:
|
|||||||
VkExtent2D mExtent;
|
VkExtent2D mExtent;
|
||||||
uint32_t mLayerCount;
|
uint32_t mLayerCount;
|
||||||
uint32_t mCurrentSwapIndex;
|
uint32_t mCurrentSwapIndex;
|
||||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
|
||||||
bool mAcquired;
|
bool mAcquired;
|
||||||
bool mIsFirstRenderPass;
|
bool mIsFirstRenderPass;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
|||||||
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
|
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
|
||||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||||
.format = VK_FORMAT_UNDEFINED,
|
.format = fvkutils::getVkFormat(params.format),
|
||||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||||
@@ -52,6 +52,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
|||||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We could put this in the platform class, but that seems like a bit of an overkill
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
VkExternalFormatANDROID externalFormat = {
|
VkExternalFormatANDROID externalFormat = {
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||||
@@ -59,6 +60,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
|||||||
};
|
};
|
||||||
if (params.externalFormat) {
|
if (params.externalFormat) {
|
||||||
conversionInfo.pNext = &externalFormat;
|
conversionInfo.pNext = &externalFormat;
|
||||||
|
conversionInfo.format = VK_FORMAT_UNDEFINED;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ class VulkanYcbcrConversionCache {
|
|||||||
public:
|
public:
|
||||||
struct Params {
|
struct Params {
|
||||||
SamplerYcbcrConversion conversion = {};
|
SamplerYcbcrConversion conversion = {};
|
||||||
uint32_t padding = 0;
|
TextureFormat format = {};
|
||||||
|
uint16_t padding = 0;
|
||||||
uint64_t externalFormat = 0;
|
uint64_t externalFormat = 0;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Params) == 16);
|
static_assert(sizeof(Params) == 16);
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
|
|||||||
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
|
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
|
||||||
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
|
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
|
||||||
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
|
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
|
||||||
|
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||||
#endif
|
#endif
|
||||||
// MoltenVk is the only non-conformant implementation we're interested in.
|
// MoltenVk is the only non-conformant implementation we're interested in.
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
@@ -326,7 +327,9 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||||
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
|
VkPhysicalDeviceFeatures2 const& features,
|
||||||
|
VkPhysicalDeviceVulkan11Features const& vk11Features,
|
||||||
|
uint32_t graphicsQueueFamilyIndex,
|
||||||
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
|
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
|
||||||
bool requestImageView2DOn3DImage) {
|
bool requestImageView2DOn3DImage) {
|
||||||
VkDevice device;
|
VkDevice device;
|
||||||
@@ -359,14 +362,28 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
|||||||
|
|
||||||
// We could simply enable all supported features, but since that may have performance
|
// We could simply enable all supported features, but since that may have performance
|
||||||
// consequences let's just enable the features we need.
|
// consequences let's just enable the features we need.
|
||||||
VkPhysicalDeviceFeatures enabledFeatures{
|
VkPhysicalDeviceFeatures enabledFeatures = {
|
||||||
.depthClamp = features.features.depthClamp,
|
.depthClamp = features.features.depthClamp,
|
||||||
.samplerAnisotropy = features.features.samplerAnisotropy,
|
.samplerAnisotropy = features.features.samplerAnisotropy,
|
||||||
.textureCompressionETC2 = features.features.textureCompressionETC2,
|
.textureCompressionETC2 = features.features.textureCompressionETC2,
|
||||||
.textureCompressionBC = features.features.textureCompressionBC,
|
.textureCompressionBC = features.features.textureCompressionBC,
|
||||||
.shaderClipDistance = features.features.shaderClipDistance,
|
.shaderClipDistance = features.features.shaderClipDistance,
|
||||||
};
|
};
|
||||||
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
|
||||||
|
VkPhysicalDeviceFeatures2 enabledFeatures2 = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||||
|
.features = enabledFeatures,
|
||||||
|
};
|
||||||
|
chainStruct(&deviceCreateInfo, &enabledFeatures2);
|
||||||
|
|
||||||
|
VkPhysicalDeviceVulkan11Features enabledVk11Features = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||||
|
.multiview = vk11Features.multiview,
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
.samplerYcbcrConversion = vk11Features.samplerYcbcrConversion,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
chainStruct(&deviceCreateInfo, &enabledVk11Features);
|
||||||
|
|
||||||
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
|
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
|
||||||
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
|
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
|
||||||
@@ -383,7 +400,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
|||||||
|
|
||||||
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
|
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
|
||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
|
||||||
.multiview = VK_TRUE,
|
.multiview = vk11Features.multiview,
|
||||||
.multiviewGeometryShader = VK_FALSE,
|
.multiviewGeometryShader = VK_FALSE,
|
||||||
.multiviewTessellationShader = VK_FALSE,
|
.multiviewTessellationShader = VK_FALSE,
|
||||||
};
|
};
|
||||||
@@ -734,6 +751,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
|
||||||
};
|
};
|
||||||
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
|
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
|
||||||
|
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPhysicalDeviceVk11Features);
|
||||||
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
|
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
|
||||||
|
|
||||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||||
@@ -795,10 +813,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mImpl->mDevice == VK_NULL_HANDLE) {
|
if (mImpl->mDevice == VK_NULL_HANDLE) {
|
||||||
mImpl->mDevice =
|
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice,
|
||||||
createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
|
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features,
|
||||||
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
||||||
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
||||||
@@ -826,12 +844,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
if (!mImpl->mSharedContext) {
|
if (!mImpl->mSharedContext) {
|
||||||
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||||
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
|
|
||||||
} else {
|
} else {
|
||||||
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
||||||
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
|
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
|
||||||
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
|
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
|
||||||
context.mMultiviewEnabled = scontext->multiviewSupported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the availability of lazily allocated memory
|
// Check the availability of lazily allocated memory
|
||||||
|
|||||||
@@ -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,11 +38,7 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -90,36 +70,20 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
|||||||
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
|
||||||
|
|||||||
@@ -668,4 +668,111 @@ VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
|
||||||
|
SamplerYcbcrModelConversion model) {
|
||||||
|
switch (model) {
|
||||||
|
case SamplerYcbcrModelConversion::RGB_IDENTITY:
|
||||||
|
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
||||||
|
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
|
||||||
|
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
|
||||||
|
case SamplerYcbcrModelConversion::YCBCR_709:
|
||||||
|
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
|
||||||
|
case SamplerYcbcrModelConversion::YCBCR_601:
|
||||||
|
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
|
||||||
|
case SamplerYcbcrModelConversion::YCBCR_2020:
|
||||||
|
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
|
||||||
|
default:
|
||||||
|
assert_invariant(false &&
|
||||||
|
"Unknown data type, conversion is not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
|
||||||
|
switch (range) {
|
||||||
|
case SamplerYcbcrRange::ITU_FULL:
|
||||||
|
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
||||||
|
case SamplerYcbcrRange::ITU_NARROW:
|
||||||
|
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
||||||
|
default:
|
||||||
|
assert_invariant(false &&
|
||||||
|
"Unknown data type, conversion is not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkChromaLocation getChromaLocation(ChromaLocation loc) {
|
||||||
|
switch (loc) {
|
||||||
|
case ChromaLocation::COSITED_EVEN:
|
||||||
|
return VK_CHROMA_LOCATION_COSITED_EVEN;
|
||||||
|
case ChromaLocation::MIDPOINT:
|
||||||
|
return VK_CHROMA_LOCATION_MIDPOINT;
|
||||||
|
default:
|
||||||
|
assert_invariant(false &&
|
||||||
|
"Unknown data type, conversion is not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model) {
|
||||||
|
switch (model) {
|
||||||
|
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY:
|
||||||
|
return SamplerYcbcrModelConversion::RGB_IDENTITY;
|
||||||
|
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY:
|
||||||
|
return SamplerYcbcrModelConversion::YCBCR_IDENTITY;
|
||||||
|
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709:
|
||||||
|
return SamplerYcbcrModelConversion::YCBCR_709;
|
||||||
|
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601:
|
||||||
|
return SamplerYcbcrModelConversion::YCBCR_601;
|
||||||
|
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020:
|
||||||
|
return SamplerYcbcrModelConversion::YCBCR_2020;
|
||||||
|
default:
|
||||||
|
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range) {
|
||||||
|
switch (range) {
|
||||||
|
case VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
|
||||||
|
return SamplerYcbcrRange::ITU_FULL;
|
||||||
|
case VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
|
||||||
|
return SamplerYcbcrRange::ITU_NARROW;
|
||||||
|
default:
|
||||||
|
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChromaLocation getChromaLocationFilament(VkChromaLocation loc) {
|
||||||
|
switch (loc) {
|
||||||
|
case VK_CHROMA_LOCATION_COSITED_EVEN:
|
||||||
|
return ChromaLocation::COSITED_EVEN;
|
||||||
|
case VK_CHROMA_LOCATION_MIDPOINT:
|
||||||
|
return ChromaLocation::MIDPOINT;
|
||||||
|
default:
|
||||||
|
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex) {
|
||||||
|
switch (c) {
|
||||||
|
case VK_COMPONENT_SWIZZLE_ZERO:
|
||||||
|
return TextureSwizzle::SUBSTITUTE_ZERO;
|
||||||
|
case VK_COMPONENT_SWIZZLE_ONE:
|
||||||
|
return TextureSwizzle::SUBSTITUTE_ONE;
|
||||||
|
case VK_COMPONENT_SWIZZLE_IDENTITY:
|
||||||
|
return (TextureSwizzle) (((uint8_t) TextureSwizzle::CHANNEL_0) + rgbaIndex);
|
||||||
|
case VK_COMPONENT_SWIZZLE_R:
|
||||||
|
return TextureSwizzle::CHANNEL_0;
|
||||||
|
case VK_COMPONENT_SWIZZLE_G:
|
||||||
|
return TextureSwizzle::CHANNEL_1;
|
||||||
|
case VK_COMPONENT_SWIZZLE_B:
|
||||||
|
return TextureSwizzle::CHANNEL_2;
|
||||||
|
case VK_COMPONENT_SWIZZLE_A:
|
||||||
|
return TextureSwizzle::CHANNEL_3;
|
||||||
|
default:
|
||||||
|
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace filament::backend::fvkutils
|
} // namespace filament::backend::fvkutils
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversio
|
|||||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
||||||
VkChromaLocation getChromaLocation(ChromaLocation loc);
|
VkChromaLocation getChromaLocation(ChromaLocation loc);
|
||||||
|
|
||||||
|
// Ycbcr related functions
|
||||||
|
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model);
|
||||||
|
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range);
|
||||||
|
ChromaLocation getChromaLocationFilament(VkChromaLocation loc);
|
||||||
|
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex);
|
||||||
|
|
||||||
inline VkImageViewType getViewType(SamplerType target) {
|
inline VkImageViewType getViewType(SamplerType target) {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case SamplerType::SAMPLER_CUBEMAP:
|
case SamplerType::SAMPLER_CUBEMAP:
|
||||||
|
|||||||
@@ -348,6 +348,10 @@ using SamplerBitmask = utils::bitset64;
|
|||||||
// general.
|
// general.
|
||||||
using InputAttachmentBitmask = utils::bitset64;
|
using InputAttachmentBitmask = utils::bitset64;
|
||||||
|
|
||||||
|
constexpr uint8_t MAX_DESCRIPTOR_SET_BITMASK_BITS =
|
||||||
|
std::max(std::max(sizeof(UniformBufferBitmask), sizeof(SamplerBitmask)),
|
||||||
|
sizeof(InputAttachmentBitmask)) * 8;
|
||||||
|
|
||||||
template<typename Bitmask>
|
template<typename Bitmask>
|
||||||
static constexpr uint8_t getVertexStageShift() noexcept {
|
static constexpr uint8_t getVertexStageShift() noexcept {
|
||||||
// We assume the bottom half of bits are for vertex stages.
|
// We assume the bottom half of bits are for vertex stages.
|
||||||
|
|||||||
@@ -193,49 +193,6 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
|||||||
return mostSignificantBit((sampleCount - 1) & mask);
|
return mostSignificantBit((sampleCount - 1) & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
|
|
||||||
SamplerYcbcrModelConversion model) {
|
|
||||||
switch (model) {
|
|
||||||
case SamplerYcbcrModelConversion::RGB_IDENTITY:
|
|
||||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
|
||||||
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
|
|
||||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
|
|
||||||
case SamplerYcbcrModelConversion::YCBCR_709:
|
|
||||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
|
|
||||||
case SamplerYcbcrModelConversion::YCBCR_601:
|
|
||||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
|
|
||||||
case SamplerYcbcrModelConversion::YCBCR_2020:
|
|
||||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
|
|
||||||
default:
|
|
||||||
assert_invariant(false &&
|
|
||||||
"Unknown data type, conversion is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
|
|
||||||
switch (range) {
|
|
||||||
case SamplerYcbcrRange::ITU_FULL:
|
|
||||||
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
|
||||||
case SamplerYcbcrRange::ITU_NARROW:
|
|
||||||
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
|
||||||
default:
|
|
||||||
assert_invariant(false &&
|
|
||||||
"Unknown data type, conversion is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkChromaLocation getChromaLocation(ChromaLocation loc) {
|
|
||||||
switch (loc) {
|
|
||||||
case ChromaLocation::COSITED_EVEN:
|
|
||||||
return VK_CHROMA_LOCATION_COSITED_EVEN;
|
|
||||||
case ChromaLocation::MIDPOINT:
|
|
||||||
return VK_CHROMA_LOCATION_MIDPOINT;
|
|
||||||
default:
|
|
||||||
assert_invariant(false &&
|
|
||||||
"Unknown data type, conversion is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace filament::backend::fvkutils
|
} // namespace filament::backend::fvkutils
|
||||||
|
|
||||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
#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>
|
||||||
|
|
||||||
#include "CommandStreamDispatcher.h"
|
#include "CommandStreamDispatcher.h"
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
#include "private/backend/Dispatcher.h"
|
#include "private/backend/Dispatcher.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Handle.h>
|
#include <backend/Handle.h>
|
||||||
|
|
||||||
@@ -228,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,9 +270,6 @@ void WebGPUDriver::tick(int) {
|
|||||||
|
|
||||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor{};
|
|
||||||
commandEncoderDescriptor.nextInChain = nullptr;
|
|
||||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||||
@@ -281,12 +286,6 @@ void WebGPUDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::endFrame(uint32_t frameId) {
|
void WebGPUDriver::endFrame(uint32_t frameId) {
|
||||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
|
||||||
mQueue.Submit(1, &mCommandBuffer);
|
|
||||||
mCommandEncoder = nullptr;
|
|
||||||
mCommandBuffer = nullptr;
|
|
||||||
mTextureView = nullptr;
|
|
||||||
mSwapChain->Present();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::flush(int) {
|
void WebGPUDriver::flush(int) {
|
||||||
@@ -296,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) {
|
||||||
@@ -320,13 +331,10 @@ 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) {
|
||||||
@@ -336,16 +344,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 {
|
||||||
@@ -377,7 +385,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||||
return allocHandle<WGPUTexture>();
|
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||||
@@ -397,7 +405,7 @@ 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 {
|
||||||
@@ -405,7 +413,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||||
return allocHandle<WGPUTexture>();
|
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||||
@@ -413,8 +421,7 @@ Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -430,43 +437,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
|
|
||||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
|
||||||
assert_invariant(!mSwapChain);
|
assert_invariant(!mSwapChain);
|
||||||
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);
|
||||||
// TODO configure the surface (maybe before or after creating the swapchain?
|
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||||
// how do we get the surface extent?)
|
mDevice, flags);
|
||||||
// TODO actually create the swapchain
|
assert_invariant(mSwapChain);
|
||||||
// auto onQueueWorkDone = [](wgpu::QueueWorkDoneStatus status, void* /* pUserData */) {
|
|
||||||
// FWGPU_LOGW << "Queued work finished with status: " << status << std::endl;
|
|
||||||
// };
|
|
||||||
// mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus status, void* pUserdata) {
|
|
||||||
// FWGPU_LOGW << "Queued work finished with status:\n";
|
|
||||||
// }, nullptr /* pUserData */);
|
|
||||||
void* userDataPtr = nullptr;
|
|
||||||
mQueue.OnSubmittedWorkDone(
|
|
||||||
wgpu::CallbackMode::AllowProcessEvents,
|
|
||||||
[](wgpu::QueueWorkDoneStatus status, void* pUserData) {
|
|
||||||
FWGPU_LOGW << "Queued work finished with status: " << static_cast<int>(status) << "\n";
|
|
||||||
if (pUserData == nullptr) {
|
|
||||||
// Expected case
|
|
||||||
} else {
|
|
||||||
FWGPU_LOGW << "Unexpected non-null pUserData received.\n";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userDataPtr /* pUserData */
|
|
||||||
);
|
|
||||||
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."
|
||||||
@@ -479,9 +458,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,
|
||||||
@@ -504,13 +480,7 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
|||||||
TextureUsage usage) {}
|
TextureUsage usage) {}
|
||||||
|
|
||||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||||
uint8_t baseLevel, uint8_t levelCount) {
|
uint8_t baseLevel, uint8_t levelCount) {}
|
||||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
|
||||||
WGPUTexture const* src = handleCast<WGPUTexture>(srch);
|
|
||||||
(void) src;
|
|
||||||
// textures.insert(
|
|
||||||
// constructHandle<WGPUTexture>(th, src, baseLevel, levelCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||||
@@ -552,7 +522,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) {}
|
||||||
@@ -704,9 +676,8 @@ void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
|
|||||||
Handle<HwBufferObject> boh) {
|
Handle<HwBufferObject> boh) {
|
||||||
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
|
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
|
||||||
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
|
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
|
||||||
assert_invariant(index < vertexBuffer->mBuffers.size());
|
assert_invariant(index < vertexBuffer->buffers.size());
|
||||||
vertexBuffer->setBuffer(bufferObject, index);
|
vertexBuffer->setBuffer(bufferObject, index);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
||||||
@@ -739,48 +710,71 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||||
mTextureView = mSwapChain->GetNextSurfaceTextureView(params.viewport.width, params.viewport.height);
|
.label = "command_encoder"
|
||||||
wgpu::RenderPassColorAttachment renderPassColorAttachment = {};
|
};
|
||||||
renderPassColorAttachment.view = mTextureView;
|
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||||
renderPassColorAttachment.resolveTarget = nullptr;
|
assert_invariant(mCommandEncoder);
|
||||||
renderPassColorAttachment.loadOp = wgpu::LoadOp::Clear;
|
// TODO: Remove this code once WebGPU pipeline is implemented
|
||||||
renderPassColorAttachment.storeOp = wgpu::StoreOp::Store;
|
static float red = 1.0f;
|
||||||
renderPassColorAttachment.clearValue = wgpu::Color{1, 0 , 0 , 1};
|
if (red - 0.01 > 0) {
|
||||||
renderPassColorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
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 = {};
|
wgpu::RenderPassDescriptor renderPassDescriptor = {
|
||||||
renderPassDescriptor.nextInChain = nullptr;
|
.colorAttachmentCount = 1,
|
||||||
renderPassDescriptor.colorAttachmentCount = 1;
|
.colorAttachments = &renderPassColorAttachment,
|
||||||
renderPassDescriptor.colorAttachments = &renderPassColorAttachment;
|
.depthStencilAttachment = nullptr,
|
||||||
renderPassDescriptor.depthStencilAttachment = nullptr;
|
.timestampWrites = nullptr,
|
||||||
renderPassDescriptor.timestampWrites = nullptr;
|
};
|
||||||
|
|
||||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
mRenderPassEncoder.SetViewport((float)params.viewport.left, (float)params.viewport.bottom,
|
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
|
||||||
(float) params.viewport.width, (float) params.viewport.height, params.depthRange.near, params.depthRange.far);
|
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
|
||||||
// (float) 1024/*params.viewport.width*/, (float) 640/*params.viewport.height*/, params.depthRange.near, params.depthRange.far);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::endRenderPass(int) {
|
void WebGPUDriver::endRenderPass(int) {
|
||||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
|
||||||
mRenderPassEncoder.End();
|
mRenderPassEncoder.End();
|
||||||
mRenderPassEncoder = nullptr;
|
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) {
|
||||||
wgpu::CommandBufferDescriptor commandBufferDescriptor = {};
|
mCommandEncoder = nullptr;
|
||||||
commandBufferDescriptor.nextInChain = nullptr;
|
mQueue.Submit(1, &mCommandBuffer);
|
||||||
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
|
mCommandBuffer = nullptr;
|
||||||
|
mTextureView = nullptr;
|
||||||
// mQueue.Submit(1, &mCommandBuffer);
|
assert_invariant(mSwapChain);
|
||||||
// mCommandBuffer = nullptr;
|
mSwapChain->present();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||||
@@ -837,8 +831,6 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||||
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -30,8 +30,6 @@
|
|||||||
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include "WebGPUHandles.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -41,6 +39,8 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
class WebGPUSwapChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebGPU backend (driver) implementation
|
* WebGPU backend (driver) implementation
|
||||||
*/
|
*/
|
||||||
@@ -62,8 +62,8 @@ 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::CommandEncoder mCommandEncoder = nullptr;
|
||||||
wgpu::TextureView mTextureView = nullptr;
|
wgpu::TextureView mTextureView = nullptr;
|
||||||
@@ -103,11 +103,17 @@ private:
|
|||||||
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
|
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
|
||||||
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename D, typename B>
|
template<typename D, typename B>
|
||||||
D* handleCast(Handle<B> handle) noexcept {
|
D* handleCast(Handle<B> handle) noexcept {
|
||||||
return mHandleAllocator.handle_cast<D*>(handle);
|
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
|
||||||
|
|||||||
@@ -1,19 +1,153 @@
|
|||||||
//
|
/*
|
||||||
// Created by Idris Idris Shah on 3/21/25.
|
* 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 "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 {
|
namespace filament::backend {
|
||||||
|
|
||||||
WGPUVertexBuffer::WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount,
|
|
||||||
Handle<WGPUVertexBufferInfo> vbih)
|
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
|
||||||
: HwVertexBuffer(vextexCount),
|
uint32_t indexCount)
|
||||||
vbih(vbih),
|
: buffer(createIndexBuffer(device, elementSize, indexCount)) {}
|
||||||
mBuffers(MAX_VERTEX_BUFFER_COUNT) {}
|
|
||||||
|
|
||||||
|
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) {}
|
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
|
||||||
|
|
||||||
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
|
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
|
||||||
: HwBufferObject(byteCount),
|
: HwBufferObject(byteCount),
|
||||||
mBindingType(bindingType) {}
|
bufferObjectBinding(bindingType) {}
|
||||||
|
|
||||||
|
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
|
||||||
|
wgpu::ShaderStage ret = wgpu::ShaderStage::None;
|
||||||
|
if (any(ShaderStageFlags::VERTEX & fFlags)) {
|
||||||
|
ret |= wgpu::ShaderStage::Vertex;
|
||||||
|
}
|
||||||
|
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
|
||||||
|
ret |= wgpu::ShaderStage::Fragment;
|
||||||
|
}
|
||||||
|
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
|
||||||
|
ret |= wgpu::ShaderStage::Compute;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
|
||||||
|
wgpu::Device const* device) {
|
||||||
|
assert_invariant(device->Get());
|
||||||
|
|
||||||
|
// 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_"+ layout.label + 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
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
//
|
/*
|
||||||
// Created by Idris Idris Shah on 3/21/25.
|
* 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
|
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||||
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||||
@@ -9,13 +22,18 @@
|
|||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Handle.h>
|
#include <backend/Handle.h>
|
||||||
|
|
||||||
#include <utils/FixedCapacityVector.h>
|
#include <utils/FixedCapacityVector.h>
|
||||||
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
struct WGPUBufferObject;
|
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 {
|
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
||||||
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||||
AttributeArray const& attributes)
|
AttributeArray const& attributes)
|
||||||
@@ -25,28 +43,46 @@ struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct WGPUVertexBuffer : public HwVertexBuffer {
|
struct WGPUVertexBuffer : public HwVertexBuffer {
|
||||||
WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount, Handle<WGPUVertexBufferInfo> vbih);
|
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
|
||||||
void setBuffer(WGPUBufferObject* bufferObject, uint32_t index);
|
Handle<WGPUVertexBufferInfo> vbih);
|
||||||
|
|
||||||
|
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
|
||||||
|
|
||||||
Handle<WGPUVertexBufferInfo> vbih;
|
Handle<WGPUVertexBufferInfo> vbih;
|
||||||
utils::FixedCapacityVector<wgpu::Buffer> mBuffers;
|
utils::FixedCapacityVector<wgpu::Buffer> buffers;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WGPUIndexBuffer : public HwIndexBuffer {
|
struct WGPUIndexBuffer : public HwIndexBuffer {
|
||||||
WGPUIndexBuffer(BufferUsage usage, uint8_t elementSize, uint32_t indexCount);
|
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
|
||||||
|
uint32_t indexCount);
|
||||||
|
|
||||||
wgpu::Buffer buffer;
|
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 {
|
struct WGPUBufferObject : HwBufferObject {
|
||||||
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
|
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
|
||||||
|
|
||||||
wgpu::Buffer mBuffer;
|
wgpu::Buffer buffer;
|
||||||
const BufferObjectBinding mBindingType;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WGPUTexture : public HwTexture {
|
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
|
||||||
public:
|
// Update the struct when used by WebGPU driver.
|
||||||
|
struct WGPUTexture : public HwTexture {
|
||||||
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||||
|
|
||||||
@@ -56,11 +92,22 @@ public:
|
|||||||
wgpu::Texture texture = nullptr;
|
wgpu::Texture texture = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WGPURenderTarget : public HwRenderTarget {
|
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||||
public:
|
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 {
|
class Attachment {
|
||||||
public:
|
public:
|
||||||
friend class WGPURenderTarget;
|
friend struct WGPURenderTarget;
|
||||||
|
|
||||||
Attachment() = default;
|
Attachment() = default;
|
||||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||||
|
|||||||
@@ -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,59 +220,55 @@ 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::GetNextSurfaceTextureView(uint32_t width, uint32_t height) {
|
wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
|
||||||
|
wgpu::Extent2D const& currentSurfaceSize) {
|
||||||
|
setExtent(currentSurfaceSize);
|
||||||
wgpu::SurfaceTexture surfaceTexture;
|
wgpu::SurfaceTexture surfaceTexture;
|
||||||
GetCurrentTexture(width, height, &surfaceTexture);
|
mSurface.GetCurrentTexture(&surfaceTexture);
|
||||||
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::Success) {
|
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a view for this surface texture
|
// Create a view for this surface texture
|
||||||
wgpu::TextureViewDescriptor textureViewDescriptor;
|
// TODO: review these initiliazations as webgpu pipeline gets mature
|
||||||
textureViewDescriptor.nextInChain = nullptr;
|
wgpu::TextureViewDescriptor textureViewDescriptor = {
|
||||||
textureViewDescriptor.label = "Surface texture view";
|
.label = "texture_view",
|
||||||
textureViewDescriptor.format = surfaceTexture.texture.GetFormat();
|
.format = surfaceTexture.texture.GetFormat(),
|
||||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
.dimension = wgpu::TextureViewDimension::e2D,
|
||||||
textureViewDescriptor.baseMipLevel = 0;
|
.baseMipLevel = 0,
|
||||||
textureViewDescriptor.mipLevelCount = 1;
|
.mipLevelCount = 1,
|
||||||
textureViewDescriptor.baseArrayLayer = 0;
|
.baseArrayLayer = 0,
|
||||||
textureViewDescriptor.arrayLayerCount = 1;
|
.arrayLayerCount = 1
|
||||||
textureViewDescriptor.aspect = wgpu::TextureAspect::All;
|
};
|
||||||
wgpu::TextureView textureView = surfaceTexture.texture.CreateView(&textureViewDescriptor);
|
return surfaceTexture.texture.CreateView(&textureViewDescriptor);
|
||||||
|
|
||||||
return textureView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUSwapChain::Present() {
|
void WebGPUSwapChain::present() {
|
||||||
assert_invariant(mSurface);
|
assert_invariant(mSurface);
|
||||||
mSurface.Present();
|
mSurface.Present();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,26 +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&);
|
||||||
wgpu::TextureView GetNextSurfaceTextureView(uint32_t width, uint32_t height);
|
|
||||||
void Present();
|
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
|
||||||
|
|||||||
@@ -23,30 +23,44 @@ namespace test {
|
|||||||
|
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) {
|
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup(cleanup) {
|
||||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniformNames.size());
|
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniforms.size());
|
||||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||||
kLayouts[i] =
|
kLayouts[i] = {
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
|
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER),
|
||||||
};
|
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
|
||||||
|
}
|
||||||
|
|
||||||
|
// This assumes that the uniforms will all be in a single descriptor set at index 1.
|
||||||
|
// If there are shaders with uniforms in other sets then ShaderConfig will need to be expanded
|
||||||
|
// to accommodate that.
|
||||||
|
size_t kDescriptorSetIndex = 1;
|
||||||
filamat::DescriptorSets descriptors;
|
filamat::DescriptorSets descriptors;
|
||||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
descriptors[kDescriptorSetIndex] = filamat::DescriptorSetInfo(config.uniforms.size());
|
||||||
descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[i], {}}};
|
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||||
|
descriptors[kDescriptorSetIndex][i] = {
|
||||||
|
config.uniforms[i].name, kLayouts[i], config.uniforms[i].samplerInfo };
|
||||||
}
|
}
|
||||||
ShaderGenerator shaderGen(
|
ShaderGenerator shaderGen(
|
||||||
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
|
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
|
||||||
BackendTest::sIsMobilePlatform, std::move(descriptors));
|
BackendTest::sIsMobilePlatform, std::move(descriptors));
|
||||||
Program prog = shaderGen.getProgram(api);
|
Program prog = shaderGen.getProgram(api);
|
||||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
|
||||||
prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
|
Program::DescriptorBindingsInfo bindingsInfo(config.uniforms.size());
|
||||||
|
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||||
|
bindingsInfo[i] = {
|
||||||
|
config.uniforms[i].name,
|
||||||
|
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER), i };
|
||||||
}
|
}
|
||||||
|
prog.descriptorBindings(1, bindingsInfo);
|
||||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||||
|
|
||||||
mDescriptorSetLayout = cleanup.add(
|
mDescriptorSetLayout = cleanup.add(
|
||||||
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
api.createDescriptorSetLayout(DescriptorSetLayout{ "kLayouts", kLayouts }));
|
||||||
|
}
|
||||||
|
|
||||||
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||||
|
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
||||||
}
|
}
|
||||||
|
|
||||||
filament::backend::ProgramHandle Shader::getProgram() const {
|
filament::backend::ProgramHandle Shader::getProgram() const {
|
||||||
@@ -57,8 +71,4 @@ filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() co
|
|||||||
return mDescriptorSetLayout;
|
return mDescriptorSetLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
|
|
||||||
return mDescriptorSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -18,14 +18,22 @@
|
|||||||
#define TNT_SHADER_H
|
#define TNT_SHADER_H
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
|
#include "private/filament/SamplerInterfaceBlock.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
struct UniformConfig {
|
||||||
|
utils::CString name;
|
||||||
|
// If not specified this will be DescriptorType::UNIFORM_BUFFER
|
||||||
|
std::optional<filament::backend::DescriptorType> type;
|
||||||
|
std::optional<filament::SamplerInterfaceBlock::SamplerInfo> samplerInfo;
|
||||||
|
};
|
||||||
|
|
||||||
// All describing a shader that should be created.
|
// All describing a shader that should be created.
|
||||||
struct ShaderConfig {
|
struct ShaderConfig {
|
||||||
std::string vertexShader;
|
std::string vertexShader;
|
||||||
std::string fragmentShader;
|
std::string fragmentShader;
|
||||||
std::vector<utils::CString> uniformNames;
|
std::vector<UniformConfig> uniforms;
|
||||||
};
|
};
|
||||||
|
|
||||||
// All values describing a uniform.
|
// All values describing a uniform.
|
||||||
@@ -35,6 +43,7 @@ struct ResolvedUniformBindingConfig {
|
|||||||
uint32_t byteOffset;
|
uint32_t byteOffset;
|
||||||
filament::backend::descriptor_set_t set;
|
filament::backend::descriptor_set_t set;
|
||||||
filament::backend::descriptor_binding_t binding;
|
filament::backend::descriptor_binding_t binding;
|
||||||
|
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
|
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
|
||||||
@@ -46,6 +55,7 @@ struct UniformBindingConfig {
|
|||||||
std::optional<uint32_t> byteOffset;
|
std::optional<uint32_t> byteOffset;
|
||||||
std::optional<filament::backend::descriptor_set_t> set;
|
std::optional<filament::backend::descriptor_set_t> set;
|
||||||
std::optional<filament::backend::descriptor_binding_t> binding;
|
std::optional<filament::backend::descriptor_binding_t> binding;
|
||||||
|
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
|
||||||
|
|
||||||
template<typename UniformType>
|
template<typename UniformType>
|
||||||
ResolvedUniformBindingConfig resolve();
|
ResolvedUniformBindingConfig resolve();
|
||||||
@@ -79,12 +89,14 @@ public:
|
|||||||
|
|
||||||
filament::backend::ProgramHandle getProgram() const;
|
filament::backend::ProgramHandle getProgram() const;
|
||||||
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
||||||
filament::backend::DescriptorSetHandle getDescriptorSet() const;
|
|
||||||
|
filament::backend::DescriptorSetHandle createDescriptorSet(
|
||||||
|
filament::backend::DriverApi& api) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Cleanup& mCleanup;
|
||||||
filament::backend::ProgramHandle mProgram;
|
filament::backend::ProgramHandle mProgram;
|
||||||
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
||||||
filament::backend::DescriptorSetHandle mDescriptorSet;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename UniformType>
|
template<typename UniformType>
|
||||||
@@ -95,7 +107,8 @@ ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
|
|||||||
.bufferSize = bufferSize.value_or(resolvedDataSize),
|
.bufferSize = bufferSize.value_or(resolvedDataSize),
|
||||||
.byteOffset = byteOffset.value_or(0),
|
.byteOffset = byteOffset.value_or(0),
|
||||||
.set = set.value_or(1),
|
.set = set.value_or(1),
|
||||||
.binding = binding.value_or(0)
|
.binding = binding.value_or(0),
|
||||||
|
.descriptorSet = descriptorSet
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +133,16 @@ void Shader::bindUniform(filament::backend::DriverApi& api,
|
|||||||
UniformBindingConfig config) const {
|
UniformBindingConfig config) const {
|
||||||
auto resolvedConfig = config.resolve<UniformType>();
|
auto resolvedConfig = config.resolve<UniformType>();
|
||||||
|
|
||||||
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
|
filament::backend::DescriptorSetHandle descriptorSet;
|
||||||
|
if (resolvedConfig.descriptorSet.has_value()) {
|
||||||
|
descriptorSet = *resolvedConfig.descriptorSet;
|
||||||
|
} else {
|
||||||
|
descriptorSet = createDescriptorSet(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
api.updateDescriptorSetBuffer(descriptorSet, resolvedConfig.binding, hwBuffer, 0,
|
||||||
resolvedConfig.bufferSize);
|
resolvedConfig.bufferSize);
|
||||||
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
|
api.bindDescriptorSet(descriptorSet, resolvedConfig.set, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename UniformType>
|
template<typename UniformType>
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 "SharedShaders.h"
|
||||||
|
|
||||||
|
#include "Shader.h"
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
using namespace filament::backend;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// A shader stored in pieces so that uniform declarations can be injected.
|
||||||
|
struct ShaderText {
|
||||||
|
std::string mPrefix;
|
||||||
|
std::string mBody;
|
||||||
|
|
||||||
|
std::string withUniform(const std::string& uniformText) const {
|
||||||
|
return absl::StrFormat("%s\n%s\n%s", mPrefix.c_str(), uniformText.c_str(), mBody.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<ShaderText> GetGlslVertexShader(VertexShaderType type) {
|
||||||
|
switch (type) {
|
||||||
|
case VertexShaderType::Noop: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||||
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||||
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
#endif
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case VertexShaderType::Simple: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(
|
||||||
|
mesh_position.xy * (params.scaleMinusOne.xy + 1.0) + params.offset.xy,
|
||||||
|
params.scaleMinusOne.z + 1.0,
|
||||||
|
1.0);
|
||||||
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||||
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
#endif
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case VertexShaderType::Textured: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
layout(location = 0) out vec2 uv;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||||
|
uv = (mesh_position.xy * 0.5 + 0.5);
|
||||||
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||||
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
#endif
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ShaderText> GetGlslFragmentShader(FragmentShaderType type) {
|
||||||
|
switch (type) {
|
||||||
|
case FragmentShaderType::White: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
precision mediump int; precision highp float;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case FragmentShaderType::SolidColored: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
precision mediump int; precision highp float;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
fragColor = params.color;
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case FragmentShaderType::Textured: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
precision mediump int; precision highp float;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
layout(location = 0) in vec2 uv;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
fragColor = texture(test_tex, uv);
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> GetGlslUniform(ShaderUniformType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ShaderUniformType::None: {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Simple: {
|
||||||
|
return R"(
|
||||||
|
layout(binding = 0, set = 1) uniform Params {
|
||||||
|
highp vec4 color;
|
||||||
|
// Use scaleMinusOne instead of scale so that a 0 initialized value is a good default
|
||||||
|
highp vec4 scaleMinusOne;
|
||||||
|
highp vec4 offset;
|
||||||
|
} params;
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
case ShaderUniformType::SimpleWithPadding: {
|
||||||
|
return R"(
|
||||||
|
layout(binding = 0, set = 1) uniform Params {
|
||||||
|
highp vec4 padding[4]; // offset of 64 bytes
|
||||||
|
|
||||||
|
highp vec4 color;
|
||||||
|
// Use scaleMinusOne instead of scale so that a 0 initialized value is a good default
|
||||||
|
highp vec4 scaleMinusOne;
|
||||||
|
highp vec4 offset;
|
||||||
|
} params;
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Sampler: {
|
||||||
|
return R"(
|
||||||
|
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UniformConfig> GetUniformConfig(ShaderUniformType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ShaderUniformType::None: {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Simple: {
|
||||||
|
return {{ "Params" }};
|
||||||
|
}
|
||||||
|
case ShaderUniformType::SimpleWithPadding: {
|
||||||
|
return {{ "Params" }};
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Sampler: {
|
||||||
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo{
|
||||||
|
"backend_test", "test_tex", 0,
|
||||||
|
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||||
|
return {{
|
||||||
|
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderLanguage getShaderLanguage(const Backend& backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case Backend::METAL:
|
||||||
|
return ShaderLanguage::MSL;
|
||||||
|
case Backend::WEBGPU:
|
||||||
|
return ShaderLanguage::WGSL;
|
||||||
|
case Backend::VULKAN:
|
||||||
|
case Backend::NOOP:
|
||||||
|
case Backend::OPENGL:
|
||||||
|
default: {
|
||||||
|
return ShaderLanguage::GLSL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Shader SharedShaders::makeShader(filament::backend::DriverApi& api, Cleanup& cleanup,
|
||||||
|
ShaderRequest request) {
|
||||||
|
std::optional<ShaderText> vertex;
|
||||||
|
std::optional<ShaderText> fragment;
|
||||||
|
std::optional<std::string> uniform;
|
||||||
|
if (getShaderLanguage(BackendTest::sBackend) != ShaderLanguage::GLSL) {
|
||||||
|
// TODO: If any shaders need backend/shader language specific shaders rather than transpiled
|
||||||
|
// versions of the GLSL shader, check environment.
|
||||||
|
}
|
||||||
|
vertex = GetGlslVertexShader(request.mVertexType);
|
||||||
|
fragment = GetGlslFragmentShader(request.mFragmentType);
|
||||||
|
uniform = GetGlslUniform(request.mUniformType);
|
||||||
|
if (vertex.has_value() && fragment.has_value() && uniform.has_value()) {
|
||||||
|
return Shader(
|
||||||
|
api, cleanup, ShaderConfig{
|
||||||
|
vertex->withUniform(*uniform), fragment->withUniform(*uniform),
|
||||||
|
GetUniformConfig(request.mUniformType)}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SharedShaders::getVertexShaderText(VertexShaderType vertex, ShaderUniformType uniform) {
|
||||||
|
std::optional<ShaderText> vertexText = GetGlslVertexShader(vertex);
|
||||||
|
std::optional<std::string> uniformText = GetGlslUniform(uniform);
|
||||||
|
if (!vertexText.has_value() || !uniformText.has_value()) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return vertexText->withUniform(*uniformText);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SharedShaders::getFragmentShaderText(FragmentShaderType fragment,
|
||||||
|
ShaderUniformType uniform) {
|
||||||
|
std::optional<ShaderText> fragmentText = GetGlslFragmentShader(fragment);
|
||||||
|
std::optional<std::string> uniformText = GetGlslUniform(uniform);
|
||||||
|
if (!fragmentText.has_value() || !uniformText.has_value()) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return fragmentText->withUniform(*uniformText);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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_SHAREDSHADERS_H
|
||||||
|
#define TNT_SHAREDSHADERS_H
|
||||||
|
|
||||||
|
#include "Shader.h"
|
||||||
|
#include "SharedShadersConstants.h"
|
||||||
|
#include "Lifetimes.h"
|
||||||
|
#include "PlatformRunner.h"
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
enum class ShaderLanguage : uint8_t {
|
||||||
|
GLSL,
|
||||||
|
MSL,
|
||||||
|
WGSL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderRequest {
|
||||||
|
VertexShaderType mVertexType;
|
||||||
|
FragmentShaderType mFragmentType;
|
||||||
|
ShaderUniformType mUniformType;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SharedShaders {
|
||||||
|
public:
|
||||||
|
static Shader makeShader(filament::backend::DriverApi& api, Cleanup& cleanup,
|
||||||
|
ShaderRequest request);
|
||||||
|
static std::string getVertexShaderText(VertexShaderType vertex, ShaderUniformType uniform);
|
||||||
|
static std::string getFragmentShaderText(FragmentShaderType fragment,
|
||||||
|
ShaderUniformType uniform);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
#endif //TNT_SHAREDSHADERS_H
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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_SHAREDSHADERSCONSTANTS_H
|
||||||
|
#define TNT_SHAREDSHADERSCONSTANTS_H
|
||||||
|
|
||||||
|
#include "math/mathfwd.h"
|
||||||
|
|
||||||
|
enum class ShaderUniformType : uint8_t {
|
||||||
|
None,
|
||||||
|
Simple,
|
||||||
|
SimpleWithPadding,
|
||||||
|
Sampler,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SimpleMaterialParams {
|
||||||
|
filament::math::float4 color;
|
||||||
|
// 1.0 will be added to this value before use.
|
||||||
|
// The XY values are used to scale position inputs and the Z value is used to set the output
|
||||||
|
// position's Z value.
|
||||||
|
filament::math::float4 scaleMinusOne;
|
||||||
|
// Offset will be applied after scale
|
||||||
|
filament::math::float4 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SimpleWithPaddingMaterialParams {
|
||||||
|
// The associated uniform structure in the shader will have 64 bytes of padding at the beginning
|
||||||
|
// So users of this struct will need to add 64 bytes to its size and offset all uniform writes.
|
||||||
|
filament::math::float4 color;
|
||||||
|
// 1.0 will be added to this value before use.
|
||||||
|
// The XY values are used to scale position inputs and the Z value is used to set the output
|
||||||
|
// position's Z value.
|
||||||
|
filament::math::float4 scaleMinusOne;
|
||||||
|
// Offset will be applied after scale
|
||||||
|
filament::math::float4 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class VertexShaderType : uint8_t {
|
||||||
|
Noop,
|
||||||
|
Simple,
|
||||||
|
Textured
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FragmentShaderType : uint8_t {
|
||||||
|
White,
|
||||||
|
SolidColored,
|
||||||
|
Textured
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //TNT_SHAREDSHADERSCONSTANTS_H
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "ImageExpectations.h"
|
#include "ImageExpectations.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <utils/Hash.h>
|
#include <utils/Hash.h>
|
||||||
@@ -40,48 +41,14 @@ using namespace filament::backend;
|
|||||||
using namespace filament::math;
|
using namespace filament::math;
|
||||||
using namespace utils;
|
using namespace utils;
|
||||||
|
|
||||||
struct MaterialParams {
|
|
||||||
float4 color;
|
|
||||||
float4 scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlitTest : public BackendTest {
|
class BlitTest : public BackendTest {
|
||||||
public:
|
public:
|
||||||
BlitTest() : mCleanup(getDriverApi()) {}
|
BlitTest() : mCleanup(getDriverApi()) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Shader createShader();
|
|
||||||
|
|
||||||
Cleanup mCleanup;
|
Cleanup mCleanup;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* const triangleVs = R"(#version 450 core
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params;
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4((mesh_position.xy + 0.5) * params.scale.xy, params.scale.z, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y = -gl_Position.y;
|
|
||||||
#endif
|
|
||||||
})";
|
|
||||||
|
|
||||||
static const char* const triangleFs = R"(#version 450 core
|
|
||||||
precision mediump int; precision highp float;
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params;
|
|
||||||
void main() {
|
|
||||||
fragColor = params.color;
|
|
||||||
})";
|
|
||||||
|
|
||||||
Shader BlitTest::createShader() {
|
|
||||||
return Shader(getDriverApi(), mCleanup, ShaderConfig{
|
|
||||||
.vertexShader = triangleVs,
|
|
||||||
.fragmentShader = triangleFs,
|
|
||||||
.uniformNames = { "Params" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t toUintColor(float4 color) {
|
static uint32_t toUintColor(float4 color) {
|
||||||
color = saturate(color);
|
color = saturate(color);
|
||||||
uint32_t r = color.r * 255.0f;
|
uint32_t r = color.r * 255.0f;
|
||||||
@@ -313,7 +280,11 @@ TEST_F(BlitTest, ColorResolve) {
|
|||||||
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
|
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
|
||||||
constexpr int kSampleCount = 4;
|
constexpr int kSampleCount = 4;
|
||||||
|
|
||||||
Shader shader = createShader();
|
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{
|
||||||
|
.mVertexType = VertexShaderType::Simple,
|
||||||
|
.mFragmentType = FragmentShaderType::SolidColored,
|
||||||
|
.mUniformType = ShaderUniformType::Simple,
|
||||||
|
});
|
||||||
|
|
||||||
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
||||||
TrianglePrimitive const triangle(api);
|
TrianglePrimitive const triangle(api);
|
||||||
@@ -356,14 +327,15 @@ TEST_F(BlitTest, ColorResolve) {
|
|||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
state.rasterState.culling = CullingMode::NONE;
|
state.rasterState.culling = CullingMode::NONE;
|
||||||
|
|
||||||
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
|
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams),
|
||||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||||
// Draw red triangle into srcRenderTarget.
|
// Draw red triangle into srcRenderTarget.
|
||||||
shader.uploadUniform(api, ubuffer, MaterialParams{
|
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{
|
||||||
.color = float4(1, 0, 0, 1),
|
.color = float4(1, 0, 0, 1),
|
||||||
.scale = float4(1, 1, 0.5, 0),
|
.scaleMinusOne = float4(0, 0, -0.5, 0),
|
||||||
|
.offset = float4(0.5, 0.5, 0, 0),
|
||||||
});
|
});
|
||||||
shader.bindUniform<MaterialParams>(api, ubuffer);
|
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
|
||||||
|
|
||||||
// FIXME: on Metal this triangle is not drawn. Can't understand why.
|
// FIXME: on Metal this triangle is not drawn. Can't understand why.
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,72 +18,18 @@
|
|||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Shaders
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
|
|
||||||
layout(location = 0) out uvec4 indices;
|
|
||||||
|
|
||||||
layout(binding = 0, set = 1) uniform Params {
|
|
||||||
highp vec4 padding[4]; // offset of 64 bytes
|
|
||||||
|
|
||||||
highp vec4 color;
|
|
||||||
highp vec4 offset;
|
|
||||||
} params;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy + params.offset.xy, 0.0, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y = -gl_Position.y;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragment (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
layout(binding = 0, set = 1) uniform Params {
|
|
||||||
highp vec4 padding[4]; // offset of 64 bytes
|
|
||||||
|
|
||||||
highp vec4 color;
|
|
||||||
highp vec4 offset;
|
|
||||||
} params;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(params.color.rgb, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
using namespace filament;
|
using namespace filament;
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
// In the shader, these MaterialParams are offset by 64 bytes into the uniform buffer to test buffer
|
|
||||||
// updates with offset.
|
|
||||||
struct MaterialParams {
|
|
||||||
math::float4 color;
|
|
||||||
math::float4 offset;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(MaterialParams) == 8 * sizeof(float));
|
|
||||||
// Uniform config for writing MaterialParams to the shader uniform with 64 bytes of padding.
|
// Uniform config for writing MaterialParams to the shader uniform with 64 bytes of padding.
|
||||||
const UniformBindingConfig kBindingConfig = {
|
const UniformBindingConfig kBindingConfig = {
|
||||||
.dataSize = sizeof(MaterialParams),
|
.dataSize = sizeof(SimpleMaterialParams),
|
||||||
.bufferSize = sizeof(MaterialParams) + 64,
|
.bufferSize = sizeof(SimpleMaterialParams) + 64,
|
||||||
.byteOffset = 64
|
.byteOffset = 64
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,8 +39,10 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
Shader createShader() {
|
Shader createShader() {
|
||||||
return Shader(getDriverApi(), mCleanup, ShaderConfig{
|
return SharedShaders::makeShader(getDriverApi(), mCleanup, ShaderRequest{
|
||||||
vertex, fragment, {"Params"}
|
.mVertexType = VertexShaderType::Simple,
|
||||||
|
.mFragmentType = FragmentShaderType::SolidColored,
|
||||||
|
.mUniformType = ShaderUniformType::SimpleWithPadding
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +77,7 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
|||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
@@ -144,17 +92,18 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
|||||||
// Create a uniform buffer.
|
// Create a uniform buffer.
|
||||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use
|
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use
|
||||||
// a GPU buffer, which is more interesting to test.
|
// a GPU buffer, which is more interesting to test.
|
||||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
|
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams) + 64,
|
||||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||||
|
|
||||||
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
|
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||||
|
|
||||||
api.startCapture(0);
|
api.startCapture(0);
|
||||||
|
|
||||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||||
// uniform definition.
|
// uniform definition.
|
||||||
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
|
shader.uploadUniform(api, ubuffer, kBindingConfig, SimpleMaterialParams{
|
||||||
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
|
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
|
||||||
|
.scaleMinusOne = { 0.0, 0.0, 0.0, 0.0 },
|
||||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -165,19 +114,21 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
|||||||
size_t triangleIndex = 0;
|
size_t triangleIndex = 0;
|
||||||
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
|
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
|
||||||
const float low = i, high = i + 0.2;
|
const float low = i, high = i + 0.2;
|
||||||
const filament::math::float2 v[3] {{low, low}, {high, low}, {low, high}};
|
const filament::math::float2 v[3]{{ low, low },
|
||||||
|
{ high, low },
|
||||||
|
{ low, high }};
|
||||||
triangle.updateVertices(v);
|
triangle.updateVertices(v);
|
||||||
|
|
||||||
if (updateIndices) {
|
if (updateIndices) {
|
||||||
if (triangleIndex % 2 == 0) {
|
if (triangleIndex % 2 == 0) {
|
||||||
// Upload each index separately, to test offsets.
|
// Upload each index separately, to test offsets.
|
||||||
const TrianglePrimitive::index_type i[3] {0, 1, 2};
|
const TrianglePrimitive::index_type i[3]{ 0, 1, 2 };
|
||||||
triangle.updateIndices(i + 0, 1, 0);
|
triangle.updateIndices(i + 0, 1, 0);
|
||||||
triangle.updateIndices(i + 1, 1, 1);
|
triangle.updateIndices(i + 1, 1, 1);
|
||||||
triangle.updateIndices(i + 2, 1, 2);
|
triangle.updateIndices(i + 2, 1, 2);
|
||||||
} else {
|
} else {
|
||||||
// This effectively hides this triangle.
|
// This effectively hides this triangle.
|
||||||
const TrianglePrimitive::index_type i[3] {0, 0, 0};
|
const TrianglePrimitive::index_type i[3]{ 0, 0, 0 };
|
||||||
triangle.updateIndices(i);
|
triangle.updateIndices(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,28 +171,29 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
|||||||
// Create a uniform buffer.
|
// Create a uniform buffer.
|
||||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
|
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
|
||||||
// GPU buffer, which is more interesting to test.
|
// GPU buffer, which is more interesting to test.
|
||||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
|
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams) + 64,
|
||||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||||
|
|
||||||
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
|
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||||
|
|
||||||
// Create a render target.
|
// Create a render target.
|
||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {}));
|
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{ colorTexture }}, {}, {}));
|
||||||
|
|
||||||
// Upload uniforms for the first triangle.
|
// Upload uniforms for the first triangle.
|
||||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||||
// uniform definition.
|
// uniform definition.
|
||||||
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
|
shader.uploadUniform(api, ubuffer, kBindingConfig, SimpleMaterialParams{
|
||||||
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
|
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
|
||||||
|
.scaleMinusOne = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||||
});
|
});
|
||||||
|
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
params.viewport.height = 512;
|
params.viewport.height = 512;
|
||||||
@@ -250,18 +202,18 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
|||||||
renderTarget, swapChain, shader.getProgram(), params);
|
renderTarget, swapChain, shader.getProgram(), params);
|
||||||
|
|
||||||
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
|
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
|
||||||
// color.b, color.a, offset.x, and offset.y.
|
// color.b, color.a, scaleMinusOne, offset.x, and offset.y.
|
||||||
shader.uploadUniform(api, ubuffer, UniformBindingConfig{
|
const UniformBindingConfig partialBindingConfig = {
|
||||||
.dataSize = sizeof(std::array<float, 4>),
|
.dataSize = sizeof(float) * 8,
|
||||||
.bufferSize = kBindingConfig.bufferSize,
|
.bufferSize = sizeof(SimpleMaterialParams) + 64,
|
||||||
.byteOffset = *kBindingConfig.byteOffset + offsetof(MaterialParams, color.b),
|
.byteOffset = 64 + offsetof(SimpleMaterialParams, color.b)
|
||||||
},
|
};
|
||||||
std::array<float, 4>{
|
shader.uploadUniform(api, ubuffer, partialBindingConfig,
|
||||||
// color.b, color.a
|
std::array<float, 8>{
|
||||||
1.0f, 1.0f,
|
1.0f, 1.0f, // color.b, color.a
|
||||||
// offset.x, offset.y
|
0.0f, 0.0f, 0.0f, 0.0f, // scale
|
||||||
0.5f, 0.5f }
|
0.5f, 0.5f // offset.x, offset.y
|
||||||
);
|
});
|
||||||
|
|
||||||
params.flags.clear = TargetBufferFlags::NONE;
|
params.flags.clear = TargetBufferFlags::NONE;
|
||||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
@@ -96,16 +96,6 @@ struct MaterialParams {
|
|||||||
float unused;
|
float unused;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
|
|
||||||
MaterialParams* tmp = new MaterialParams(params);
|
|
||||||
auto cb = [](void* buffer, size_t size, void* user) {
|
|
||||||
MaterialParams* sp = (MaterialParams*) buffer;
|
|
||||||
delete sp;
|
|
||||||
};
|
|
||||||
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
|
|
||||||
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
||||||
const size_t size = kTexWidth * kTexHeight * 4;
|
const size_t size = kTexWidth * kTexHeight * 4;
|
||||||
void* buffer = calloc(1, size);
|
void* buffer = calloc(1, size);
|
||||||
@@ -140,38 +130,13 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program.
|
// Create a program.
|
||||||
ProgramHandle program;
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
{
|
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
|
||||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||||
filamat::DescriptorSets descriptors;
|
Shader shader = Shader(api, cleanup, ShaderConfig {
|
||||||
descriptors[1] = {
|
.vertexShader = fullscreenVs,
|
||||||
{ "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
.fragmentShader = fullscreenFs,
|
||||||
samplerInfo },
|
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}, {"Params"}}
|
||||||
{ "Params", { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 1 }, {} }
|
});
|
||||||
};
|
|
||||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform,
|
|
||||||
std::move(descriptors));
|
|
||||||
Program prog = shaderGen.getProgram(api);
|
|
||||||
prog.descriptorBindings(1, {
|
|
||||||
{ "test_tex", DescriptorType::SAMPLER, 0 },
|
|
||||||
{ "Params", DescriptorType::UNIFORM_BUFFER, 1 }
|
|
||||||
});
|
|
||||||
program = cleanup.add(api.createProgram(std::move(prog)));
|
|
||||||
}
|
|
||||||
|
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DescriptorType::UNIFORM_BUFFER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 1,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}}));
|
|
||||||
|
|
||||||
|
|
||||||
TrianglePrimitive const triangle(getDriverApi());
|
TrianglePrimitive const triangle(getDriverApi());
|
||||||
|
|
||||||
@@ -218,8 +183,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
state.program = program;
|
state.program = shader.getProgram();
|
||||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||||
|
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
@@ -233,20 +198,24 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
params.viewport.width = kTexWidth >> targetLevel;
|
params.viewport.width = kTexWidth >> targetLevel;
|
||||||
params.viewport.height = kTexHeight >> targetLevel;
|
params.viewport.height = kTexHeight >> targetLevel;
|
||||||
|
|
||||||
|
auto descriptorSet = shader.createDescriptorSet(api);
|
||||||
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
||||||
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
||||||
.filterMag = SamplerMagFilter::LINEAR,
|
.filterMag = SamplerMagFilter::LINEAR,
|
||||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||||
});
|
});
|
||||||
api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams));
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
|
||||||
|
|
||||||
uploadUniforms(getDriverApi(), ubuffer, {
|
UniformBindingConfig uniformBinding{
|
||||||
|
.binding = 1,
|
||||||
|
.descriptorSet = descriptorSet
|
||||||
|
};
|
||||||
|
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
|
||||||
|
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
|
||||||
.fbWidth = float(params.viewport.width),
|
.fbWidth = float(params.viewport.width),
|
||||||
.fbHeight = float(params.viewport.height),
|
.fbHeight = float(params.viewport.height),
|
||||||
.sourceLevel = float(sourceLevel),
|
.sourceLevel = float(sourceLevel),
|
||||||
});
|
});
|
||||||
|
|
||||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
@@ -262,20 +231,24 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
params.viewport.width = kTexWidth >> targetLevel;
|
params.viewport.width = kTexWidth >> targetLevel;
|
||||||
params.viewport.height = kTexHeight >> targetLevel;
|
params.viewport.height = kTexHeight >> targetLevel;
|
||||||
|
|
||||||
|
auto descriptorSet = shader.createDescriptorSet(api);
|
||||||
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
||||||
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
||||||
.filterMag = SamplerMagFilter::LINEAR,
|
.filterMag = SamplerMagFilter::LINEAR,
|
||||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||||
});
|
});
|
||||||
api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams));
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
|
||||||
|
|
||||||
uploadUniforms(getDriverApi(), ubuffer, {
|
UniformBindingConfig uniformBinding{
|
||||||
.fbWidth = float(params.viewport.width),
|
.binding = 1,
|
||||||
.fbHeight = float(params.viewport.height),
|
.descriptorSet = descriptorSet
|
||||||
.sourceLevel = float(sourceLevel),
|
};
|
||||||
|
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
|
||||||
|
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
|
||||||
|
.fbWidth = float(params.viewport.width),
|
||||||
|
.fbHeight = float(params.viewport.height),
|
||||||
|
.sourceLevel = float(sourceLevel),
|
||||||
});
|
});
|
||||||
|
|
||||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|||||||
@@ -18,16 +18,14 @@
|
|||||||
|
|
||||||
#include "BackendTestUtils.h"
|
#include "BackendTestUtils.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "SharedShaders.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Handle.h>
|
#include <backend/Handle.h>
|
||||||
|
|
||||||
#include "private/filament/SamplerInterfaceBlock.h"
|
#include "private/filament/SamplerInterfaceBlock.h"
|
||||||
|
|
||||||
#include <math/half.h>
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@@ -42,19 +40,6 @@ namespace {
|
|||||||
// Shaders
|
// Shaders
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y *= -1.0f;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragmentTemplate (R"(#version 450 core
|
std::string fragmentTemplate (R"(#version 450 core
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
@@ -119,6 +104,15 @@ namespace test {
|
|||||||
|
|
||||||
template<typename componentType> inline componentType getMaxValue();
|
template<typename componentType> inline componentType getMaxValue();
|
||||||
|
|
||||||
|
class LoadImageTest : public BackendTest {
|
||||||
|
public:
|
||||||
|
LoadImageTest() {
|
||||||
|
mVertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
|
||||||
|
ShaderUniformType::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mVertexShader;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
inline std::string stringReplace(const std::string& find, const std::string& replace,
|
inline std::string stringReplace(const std::string& find, const std::string& replace,
|
||||||
@@ -216,7 +210,7 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BackendTest, UpdateImage2D) {
|
TEST_F(LoadImageTest, UpdateImage2D) {
|
||||||
|
|
||||||
// All of these test cases should result in the same rendered image, and thus the same hash.
|
// All of these test cases should result in the same rendered image, and thus the same hash.
|
||||||
static const uint32_t expectedHash = 3644679986;
|
static const uint32_t expectedHash = 3644679986;
|
||||||
@@ -306,28 +300,14 @@ TEST_F(BackendTest, UpdateImage2D) {
|
|||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false };
|
||||||
filamat::DescriptorSets descriptors;
|
|
||||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
|
||||||
samplerInfo } };
|
|
||||||
|
|
||||||
std::string const fragment = stringReplace("{samplerType}",
|
std::string const fragment = stringReplace("{samplerType}",
|
||||||
getSamplerTypeName(t.textureFormat), fragmentTemplate);
|
getSamplerTypeName(t.textureFormat), fragmentTemplate);
|
||||||
ShaderGenerator shaderGen(
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
.vertexShader = mVertexShader,
|
||||||
|
.fragmentShader= fragment,
|
||||||
Program prog = shaderGen.getProgram(api);
|
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
});
|
||||||
|
|
||||||
ProgramHandle const program = cleanup.add(api.createProgram(std::move(prog)));
|
|
||||||
|
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}}));
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
|
|
||||||
// Create a Texture.
|
// Create a Texture.
|
||||||
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||||
@@ -349,14 +329,15 @@ TEST_F(BackendTest, UpdateImage2D) {
|
|||||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
|
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||||
.filterMag = SamplerMagFilter::NEAREST,
|
.filterMag = SamplerMagFilter::NEAREST,
|
||||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||||
|
|
||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, program);
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
|
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
|
||||||
|
|
||||||
@@ -370,8 +351,9 @@ TEST_F(BackendTest, UpdateImage2D) {
|
|||||||
flushAndWait();
|
flushAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BackendTest, UpdateImageSRGB) {
|
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
Cleanup cleanup(api);
|
||||||
api.startCapture();
|
api.startCapture();
|
||||||
|
|
||||||
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
|
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
|
||||||
@@ -379,36 +361,23 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
|||||||
TextureFormat const textureFormat = TextureFormat::SRGB8_A8;
|
TextureFormat const textureFormat = TextureFormat::SRGB8_A8;
|
||||||
|
|
||||||
// Create a platform-specific SwapChain and make it current.
|
// Create a platform-specific SwapChain and make it current.
|
||||||
auto swapChain = createSwapChain();
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||||
|
|
||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||||
filamat::DescriptorSets descriptors;
|
|
||||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
|
||||||
samplerInfo } };
|
|
||||||
|
|
||||||
std::string const fragment = stringReplace("{samplerType}",
|
std::string const fragment = stringReplace("{samplerType}",
|
||||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
getSamplerTypeName(textureFormat), fragmentTemplate);
|
||||||
ShaderGenerator shaderGen(
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
||||||
Program prog = shaderGen.getProgram(api);
|
"text_tex", DescriptorType::SAMPLER, samplerInfo
|
||||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
}}});
|
||||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}});
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
|
||||||
|
|
||||||
// Create a texture.
|
// Create a texture.
|
||||||
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
|
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
||||||
|
|
||||||
// Create image data.
|
// Create image data.
|
||||||
size_t components; int bpp;
|
size_t components; int bpp;
|
||||||
@@ -436,6 +405,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
|||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
// Update samplers.
|
// Update samplers.
|
||||||
|
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
|
||||||
@@ -443,8 +413,8 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||||
|
|
||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, program);
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
static const uint32_t expectedHash = 359858623;
|
static const uint32_t expectedHash = 359858623;
|
||||||
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
|
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
|
||||||
@@ -453,21 +423,14 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
|||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
api.destroyDescriptorSet(descriptorSet);
|
|
||||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
|
||||||
api.destroyProgram(program);
|
|
||||||
api.destroySwapChain(swapChain);
|
|
||||||
api.destroyRenderTarget(defaultRenderTarget);
|
|
||||||
|
|
||||||
// This ensures all driver commands have finished before exiting the test.
|
// This ensures all driver commands have finished before exiting the test.
|
||||||
api.finish();
|
api.finish();
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BackendTest, UpdateImageMipLevel) {
|
TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
Cleanup cleanup(api);
|
||||||
api.startCapture();
|
api.startCapture();
|
||||||
|
|
||||||
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
||||||
@@ -475,38 +438,27 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
|||||||
TextureFormat textureFormat = TextureFormat::RGBA32F;
|
TextureFormat textureFormat = TextureFormat::RGBA32F;
|
||||||
|
|
||||||
// Create a platform-specific SwapChain and make it current.
|
// Create a platform-specific SwapChain and make it current.
|
||||||
auto swapChain = createSwapChain();
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||||
|
|
||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||||
filamat::DescriptorSets descriptors;
|
|
||||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
|
||||||
samplerInfo } };
|
|
||||||
std::string const fragment = stringReplace("{samplerType}",
|
std::string const fragment = stringReplace("{samplerType}",
|
||||||
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
|
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
|
||||||
ShaderGenerator shaderGen(
|
Shader shader(api, cleanup, ShaderConfig {
|
||||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
.vertexShader = mVertexShader,
|
||||||
Program prog = shaderGen.getProgram(api);
|
.fragmentShader = fragment,
|
||||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
});
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}});
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
|
||||||
|
|
||||||
// Create a texture with 3 mip levels.
|
// Create a texture with 3 mip levels.
|
||||||
// Base level: 1024
|
// Base level: 1024
|
||||||
// Level 1: 512 <-- upload data and sample from this level
|
// Level 1: 512 <-- upload data and sample from this level
|
||||||
// Level 2: 256
|
// Level 2: 256
|
||||||
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, 3,
|
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 3,
|
||||||
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
|
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
||||||
|
|
||||||
// Create image data.
|
// Create image data.
|
||||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
|
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
|
||||||
@@ -515,6 +467,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
|||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
// Update samplers.
|
// Update samplers.
|
||||||
|
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
|
||||||
@@ -522,8 +475,8 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||||
|
|
||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, program);
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
static const uint32_t expectedHash = 3644679986;
|
static const uint32_t expectedHash = 3644679986;
|
||||||
readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash);
|
readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash);
|
||||||
@@ -532,21 +485,14 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
|||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
api.destroyDescriptorSet(descriptorSet);
|
|
||||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
|
||||||
api.destroyProgram(program);
|
|
||||||
api.destroySwapChain(swapChain);
|
|
||||||
api.destroyRenderTarget(defaultRenderTarget);
|
|
||||||
|
|
||||||
// This ensures all driver commands have finished before exiting the test.
|
// This ensures all driver commands have finished before exiting the test.
|
||||||
api.finish();
|
api.finish();
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(BackendTest, UpdateImage3D) {
|
TEST_F(LoadImageTest, UpdateImage3D) {
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
Cleanup cleanup(api);
|
||||||
api.startCapture();
|
api.startCapture();
|
||||||
|
|
||||||
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
||||||
@@ -556,35 +502,24 @@ TEST_F(BackendTest, UpdateImage3D) {
|
|||||||
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||||
|
|
||||||
// Create a platform-specific SwapChain and make it current.
|
// Create a platform-specific SwapChain and make it current.
|
||||||
auto swapChain = createSwapChain();
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||||
|
|
||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||||
filamat::DescriptorSets descriptors;
|
|
||||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
|
||||||
samplerInfo } };
|
|
||||||
std::string fragment = stringReplace("{samplerType}",
|
std::string fragment = stringReplace("{samplerType}",
|
||||||
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
|
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
|
||||||
ShaderGenerator shaderGen(
|
Shader shader(api, cleanup, ShaderConfig {
|
||||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
.vertexShader = mVertexShader,
|
||||||
Program prog = shaderGen.getProgram(api);
|
.fragmentShader = fragment,
|
||||||
prog.descriptorBindings(1, {{ "test_tex", DescriptorType::SAMPLER, 0 }});
|
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
});
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}});
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
|
||||||
|
|
||||||
// Create a texture.
|
// Create a texture.
|
||||||
Handle<HwTexture> texture = api.createTexture(samplerType, 1,
|
Handle<HwTexture> texture = cleanup.add(api.createTexture(samplerType, 1,
|
||||||
textureFormat, 1, 512, 512, 4, usage);
|
textureFormat, 1, 512, 512, 4, usage));
|
||||||
|
|
||||||
// Create image data for all 4 layers.
|
// Create image data for all 4 layers.
|
||||||
size_t components; int bpp;
|
size_t components; int bpp;
|
||||||
@@ -606,6 +541,7 @@ TEST_F(BackendTest, UpdateImage3D) {
|
|||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
// Update samplers.
|
// Update samplers.
|
||||||
|
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
|
||||||
@@ -613,8 +549,8 @@ TEST_F(BackendTest, UpdateImage3D) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||||
|
|
||||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||||
defaultRenderTarget, swapChain, program);
|
defaultRenderTarget, swapChain, shader.getProgram());
|
||||||
|
|
||||||
static const uint32_t expectedHash = 3644679986;
|
static const uint32_t expectedHash = 3644679986;
|
||||||
readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash);
|
readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash);
|
||||||
@@ -623,17 +559,9 @@ TEST_F(BackendTest, UpdateImage3D) {
|
|||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
api.destroyDescriptorSet(descriptorSet);
|
|
||||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
|
||||||
api.destroyProgram(program);
|
|
||||||
api.destroySwapChain(swapChain);
|
|
||||||
api.destroyRenderTarget(defaultRenderTarget);
|
|
||||||
|
|
||||||
// This ensures all driver commands have finished before exiting the test.
|
// This ensures all driver commands have finished before exiting the test.
|
||||||
api.finish();
|
api.finish();
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
|
|
||||||
flushAndWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -26,21 +27,6 @@ namespace {
|
|||||||
// Shaders
|
// Shaders
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
|
|
||||||
layout(location = 0) out uvec4 indices;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y = -gl_Position.y;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragment (R"(#version 450 core
|
std::string fragment (R"(#version 450 core
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
@@ -71,10 +57,12 @@ TEST_F(BackendTest, MRT) {
|
|||||||
auto swapChain = cleanup.add(createSwapChain());
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program.
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
.vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
|
||||||
Program p = shaderGen.getProgram(api);
|
ShaderUniformType::None),
|
||||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
.fragmentShader = fragment,
|
||||||
|
.uniforms = {}
|
||||||
|
});
|
||||||
|
|
||||||
TrianglePrimitive triangle(api);
|
TrianglePrimitive triangle(api);
|
||||||
|
|
||||||
@@ -122,7 +110,7 @@ TEST_F(BackendTest, MRT) {
|
|||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = program;
|
state.program = shader.getProgram();
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
#include "BackendTestUtils.h"
|
#include "BackendTestUtils.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
@@ -34,18 +35,7 @@ namespace {
|
|||||||
// Shaders
|
// Shaders
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
std::string fragmentTexturedLod (R"(#version 450 core
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
layout(location = 0) out vec2 uv;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
uv = (mesh_position.xy * 0.5 + 0.5);
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragment (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
layout(location = 0) in vec2 uv;
|
layout(location = 0) in vec2 uv;
|
||||||
@@ -57,18 +47,6 @@ void main() {
|
|||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
std::string whiteFragment (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
layout(location = 0) in vec2 uv;
|
|
||||||
|
|
||||||
layout(location = 0, set = 1) uniform sampler2D backend_test_sib_tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(1.0);
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
@@ -88,39 +66,25 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
auto swapChain = cleanup.add(createSwapChain());
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program that draws only white.
|
Shader whiteShader = SharedShaders::makeShader(api, cleanup, ShaderRequest {
|
||||||
Handle<HwProgram> whiteProgram;
|
.mVertexType = VertexShaderType::Textured,
|
||||||
{
|
.mFragmentType = FragmentShaderType::White,
|
||||||
ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform);
|
.mUniformType = ShaderUniformType::Sampler
|
||||||
Program p = shaderGen.getProgram(api);
|
});
|
||||||
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
|
|
||||||
whiteProgram = cleanup.add(api.createProgram(std::move(p)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a program that samples a texture.
|
// Create a program that samples a texture.
|
||||||
Handle<HwProgram> textureProgram;
|
std::string vertexShader = SharedShaders::getVertexShaderText(
|
||||||
{
|
VertexShaderType::Textured, ShaderUniformType::Sampler);
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "backend_test", "sib_tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo {
|
||||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
"backend_test", "sib_tex", 0,
|
||||||
filamat::DescriptorSets descriptors;
|
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||||
descriptors[1] = { { "backend_test_sib_tex",
|
Shader texturedShader(api, cleanup, ShaderConfig {
|
||||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, samplerInfo } };
|
.vertexShader = vertexShader,
|
||||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
.fragmentShader = fragmentTexturedLod,
|
||||||
Program p = shaderGen.getProgram(api);
|
.uniforms = {{
|
||||||
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
|
"backend_test_sib_tex", DescriptorType::SAMPLER, samplerInfo
|
||||||
textureProgram = cleanup.add(api.createProgram(std::move(p)));
|
}}
|
||||||
}
|
});
|
||||||
|
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}}));
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet[2];
|
|
||||||
descriptorSet[0] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
descriptorSet[1] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
|
|
||||||
// Create a texture that has 4 mip levels. Each level is a different color.
|
// Create a texture that has 4 mip levels. Each level is a different color.
|
||||||
// Level 0: 128x128 (red)
|
// Level 0: 128x128 (red)
|
||||||
@@ -129,9 +93,10 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
// Level 3: 16x16 (yellow)
|
// Level 3: 16x16 (yellow)
|
||||||
const size_t kTextureSize = 128;
|
const size_t kTextureSize = 128;
|
||||||
const size_t kMipLevels = 4;
|
const size_t kMipLevels = 4;
|
||||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
|
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||||
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
|
kMipLevels, TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
|
||||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
|
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT
|
||||||
|
| TextureUsage::UPLOADABLE));
|
||||||
|
|
||||||
// Create image data.
|
// Create image data.
|
||||||
auto pixelFormat = PixelDataFormat::RGBA;
|
auto pixelFormat = PixelDataFormat::RGBA;
|
||||||
@@ -179,7 +144,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
PipelineState ps = {};
|
PipelineState ps = {};
|
||||||
ps.program = whiteProgram;
|
ps.program = whiteShader.getProgram();
|
||||||
ps.rasterState.colorWrite = true;
|
ps.rasterState.colorWrite = true;
|
||||||
ps.rasterState.depthWrite = false;
|
ps.rasterState.depthWrite = false;
|
||||||
api.beginRenderPass(renderTarget, params);
|
api.beginRenderPass(renderTarget, params);
|
||||||
@@ -187,7 +152,8 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
backend::Handle<HwRenderTarget> defaultRenderTarget =
|
||||||
|
cleanup.add(api.createDefaultRenderTarget(0));
|
||||||
|
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
@@ -197,18 +163,19 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = textureProgram;
|
state.program = texturedShader.getProgram();
|
||||||
state.pipelineLayout.setLayout = { descriptorSetLayout };
|
state.pipelineLayout.setLayout = { texturedShader.getDescriptorSetLayout() };
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = SamplerCompareFunc::A;
|
state.rasterState.depthFunc = SamplerCompareFunc::A;
|
||||||
state.rasterState.culling = CullingMode::NONE;
|
state.rasterState.culling = CullingMode::NONE;
|
||||||
|
|
||||||
api.updateDescriptorSetTexture(descriptorSet[0], 0, texture13, {
|
DescriptorSetHandle descriptorSet13 = texturedShader.createDescriptorSet(api);
|
||||||
|
api.updateDescriptorSetTexture(descriptorSet13, 0, texture13, {
|
||||||
.filterMag = SamplerMagFilter::NEAREST,
|
.filterMag = SamplerMagFilter::NEAREST,
|
||||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet[0], 0, {});
|
api.bindDescriptorSet(descriptorSet13, 0, {});
|
||||||
|
|
||||||
// Render a triangle to the screen, sampling from mip level 1.
|
// Render a triangle to the screen, sampling from mip level 1.
|
||||||
// Because the min level is 1, the result color should be the white triangle drawn in the
|
// Because the min level is 1, the result color should be the white triangle drawn in the
|
||||||
@@ -221,11 +188,12 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
// Adjust the base mip to 2.
|
// Adjust the base mip to 2.
|
||||||
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
|
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
|
||||||
|
|
||||||
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
|
DescriptorSetHandle descriptorSet22 = texturedShader.createDescriptorSet(api);
|
||||||
|
api.updateDescriptorSetTexture(descriptorSet22, 0, texture22, {
|
||||||
.filterMag = SamplerMagFilter::NEAREST,
|
.filterMag = SamplerMagFilter::NEAREST,
|
||||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet[1], 0, {});
|
api.bindDescriptorSet(descriptorSet22, 0, {});
|
||||||
|
|
||||||
// Render a second, smaller, triangle, again sampling from mip level 1.
|
// Render a second, smaller, triangle, again sampling from mip level 1.
|
||||||
// This triangle should be yellow striped.
|
// This triangle should be yellow striped.
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -26,7 +27,7 @@ namespace {
|
|||||||
// Shaders
|
// Shaders
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
std::string vertex(R"(#version 450 core
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
|
||||||
@@ -46,16 +47,6 @@ void main() {
|
|||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
std::string fragment (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
@@ -78,9 +69,11 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
|||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program.
|
// Create a program.
|
||||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
Program p = shaderGen.getProgram(api);
|
.vertexShader = vertex,
|
||||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
.fragmentShader = SharedShaders::getFragmentShaderText(FragmentShaderType::White,
|
||||||
|
ShaderUniformType::None),
|
||||||
|
});
|
||||||
|
|
||||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||||
|
|
||||||
@@ -89,12 +82,12 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
|||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = program;
|
state.program = shader.getProgram();
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
#include "BackendTestUtils.h"
|
#include "BackendTestUtils.h"
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <utils/Hash.h>
|
#include <utils/Hash.h>
|
||||||
@@ -30,6 +31,7 @@ using namespace filament;
|
|||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
#ifndef FILAMENT_IOS
|
#ifndef FILAMENT_IOS
|
||||||
|
|
||||||
#include <imageio/ImageEncoder.h>
|
#include <imageio/ImageEncoder.h>
|
||||||
#include <image/ColorTransform.h>
|
#include <image/ColorTransform.h>
|
||||||
|
|
||||||
@@ -42,20 +44,7 @@ namespace {
|
|||||||
// Shaders
|
// Shaders
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
std::string fragmentFloat(R"(#version 450 core
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y = -gl_Position.y;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragmentFloat (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
@@ -65,7 +54,7 @@ void main() {
|
|||||||
|
|
||||||
)");
|
)");
|
||||||
|
|
||||||
std::string fragmentUint (R"(#version 450 core
|
std::string fragmentUint(R"(#version 450 core
|
||||||
|
|
||||||
layout(location = 0) out uvec4 fragColor;
|
layout(location = 0) out uvec4 fragColor;
|
||||||
|
|
||||||
@@ -108,7 +97,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
size_t samples = 1;
|
size_t samples = 1;
|
||||||
|
|
||||||
// The size of the actual render target, taking mip level into account;
|
// The size of the actual render target, taking mip level into account;
|
||||||
size_t getRenderTargetSize () const {
|
size_t getRenderTargetSize() const {
|
||||||
return std::max(size_t(1), renderTargetBaseSize >> mipLevel);
|
return std::max(size_t(1), renderTargetBaseSize >> mipLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,11 +127,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void exportScreenshot(void* pixelData) const {
|
void exportScreenshot(void* pixelData) const {
|
||||||
#ifndef FILAMENT_IOS
|
#ifndef FILAMENT_IOS
|
||||||
const size_t width = readRect.width, height = readRect.height;
|
const size_t width = readRect.width, height = readRect.height;
|
||||||
LinearImage image(width, height, 4);
|
LinearImage image(width, height, 4);
|
||||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
|
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
|
||||||
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*) pixelData);
|
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
|
||||||
}
|
}
|
||||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
|
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
|
||||||
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
|
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
|
||||||
@@ -151,13 +140,13 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
||||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
||||||
png.c_str());
|
png.c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void exportRawBytes(void* pixelData) const {
|
void exportRawBytes(void* pixelData) const {
|
||||||
std::string out = std::string(testName) + ".raw";
|
std::string out = std::string(testName) + ".raw";
|
||||||
std::ofstream outputStream(out.c_str(), std::ios::binary | std::ios::trunc);
|
std::ofstream outputStream(out.c_str(), std::ios::binary | std::ios::trunc);
|
||||||
outputStream.write((char*) pixelData, getBufferSizeBytes());
|
outputStream.write((char*)pixelData, getBufferSizeBytes());
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,22 +229,20 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
DriverApi& api = getDriverApi();
|
DriverApi& api = getDriverApi();
|
||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
// Create programs.
|
std::string vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
|
||||||
Handle<HwProgram> programFloat, programUint;
|
ShaderUniformType::None);
|
||||||
{
|
Shader floatShader(api, cleanup, ShaderConfig{
|
||||||
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
|
.vertexShader = vertexShader,
|
||||||
Program p = shaderGen.getProgram(api);
|
.fragmentShader = fragmentFloat,
|
||||||
programFloat = cleanup.add(api.createProgram(std::move(p)));
|
.uniforms = {}
|
||||||
}
|
});
|
||||||
{
|
Shader uintShader(api, cleanup, ShaderConfig{
|
||||||
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
|
.vertexShader = vertexShader,
|
||||||
Program p = shaderGen.getProgram(api);
|
.fragmentShader = fragmentUint,
|
||||||
programUint = cleanup.add(api.createProgram(std::move(p)));
|
.uniforms = {}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
for (const auto& t: testCases) {
|
||||||
for (const auto& t : testCases)
|
|
||||||
{
|
|
||||||
// Create a platform-specific SwapChain and make it current.
|
// Create a platform-specific SwapChain and make it current.
|
||||||
Handle<HwSwapChain> swapChain;
|
Handle<HwSwapChain> swapChain;
|
||||||
if (t.useDefaultRT) {
|
if (t.useDefaultRT) {
|
||||||
@@ -292,7 +279,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
params.viewport.height = t.getRenderTargetSize();
|
params.viewport.height = t.getRenderTargetSize();
|
||||||
@@ -305,9 +292,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
api.beginRenderPass(renderTarget, params);
|
api.beginRenderPass(renderTarget, params);
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = programFloat;
|
state.program = floatShader.getProgram();
|
||||||
if (isUnsignedIntFormat(t.textureFormat)) {
|
if (isUnsignedIntFormat(t.textureFormat)) {
|
||||||
state.program = programUint;
|
state.program = uintShader.getProgram();
|
||||||
}
|
}
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
@@ -325,7 +312,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
|
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
|
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
|
||||||
{{ texture }}, {}, {}));
|
{{ texture }}, {}, {}));
|
||||||
p.clearColor = {1.f, 0.f, 0.f, 1.f};
|
p.clearColor = { 1.f, 0.f, 0.f, 1.f };
|
||||||
api.beginRenderPass(mipLevelOneRT, p);
|
api.beginRenderPass(mipLevelOneRT, p);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
@@ -335,28 +322,28 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
|
|
||||||
PixelBufferDescriptor descriptor(buffer, t.getBufferSizeBytes(), t.format, t.type,
|
PixelBufferDescriptor descriptor(buffer, t.getBufferSizeBytes(), t.format, t.type,
|
||||||
t.alignment, t.left, t.top, t.getPixelBufferStride(), [](void* buffer, size_t size,
|
t.alignment, t.left, t.top, t.getPixelBufferStride(), [](void* buffer, size_t size,
|
||||||
void* user) {
|
void* user) {
|
||||||
const auto* test = (const TestCase*) user;
|
const auto* test = (const TestCase*)user;
|
||||||
assert_invariant(test);
|
assert_invariant(test);
|
||||||
|
|
||||||
test->exportScreenshot(buffer);
|
test->exportScreenshot(buffer);
|
||||||
//test->exportRawBytes(buffer);
|
//test->exportRawBytes(buffer);
|
||||||
|
|
||||||
// Hash the contents of the buffer and check that they match.
|
// Hash the contents of the buffer and check that they match.
|
||||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
uint32_t hash = utils::hash::murmur3((const uint32_t*)buffer, size / 4, 0);
|
||||||
|
|
||||||
ASSERT_EQ(test->hash, hash) << test->testName <<
|
ASSERT_EQ(test->hash, hash) << test->testName <<
|
||||||
" failed: hashes do not match." << std::endl;
|
" failed: hashes do not match." << std::endl;
|
||||||
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}, (void*) &t);
|
}, (void*)&t);
|
||||||
|
|
||||||
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
|
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
|
||||||
t.readRect.height, std::move(descriptor));
|
t.readRect.height, std::move(descriptor));
|
||||||
|
|
||||||
// Now render red over what was just rendered. This ensures that readPixels captures the
|
// Now render red over what was just rendered. This ensures that readPixels captures the
|
||||||
// state of rendering between render passes.
|
// state of rendering between render passes.
|
||||||
params.clearColor = {1.f, 0.f, 0.f, 1.f};
|
params.clearColor = { 1.f, 0.f, 0.f, 1.f };
|
||||||
api.beginRenderPass(renderTarget, params);
|
api.beginRenderPass(renderTarget, params);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|
||||||
@@ -376,25 +363,27 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
|||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
// Create a platform-specific SwapChain and make it current.
|
// Create a platform-specific SwapChain and make it current.
|
||||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
|
auto swapChain = cleanup.add(
|
||||||
|
api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program.
|
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||||
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
|
.mVertexType = VertexShaderType::Noop,
|
||||||
Program p = shaderGen.getProgram(api);
|
.mFragmentType = FragmentShaderType::White,
|
||||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
.mUniformType = ShaderUniformType::None
|
||||||
|
});
|
||||||
|
|
||||||
// Create a Texture and RenderTarget to render into.
|
// Create a Texture and RenderTarget to render into.
|
||||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
||||||
SamplerType::SAMPLER_2D, // target
|
SamplerType::SAMPLER_2D, // target
|
||||||
1, // levels
|
1, // levels
|
||||||
TextureFormat::RGBA8, // format
|
TextureFormat::RGBA8, // format
|
||||||
1, // samples
|
1, // samples
|
||||||
renderTargetSize, // width
|
renderTargetSize, // width
|
||||||
renderTargetSize, // height
|
renderTargetSize, // height
|
||||||
1, // depth
|
1, // depth
|
||||||
usage)); // usage
|
usage)); // usage
|
||||||
|
|
||||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR,
|
TargetBufferFlags::COLOR,
|
||||||
@@ -411,7 +400,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
|||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
params.viewport.height = renderTargetSize;
|
params.viewport.height = renderTargetSize;
|
||||||
@@ -420,7 +409,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
|||||||
void* buffer = calloc(1, renderTargetSize * renderTargetSize * 4);
|
void* buffer = calloc(1, renderTargetSize * renderTargetSize * 4);
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = program;
|
state.program = shader.getProgram();
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
@@ -444,11 +433,12 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
|||||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||||
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
|
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
|
||||||
[](void* buffer, size_t size, void* user) {
|
[](void* buffer, size_t size, void* user) {
|
||||||
ReadPixelsTest* test = (ReadPixelsTest*) user;
|
ReadPixelsTest* test = (ReadPixelsTest*)user;
|
||||||
test->readPixelsFinished = true;
|
test->readPixelsFinished = true;
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
|
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize,
|
||||||
|
std::move(descriptor));
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
@@ -28,42 +29,19 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Shaders
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
layout(location = 0) out vec2 uv;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
uv = (mesh_position.xy * 0.5 + 0.5);
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragment (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
layout(location = 0) in vec2 uv;
|
|
||||||
|
|
||||||
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = texture(test_tex, uv);
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
using namespace filament;
|
using namespace filament;
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
|
Shader createShader(DriverApi& api, Cleanup& cleanup, Backend backend) {
|
||||||
|
return SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||||
|
.mVertexType = VertexShaderType::Textured,
|
||||||
|
.mFragmentType = FragmentShaderType::Textured,
|
||||||
|
.mUniformType = ShaderUniformType::Sampler
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Rendering an external image without setting any data should not crash.
|
// Rendering an external image without setting any data should not crash.
|
||||||
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
@@ -73,57 +51,41 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
|||||||
|
|
||||||
auto swapChain = cleanup.add(createSwapChain());
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
|
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
Shader shader = createShader(api, cleanup, sBackend);
|
||||||
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
|
|
||||||
filamat::DescriptorSets descriptors;
|
|
||||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
|
||||||
samplerInfo } };
|
|
||||||
ShaderGenerator shaderGen(
|
|
||||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
|
||||||
|
|
||||||
// Create a program that samples a texture.
|
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(
|
||||||
Program p = shaderGen.getProgram(api);
|
api.createDefaultRenderTarget(0));
|
||||||
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
|
||||||
backend::Handle<HwProgram> program = cleanup.add(api.createProgram(std::move(p)));
|
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}}));
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
|
|
||||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
|
||||||
|
|
||||||
// Create a texture that will be backed by an external image.
|
// Create a texture that will be backed by an external image.
|
||||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||||
const NativeView& view = getNativeView();
|
const NativeView& view = getNativeView();
|
||||||
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
||||||
SamplerType::SAMPLER_EXTERNAL, // target
|
SamplerType::SAMPLER_EXTERNAL, // target
|
||||||
1, // levels
|
1, // levels
|
||||||
TextureFormat::RGBA8, // format
|
TextureFormat::RGBA8, // format
|
||||||
1, // samples
|
1, // samples
|
||||||
view.width, // width
|
view.width, // width
|
||||||
view.height, // height
|
view.height, // height
|
||||||
1, // depth
|
1, // depth
|
||||||
usage)); // usage
|
usage)); // usage
|
||||||
|
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = program;
|
state.program = shader.getProgram();
|
||||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
state.rasterState.culling = CullingMode::NONE;
|
state.rasterState.culling = CullingMode::NONE;
|
||||||
|
|
||||||
|
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||||
|
|
||||||
api.startCapture(0);
|
api.startCapture(0);
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
@@ -155,29 +117,11 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
|
|
||||||
auto swapChain = cleanup.add(createSwapChain());
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
|
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
Shader shader = createShader(api, cleanup, sBackend);
|
||||||
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
|
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||||
filamat::DescriptorSets descriptors;
|
|
||||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
|
||||||
samplerInfo } };
|
|
||||||
ShaderGenerator shaderGen(
|
|
||||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
|
||||||
|
|
||||||
// Create a program that samples a texture.
|
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(
|
||||||
Program p = shaderGen.getProgram(api);
|
api.createDefaultRenderTarget(0));
|
||||||
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
|
||||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
|
||||||
|
|
||||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
|
||||||
{{
|
|
||||||
DescriptorType::SAMPLER,
|
|
||||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
|
||||||
DescriptorFlags::NONE, 0
|
|
||||||
}}}));
|
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
|
||||||
|
|
||||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
|
||||||
|
|
||||||
// require users to create two Filament textures and have two material parameters
|
// require users to create two Filament textures and have two material parameters
|
||||||
// add a "plane" parameter to setExternalImage
|
// add a "plane" parameter to setExternalImage
|
||||||
@@ -198,10 +142,12 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
values[1] = values[0];
|
values[1] = values[0];
|
||||||
values[2] = values[0];
|
values[2] = values[0];
|
||||||
values[3] = values[0];
|
values[3] = values[0];
|
||||||
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**) keys, (const void**) values, 4, nullptr, nullptr);
|
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys,
|
||||||
|
(const void**)values, 4, nullptr, nullptr);
|
||||||
CVPixelBufferRef pixBuffer = nullptr;
|
CVPixelBufferRef pixBuffer = nullptr;
|
||||||
CVReturn status =
|
CVReturn status =
|
||||||
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options, &pixBuffer);
|
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options,
|
||||||
|
&pixBuffer);
|
||||||
assert(status == kCVReturnSuccess);
|
assert(status == kCVReturnSuccess);
|
||||||
|
|
||||||
// Fill image with checker-pattern.
|
// Fill image with checker-pattern.
|
||||||
@@ -210,7 +156,7 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
const uint32_t black = 0xFF000000;
|
const uint32_t black = 0xFF000000;
|
||||||
CVReturn lockStatus = CVPixelBufferLockBaseAddress(pixBuffer, 0);
|
CVReturn lockStatus = CVPixelBufferLockBaseAddress(pixBuffer, 0);
|
||||||
assert(lockStatus == kCVReturnSuccess);
|
assert(lockStatus == kCVReturnSuccess);
|
||||||
uint32_t* pix = (uint32_t*) CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0);
|
uint32_t* pix = (uint32_t*)CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0);
|
||||||
assert(pix);
|
assert(pix);
|
||||||
for (size_t r = 0; r < 1024; r++) {
|
for (size_t r = 0; r < 1024; r++) {
|
||||||
for (size_t c = 0; c < 1024; c++) {
|
for (size_t c = 0; c < 1024; c++) {
|
||||||
@@ -230,13 +176,13 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
fullViewport(params);
|
fullViewport(params);
|
||||||
params.flags.clear = TargetBufferFlags::COLOR;
|
params.flags.clear = TargetBufferFlags::COLOR;
|
||||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState state;
|
PipelineState state;
|
||||||
state.program = program;
|
state.program = shader.getProgram();
|
||||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||||
state.rasterState.colorWrite = true;
|
state.rasterState.colorWrite = true;
|
||||||
state.rasterState.depthWrite = false;
|
state.rasterState.depthWrite = false;
|
||||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||||
@@ -247,7 +193,6 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||||
|
|
||||||
// Render a triangle.
|
// Render a triangle.
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <utils/Hash.h>
|
#include <utils/Hash.h>
|
||||||
@@ -27,23 +28,6 @@ namespace test {
|
|||||||
using namespace filament;
|
using namespace filament;
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
static const char* const triangleVs = R"(#version 450 core
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y = -gl_Position.y;
|
|
||||||
#endif
|
|
||||||
})";
|
|
||||||
|
|
||||||
static const char* const triangleFs = R"(#version 450 core
|
|
||||||
precision mediump int; precision highp float;
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(1.0f);
|
|
||||||
})";
|
|
||||||
|
|
||||||
TEST_F(BackendTest, ScissorViewportRegion) {
|
TEST_F(BackendTest, ScissorViewportRegion) {
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
|
||||||
@@ -85,10 +69,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
|||||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program.
|
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
.mVertexType = VertexShaderType::Noop,
|
||||||
Program p = shaderGen.getProgram(api);
|
.mFragmentType = FragmentShaderType::White,
|
||||||
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
|
.mUniformType = ShaderUniformType::None,
|
||||||
|
});
|
||||||
|
|
||||||
// Create source color and depth textures.
|
// Create source color and depth textures.
|
||||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
|
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
|
||||||
@@ -133,7 +118,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
|||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState ps = {};
|
PipelineState ps = {};
|
||||||
ps.program = program;
|
ps.program = shader.getProgram();
|
||||||
ps.rasterState.colorWrite = true;
|
ps.rasterState.colorWrite = true;
|
||||||
ps.rasterState.depthWrite = false;
|
ps.rasterState.depthWrite = false;
|
||||||
|
|
||||||
@@ -175,10 +160,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
|||||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
// Create a program.
|
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
.mVertexType = VertexShaderType::Noop,
|
||||||
Program p = shaderGen.getProgram(api);
|
.mFragmentType = FragmentShaderType::White,
|
||||||
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
|
.mUniformType = ShaderUniformType::None,
|
||||||
|
});
|
||||||
|
|
||||||
// Create a source color textures.
|
// Create a source color textures.
|
||||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
@@ -220,7 +206,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
|||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|
||||||
PipelineState ps = {};
|
PipelineState ps = {};
|
||||||
ps.program = program;
|
ps.program = shader.getProgram();
|
||||||
ps.rasterState.colorWrite = true;
|
ps.rasterState.colorWrite = true;
|
||||||
ps.rasterState.depthWrite = false;
|
ps.rasterState.depthWrite = false;
|
||||||
|
|
||||||
|
|||||||
@@ -17,43 +17,13 @@
|
|||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
#include "ShaderGenerator.h"
|
#include "Shader.h"
|
||||||
|
#include "SharedShaders.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
using namespace filament;
|
using namespace filament;
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Shaders
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
std::string vertex (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) in vec4 mesh_position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
|
||||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
||||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
||||||
gl_Position.y = -gl_Position.y;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
std::string fragment (R"(#version 450 core
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 fragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
fragColor = vec4(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
)");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
// 1. Clear the stencil buffer to all zeroes.
|
// 1. Clear the stencil buffer to all zeroes.
|
||||||
@@ -64,7 +34,7 @@ namespace test {
|
|||||||
class BasicStencilBufferTest : public BackendTest {
|
class BasicStencilBufferTest : public BackendTest {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Handle<HwSwapChain> mSwapChain;
|
Handle <HwSwapChain> mSwapChain;
|
||||||
ProgramHandle mProgram;
|
ProgramHandle mProgram;
|
||||||
Cleanup mCleanup;
|
Cleanup mCleanup;
|
||||||
|
|
||||||
@@ -77,21 +47,23 @@ public:
|
|||||||
mSwapChain = mCleanup.add(createSwapChain());
|
mSwapChain = mCleanup.add(createSwapChain());
|
||||||
api.makeCurrent(mSwapChain, mSwapChain);
|
api.makeCurrent(mSwapChain, mSwapChain);
|
||||||
|
|
||||||
// Create a program.
|
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{
|
||||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
.mVertexType = VertexShaderType::Noop,
|
||||||
Program p = shaderGen.getProgram(api);
|
.mFragmentType = FragmentShaderType::White,
|
||||||
mProgram = mCleanup.add(api.createProgram(std::move(p)));
|
.mUniformType = ShaderUniformType::None
|
||||||
|
});
|
||||||
|
mProgram = shader.getProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunTest(Handle<HwRenderTarget> renderTarget) {
|
void RunTest(Handle <HwRenderTarget> renderTarget) {
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
|
|
||||||
// We'll be using a triangle as geometry.
|
// We'll be using a triangle as geometry.
|
||||||
TrianglePrimitive smallTriangle(api);
|
TrianglePrimitive smallTriangle(api);
|
||||||
static filament::math::float2 vertices[3] = {
|
static filament::math::float2 vertices[3] = {
|
||||||
{ -0.5, -0.5 },
|
{ -0.5, -0.5 },
|
||||||
{ 0.5, -0.5 },
|
{ 0.5, -0.5 },
|
||||||
{ -0.5, 0.5 }
|
{ -0.5, 0.5 }
|
||||||
};
|
};
|
||||||
smallTriangle.updateVertices(vertices);
|
smallTriangle.updateVertices(vertices);
|
||||||
TrianglePrimitive triangle(api);
|
TrianglePrimitive triangle(api);
|
||||||
@@ -100,7 +72,7 @@ public:
|
|||||||
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
params.flags.clear = TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL;
|
params.flags.clear = TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL;
|
||||||
params.viewport = {0, 0, 512, 512};
|
params.viewport = { 0, 0, 512, 512 };
|
||||||
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||||
params.clearStencil = 0u;
|
params.clearStencil = 0u;
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
@@ -153,7 +125,7 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
|||||||
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
|
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
|
||||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||||
{{colorTexture}}, {}, {{stencilTexture}}));
|
{{ colorTexture }}, {}, {{ stencilTexture }}));
|
||||||
|
|
||||||
RunTest(renderTarget);
|
RunTest(renderTarget);
|
||||||
|
|
||||||
@@ -171,10 +143,11 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
|||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||||
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1,
|
||||||
|
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||||
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
|
{{ colorTexture }}, { depthStencilTexture }, {{ depthStencilTexture }}));
|
||||||
|
|
||||||
RunTest(renderTarget);
|
RunTest(renderTarget);
|
||||||
|
|
||||||
@@ -194,15 +167,17 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
|
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
|
||||||
// Performs an auto-resolve on the color.
|
// Performs an auto-resolve on the color.
|
||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
TextureFormat::RGBA8, 1, 512, 512, 1,
|
||||||
|
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||||
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1,
|
||||||
|
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||||
auto renderTarget0 = cleanup.add(api.createRenderTarget(
|
auto renderTarget0 = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||||
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
|
{{}}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||||
auto renderTarget1 = cleanup.add(api.createRenderTarget(
|
auto renderTarget1 = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||||
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
|
{{ colorTexture }}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||||
|
|
||||||
api.startCapture(0);
|
api.startCapture(0);
|
||||||
|
|
||||||
@@ -210,8 +185,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
TrianglePrimitive smallTriangle(api);
|
TrianglePrimitive smallTriangle(api);
|
||||||
static filament::math::float2 vertices[3] = {
|
static filament::math::float2 vertices[3] = {
|
||||||
{ -0.5, -0.5 },
|
{ -0.5, -0.5 },
|
||||||
{ 0.5, -0.5 },
|
{ 0.5, -0.5 },
|
||||||
{ -0.5, 0.5 }
|
{ -0.5, 0.5 }
|
||||||
};
|
};
|
||||||
smallTriangle.updateVertices(vertices);
|
smallTriangle.updateVertices(vertices);
|
||||||
TrianglePrimitive triangle(api);
|
TrianglePrimitive triangle(api);
|
||||||
@@ -220,7 +195,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
params.flags.clear = TargetBufferFlags::STENCIL;
|
params.flags.clear = TargetBufferFlags::STENCIL;
|
||||||
params.viewport = {0, 0, 512, 512};
|
params.viewport = { 0, 0, 512, 512 };
|
||||||
params.clearStencil = 0u;
|
params.clearStencil = 0u;
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
|
|||||||
@@ -75,6 +75,14 @@ public:
|
|||||||
|
|
||||||
utils::CString const& getName() const noexcept { return mName; }
|
utils::CString const& getName() const noexcept { return mName; }
|
||||||
|
|
||||||
|
utils::CString const& getNameOrDefault() const noexcept {
|
||||||
|
if (const auto& name = getName(); !name.empty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
static const utils::CString sDefaultName = "(none)";
|
||||||
|
return sDefaultName;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
utils::CString mName;
|
utils::CString mName;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -269,12 +269,14 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
|
|||||||
if (texture && texture->textureHandleCanMutate()) {
|
if (texture && texture->textureHandleCanMutate()) {
|
||||||
mTextureParameters[binding] = { texture, sampler.getSamplerParams() };
|
mTextureParameters[binding] = { texture, sampler.getSamplerParams() };
|
||||||
} else {
|
} else {
|
||||||
|
// Ensure to erase the binding from mTextureParameters since it will not
|
||||||
|
// be updated.
|
||||||
|
mTextureParameters.erase(binding);
|
||||||
|
|
||||||
Handle<HwTexture> handle{};
|
Handle<HwTexture> handle{};
|
||||||
if (texture) {
|
if (texture) {
|
||||||
handle = texture->getHwHandleForSampling();
|
handle = texture->getHwHandleForSampling();
|
||||||
assert_invariant(handle == texture->getHwHandle());
|
assert_invariant(handle == texture->getHwHandle());
|
||||||
} else {
|
|
||||||
mTextureParameters.erase(binding);
|
|
||||||
}
|
}
|
||||||
mDescriptorSet.setSampler(binding, handle, sampler.getSamplerParams());
|
mDescriptorSet.setSampler(binding, handle, sampler.getSamplerParams());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,7 +161,13 @@ Texture::Builder& Texture::Builder::name(StaticString const& name) noexcept {
|
|||||||
Texture* Texture::Builder::build(Engine& engine) {
|
Texture* Texture::Builder::build(Engine& engine) {
|
||||||
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
|
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
|
||||||
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
|
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
|
||||||
<< "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
|
<< "Texture format " << uint16_t(mImpl->mFormat)
|
||||||
|
<< " not supported on this platform, texture name="
|
||||||
|
<< getNameOrDefault().c_str_safe();
|
||||||
|
|
||||||
|
FILAMENT_CHECK_PRECONDITION(mImpl->mWidth > 0 && mImpl->mHeight > 0)
|
||||||
|
<< "Texture has invalid dimensions: (" << mImpl->mWidth << ", " << mImpl->mHeight
|
||||||
|
<< "), texture name=" << getNameOrDefault().c_str_safe();
|
||||||
}
|
}
|
||||||
const bool isProtectedTexturesSupported =
|
const bool isProtectedTexturesSupported =
|
||||||
downcast(engine).getDriverApi().isProtectedTexturesSupported();
|
downcast(engine).getDriverApi().isProtectedTexturesSupported();
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
Pod::Spec.new do |spec|
|
Pod::Spec.new do |spec|
|
||||||
spec.name = "Filament"
|
spec.name = "Filament"
|
||||||
spec.version = "1.59.0"
|
spec.version = "1.59.1"
|
||||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||||
spec.homepage = "https://google.github.io/filament"
|
spec.homepage = "https://google.github.io/filament"
|
||||||
spec.authors = "Google LLC."
|
spec.authors = "Google LLC."
|
||||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||||
spec.platform = :ios, "11.0"
|
spec.platform = :ios, "11.0"
|
||||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.0/filament-v1.59.0-ios.tgz" }
|
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.1/filament-v1.59.1-ios.tgz" }
|
||||||
|
|
||||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||||
spec.pod_target_xcconfig = {
|
spec.pod_target_xcconfig = {
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ namespace filament::descriptor_sets {
|
|||||||
|
|
||||||
using namespace backend;
|
using namespace backend;
|
||||||
|
|
||||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{{
|
static DescriptorSetLayout const postProcessDescriptorSetLayout{"postProcess", {
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
|
static DescriptorSetLayout const depthVariantDescriptorSetLayout{"depthVariant",{
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
@@ -46,14 +46,14 @@ static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
|
|||||||
// dedicated SSR vertex shader), which uses perViewDescriptorSetLayout.
|
// dedicated SSR vertex shader), which uses perViewDescriptorSetLayout.
|
||||||
// This means that PerViewBindingPoints::SHADOWS must be in the layout even though it's not used
|
// This means that PerViewBindingPoints::SHADOWS must be in the layout even though it's not used
|
||||||
// by the SSR variant.
|
// by the SSR variant.
|
||||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{{
|
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{"ssrVariant", {
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static DescriptorSetLayout perViewDescriptorSetLayout = {{
|
static DescriptorSetLayout perViewDescriptorSetLayout = {"perView", {
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
|
||||||
@@ -68,7 +68,7 @@ static DescriptorSetLayout perViewDescriptorSetLayout = {{
|
|||||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
|
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {{
|
static DescriptorSetLayout perRenderableDescriptorSetLayout = {"preRenderable",{
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
||||||
|
|||||||
@@ -704,7 +704,9 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
|
|||||||
::prepareNativeWindow(mWindow);
|
::prepareNativeWindow(mWindow);
|
||||||
|
|
||||||
void* metalLayer = nullptr;
|
void* metalLayer = nullptr;
|
||||||
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN) {
|
|
||||||
|
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN
|
||||||
|
|| config.backend == filament::Engine::Backend::WEBGPU) {
|
||||||
metalLayer = setUpMetalLayer(nativeWindow);
|
metalLayer = setUpMetalLayer(nativeWindow);
|
||||||
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
|
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
|
||||||
nativeSwapChain = metalLayer;
|
nativeSwapChain = metalLayer;
|
||||||
|
|||||||
@@ -205,7 +205,13 @@ void setup_window(Window& w, Engine* engine) {
|
|||||||
void* nativeSwapChain = nativeWindow;
|
void* nativeSwapChain = nativeWindow;
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
void* metalLayer = nullptr;
|
void* metalLayer = nullptr;
|
||||||
|
|
||||||
|
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||||
|
if (kBackend == filament::Engine::Backend::METAL || kBackend == filament::Engine::Backend::VULKAN
|
||||||
|
|| kBackend == filament::Engine::Backend::WEBGPU) {
|
||||||
|
#else
|
||||||
if (kBackend == filament::Engine::Backend::METAL || kBackend == filament::Engine::Backend::VULKAN) {
|
if (kBackend == filament::Engine::Backend::METAL || kBackend == filament::Engine::Backend::VULKAN) {
|
||||||
|
#endif
|
||||||
metalLayer = setUpMetalLayer(nativeWindow);
|
metalLayer = setUpMetalLayer(nativeWindow);
|
||||||
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
|
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
|
||||||
nativeSwapChain = metalLayer;
|
nativeSwapChain = metalLayer;
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import yaml
|
||||||
|
import hashlib
|
||||||
|
import concurrent.futures
|
||||||
|
import re
|
||||||
|
|
||||||
|
from utils import execute, ArgParseImpl
|
||||||
|
|
||||||
|
def get_line(file_name, offset):
|
||||||
|
with open(f'{file_name}', 'rb') as file:
|
||||||
|
bytes = file.read()[0:offset]
|
||||||
|
f_str = bytes.decode('utf-8')
|
||||||
|
return len(f_str.split('\n'))
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def get_func_name(msg):
|
||||||
|
pattern = r"\'(.+)\'"
|
||||||
|
res = re.findall(pattern, msg)
|
||||||
|
if len(res) > 0:
|
||||||
|
return res[0].replace("'", '')
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def run_tidy(files):
|
||||||
|
files_str = ' '.join(files)
|
||||||
|
hid = hashlib.md5(files_str.encode('utf-8')).hexdigest()
|
||||||
|
_, _ = execute(f'clang-tidy --export-fixes=/tmp/{hid}.yaml --quiet --checks=-*,bugprone-exception-escape {files_str}')
|
||||||
|
results = []
|
||||||
|
with open(f'/tmp/{hid}.yaml', 'r') as file:
|
||||||
|
data = yaml.safe_load(file)
|
||||||
|
for d in data['Diagnostics']:
|
||||||
|
if d['DiagnosticName'] != 'bugprone-exception-escape':
|
||||||
|
continue
|
||||||
|
msg = d['DiagnosticMessage']
|
||||||
|
fpath = msg['FilePath']
|
||||||
|
offset = msg['FileOffset']
|
||||||
|
line_num = get_line(fpath, offset)
|
||||||
|
results.append((msg['FilePath'].replace(f'{os.getcwd()}/', ''), line_num, get_func_name(msg['Message'])))
|
||||||
|
return results
|
||||||
|
|
||||||
|
def exception_escape_test():
|
||||||
|
files = glob.glob('filament/**/*.mm', recursive=True) + \
|
||||||
|
glob.glob('filament/**/*.cpp', recursive=True) + \
|
||||||
|
glob.glob('filament/**/*.h', recursive=True)
|
||||||
|
|
||||||
|
num_workers = 5 # Number of threads to spawn
|
||||||
|
part_len = len(files) // num_workers
|
||||||
|
workloads = []
|
||||||
|
for i in range(num_workers):
|
||||||
|
next = min(len(files), part_len)
|
||||||
|
workloads.append(files[0:next])
|
||||||
|
files = files[next:]
|
||||||
|
|
||||||
|
all_results = []
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor:
|
||||||
|
future_to_worker_id = {executor.submit(run_tidy, workloads[i]): i for i in range(num_workers)}
|
||||||
|
|
||||||
|
for future in concurrent.futures.as_completed(future_to_worker_id):
|
||||||
|
worker_id = future_to_worker_id[future]
|
||||||
|
try:
|
||||||
|
all_results.extend(future.result())
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"Main: Worker {worker_id} generated an exception: {exc}")
|
||||||
|
test_name = 'code-correctness::exception-escape'
|
||||||
|
failure_str_lines = []
|
||||||
|
if len(all_results) > 0:
|
||||||
|
failure_str_lines.append(f'Number of failures: {len(all_results)}')
|
||||||
|
for fname, line_num, msg in all_results:
|
||||||
|
failure_str_lines.append(f'{fname}({line_num}): {msg}()')
|
||||||
|
return (len(all_results) == 0, failure_str_lines)
|
||||||
|
|
||||||
|
TESTS = [
|
||||||
|
(exception_escape_test,
|
||||||
|
'exception-escape',
|
||||||
|
'\'an exception may be thrown in a function which should not throw exceptions\'')
|
||||||
|
]
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
has_failures = False
|
||||||
|
for test_func, test_name, test_desc in TESTS:
|
||||||
|
result, res_strs = test_func()
|
||||||
|
ss = ' ' * 4
|
||||||
|
if result:
|
||||||
|
print(f'[{test_name}] PASSED')
|
||||||
|
else:
|
||||||
|
has_failures = True
|
||||||
|
print(f'[{test_name}] FAILED')
|
||||||
|
print(f'{ss}Description: \'{test_desc}\'')
|
||||||
|
for s in res_strs:
|
||||||
|
print(f'{ss}{s}')
|
||||||
|
if has_failures:
|
||||||
|
# TODO: Enable this when we've fixed all the exception-escape errors
|
||||||
|
#exit(1)
|
||||||
|
pass
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Copyright (C) 2024 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.
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def execute(cmd,
|
||||||
|
cwd=None,
|
||||||
|
capture_output=True,
|
||||||
|
stdin=None,
|
||||||
|
env=None,
|
||||||
|
raise_errors=False):
|
||||||
|
in_env = os.environ
|
||||||
|
in_env.update(env if env else {})
|
||||||
|
home = os.environ['HOME']
|
||||||
|
if f'{home}/bin' not in in_env['PATH']:
|
||||||
|
in_env['PATH'] = in_env['PATH'] + f':{home}/bin'
|
||||||
|
|
||||||
|
stdout = subprocess.PIPE if capture_output else sys.stdout
|
||||||
|
stderr = subprocess.PIPE if capture_output else sys.stdout
|
||||||
|
output = ''
|
||||||
|
err_output = ''
|
||||||
|
return_code = -1
|
||||||
|
kwargs = {
|
||||||
|
'cwd': cwd,
|
||||||
|
'env': in_env,
|
||||||
|
'stdout': stdout,
|
||||||
|
'stderr': stderr,
|
||||||
|
'stdin': stdin,
|
||||||
|
'universal_newlines': True
|
||||||
|
}
|
||||||
|
if capture_output:
|
||||||
|
process = subprocess.Popen(cmd.split(' '), **kwargs)
|
||||||
|
output, err_output = process.communicate()
|
||||||
|
return_code = process.returncode
|
||||||
|
else:
|
||||||
|
return_code = subprocess.call(cmd.split(' '), **kwargs)
|
||||||
|
|
||||||
|
if return_code:
|
||||||
|
# Error
|
||||||
|
if raise_errors:
|
||||||
|
raise subprocess.CalledProcessError(return_code, cmd)
|
||||||
|
if output:
|
||||||
|
if type(output) != str:
|
||||||
|
try:
|
||||||
|
output = output.decode('utf-8').strip()
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
print('cannot decode ', output, file=sys.stderr)
|
||||||
|
return return_code, (output if return_code == 0 else err_output)
|
||||||
|
|
||||||
|
class ArgParseImpl(argparse.ArgumentParser):
|
||||||
|
def error(self, message):
|
||||||
|
sys.stderr.write('error: %s\n' % message)
|
||||||
|
self.print_help()
|
||||||
|
sys.exit(1)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
CODE_CORRECTNESS_TEST_DIR="$(pwd)/test/code-correctness"
|
||||||
|
|
||||||
|
# Check if the clang-tidy command exists and is executable
|
||||||
|
if ! command -v clang-tidy > /dev/null 2>&1; then
|
||||||
|
# If command -v fails (returns a non-zero exit status), clang-tidy is not found
|
||||||
|
echo "Error: clang-tidy command not found." >&2
|
||||||
|
echo "Please install clang-tidy or ensure it is in your system's PATH." >&2
|
||||||
|
exit 1 # Exit the script with an error code (conventionally non-zero for failure)
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e && set -x && \
|
||||||
|
python3 ${CODE_CORRECTNESS_TEST_DIR}/src/run.py
|
||||||
Vendored
+15
@@ -33,22 +33,36 @@ if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
|||||||
tint_lang_core
|
tint_lang_core
|
||||||
tint_lang_core_constant
|
tint_lang_core_constant
|
||||||
tint_lang_core_intrinsic
|
tint_lang_core_intrinsic
|
||||||
|
tint_lang_core_ir
|
||||||
|
tint_lang_core_ir_transform
|
||||||
|
tint_lang_core_ir_type
|
||||||
tint_lang_core_type
|
tint_lang_core_type
|
||||||
|
tint_lang_spirv
|
||||||
|
tint_lang_spirv_intrinsic
|
||||||
|
tint_lang_spirv_ir
|
||||||
tint_lang_spirv_reader
|
tint_lang_spirv_reader
|
||||||
tint_lang_spirv_reader_ast_lower
|
tint_lang_spirv_reader_ast_lower
|
||||||
tint_lang_spirv_reader_ast_parser
|
tint_lang_spirv_reader_ast_parser
|
||||||
tint_lang_spirv_reader_common
|
tint_lang_spirv_reader_common
|
||||||
|
tint_lang_spirv_reader_lower
|
||||||
|
tint_lang_spirv_reader_parser
|
||||||
|
tint_lang_spirv_type
|
||||||
|
tint_lang_spirv_validate
|
||||||
tint_lang_spirv_writer
|
tint_lang_spirv_writer
|
||||||
tint_lang_wgsl
|
tint_lang_wgsl
|
||||||
tint_lang_wgsl_ast
|
tint_lang_wgsl_ast
|
||||||
tint_lang_wgsl_ast_transform
|
tint_lang_wgsl_ast_transform
|
||||||
|
tint_lang_wgsl_common
|
||||||
tint_lang_wgsl_features
|
tint_lang_wgsl_features
|
||||||
tint_lang_wgsl_intrinsic
|
tint_lang_wgsl_intrinsic
|
||||||
|
tint_lang_wgsl_ir
|
||||||
tint_lang_wgsl_program
|
tint_lang_wgsl_program
|
||||||
tint_lang_wgsl_resolver
|
tint_lang_wgsl_resolver
|
||||||
tint_lang_wgsl_sem
|
tint_lang_wgsl_sem
|
||||||
tint_lang_wgsl_writer
|
tint_lang_wgsl_writer
|
||||||
tint_lang_wgsl_writer_ast_printer
|
tint_lang_wgsl_writer_ast_printer
|
||||||
|
tint_lang_wgsl_writer_ir_to_program
|
||||||
|
tint_lang_wgsl_writer_raise
|
||||||
tint_results
|
tint_results
|
||||||
tint_utils
|
tint_utils
|
||||||
tint_utils_diagnostic
|
tint_utils_diagnostic
|
||||||
@@ -58,6 +72,7 @@ if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
|||||||
tint_utils_strconv
|
tint_utils_strconv
|
||||||
tint_utils_symbol
|
tint_utils_symbol
|
||||||
tint_utils_text
|
tint_utils_text
|
||||||
|
tint_utils_text_generator
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use the following no-op definitions to introduce a library and its dependency.
|
# Use the following no-op definitions to introduce a library and its dependency.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "filament",
|
"name": "filament",
|
||||||
"version": "1.59.0",
|
"version": "1.59.1",
|
||||||
"description": "Real-time physically based rendering engine",
|
"description": "Real-time physically based rendering engine",
|
||||||
"main": "filament.js",
|
"main": "filament.js",
|
||||||
"module": "filament.js",
|
"module": "filament.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user