Compare commits

..
81 changed files with 1501 additions and 2777 deletions
-34
View File
@@ -19,8 +19,6 @@ 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:]`
@@ -46,8 +44,6 @@ 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'
@@ -64,8 +60,6 @@ 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
@@ -128,31 +122,3 @@ 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
+2
View File
@@ -7,3 +7,5 @@ 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
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.59.2' implementation 'com.google.android.filament:filament-android:1.59.0'
} }
``` ```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release: iOS projects can use CocoaPods to install the latest release:
```shell ```shell
pod 'Filament', '~> 1.59.2' pod 'Filament', '~> 1.59.0'
``` ```
## Documentation ## Documentation
-7
View File
@@ -7,13 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to 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.3
## v1.59.2
- Fix build/compile errors when upgrading to MacOS 15.4
## v1.59.1 ## v1.59.1
@@ -142,13 +142,16 @@ 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) {
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo'] if (!include_webgpu) {
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 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament GROUP=com.google.android.filament
VERSION_NAME=1.59.2 VERSION_NAME=1.59.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android. POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+28 -31
View File
@@ -177,6 +177,29 @@ 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
@@ -187,10 +210,6 @@ 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
@@ -198,43 +217,24 @@ 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.cpp
src/vulkan/VulkanMemory.h src/vulkan/VulkanMemory.h
src/vulkan/VulkanMemory.cpp
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/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
src/webgpu/WebGPUSwapChain.cpp src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h src/webgpu/WebGPUSwapChain.h
src/webgpu/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
) )
if (WIN32) if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp) list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
@@ -507,10 +507,7 @@ if (APPLE OR LINUX)
test/Arguments.cpp test/Arguments.cpp
test/ImageExpectations.cpp test/ImageExpectations.cpp
test/Lifetimes.cpp test/Lifetimes.cpp
test/PlatformRunner.cpp
test/Shader.cpp test/Shader.cpp
test/SharedShaders.cpp
test/Skip.cpp
test/test_FeedbackLoops.cpp test/test_FeedbackLoops.cpp
test/test_Blit.cpp test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp test/test_MissingRequiredAttributes.cpp
@@ -55,9 +55,4 @@ 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
@@ -104,6 +104,10 @@ 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,12 +38,6 @@ 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,27 +54,31 @@ using namespace utils;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
static inline std::string to_string(bool b) noexcept { return b ? "true" : "false"; } static void logCompilationError(utils::io::ostream& out,
static inline std::string to_string(int i) noexcept { return std::to_string(i); } ShaderStage shaderType, const char* name,
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 process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source, static void logProgramLinkError(utils::io::ostream& out,
size_t len) noexcept; const char* name, GLuint program) noexcept;
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source,
size_t len) noexcept; static inline std::string to_string(bool b) noexcept {
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept; return b ? "true" : "false";
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{};
shaders_t shaders{}; std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
}; };
~OpenGLProgramToken() override; ~OpenGLProgramToken() override;
@@ -86,10 +90,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;
shaders_source_t shaderSourceCode; std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
void* user = nullptr; void* user = nullptr;
struct { struct {
shaders_t shaders{}; std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
GLuint program = 0; GLuint program = 0;
} gl; // 12 bytes } gl; // 12 bytes
@@ -136,12 +140,11 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() = default; ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() = default;
/* static */ void ShaderCompilerService::setUserData(const program_token_t& token, void ShaderCompilerService::setUserData(const program_token_t& token, void* user) noexcept {
void* user) noexcept {
token->user = user; token->user = user;
} }
/* static */ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept { void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept {
return token->user; return token->user;
} }
@@ -265,13 +268,12 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
token->handle = mCallbackManager.get(); token->handle = mCallbackManager.get();
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
switch (mMode) { if (mMode == Mode::THREAD_POOL) {
case Mode::THREAD_POOL: {
// queue a compile job // queue a compile job
mCompilerThreadPool.queue(priorityQueue, token, mCompilerThreadPool.queue(priorityQueue, token,
[this, &gl, program = std::move(program), token]() mutable { [this, &gl, program = std::move(program), token]() mutable {
// compile the shaders // compile the shaders
shaders_t shaders{}; std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
compileShaders(gl, compileShaders(gl,
std::move(program.getShadersSource()), std::move(program.getShadersSource()),
program.getSpecializationConstants(), program.getSpecializationConstants(),
@@ -304,11 +306,8 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram); mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
} }
}); });
break;
}
case Mode::SYNCHRONOUS: } else {
case Mode::ASYNCHRONOUS: {
// this cannot fail because we check compilation status after linking the program // this cannot fail because we check compilation status after linking the program
// shaders[] is filled with id of shader stages present. // shaders[] is filled with id of shader stages present.
compileShaders(gl, compileShaders(gl,
@@ -364,12 +363,6 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
return true; return true;
}); });
break;
}
case Mode::UNDEFINED: {
assert_invariant(false);
}
} }
return token; return token;
@@ -384,12 +377,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
return program; return program;
} }
/* static */ void ShaderCompilerService::terminate(program_token_t& token) { void ShaderCompilerService::terminate(program_token_t& token) {
assert_invariant(token); assert_invariant(token);
token->canceled = true; token->canceled = true;
bool const isTickOpCanceled = token->compiler.cancelTickOp(token); bool const canceled = 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);
@@ -402,7 +395,7 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
// 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 (isTickOpCanceled) { } else if (canceled) {
// 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);
} }
@@ -439,8 +432,7 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
/* static */ void ShaderCompilerService::getProgramFromCompilerPool( void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) noexcept {
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;
@@ -451,8 +443,7 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
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) {
switch (mMode) { if (mMode == Mode::THREAD_POOL) {
case Mode::THREAD_POOL: {
// we need this program right now, remove it from the queue // we need this program right now, remove it from the queue
auto job = mCompilerThreadPool.dequeue(token); auto job = mCompilerThreadPool.dequeue(token);
if (job) { if (job) {
@@ -468,16 +459,13 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
// Block until we get the program from the pool. Generally this wouldn't block // Block until we get the program from the pool. Generally this wouldn't block
// because we just compiled the program above, when executing job. // because we just compiled the program above, when executing job.
ShaderCompilerService::getProgramFromCompilerPool(token); ShaderCompilerService::getProgramFromCompilerPool(token);
break; } else if (mMode == Mode::ASYNCHRONOUS) {
}
case Mode::ASYNCHRONOUS: {
// we force the program link -- which might stall, either here or below in // 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. // 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 = token->gl.program = linkProgram(mDriver.getContext(),
linkProgram(mDriver.getContext(), token->gl.shaders, token->attributes); token->gl.shaders, token->attributes);
assert_invariant(token->gl.program); assert_invariant(token->gl.program);
@@ -486,18 +474,9 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
if (token->key) { if (token->key) {
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program); mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
} }
break; } else {
}
case Mode::SYNCHRONOUS: {
// if we don't have a program yet, block until we get it. // if we don't have a program yet, block until we get it.
tick(); tick();
break;
}
case Mode::UNDEFINED: {
assert_invariant(false);
}
} }
} }
@@ -540,12 +519,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.
*/ */
/* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context, 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,
shaders_t& outShaders, std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
UTILS_UNUSED_IN_RELEASE shaders_source_t& outShaderSourceCode) noexcept { UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
SYSTRACE_CALL(); SYSTRACE_CALL();
@@ -656,13 +635,209 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
} }
} }
// 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).
*/ */
/* static */ GLuint ShaderCompilerService::linkProgram(OpenGLContext& context, GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
shaders_t const& shaders, 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 {
SYSTRACE_CALL(); SYSTRACE_CALL();
@@ -738,7 +913,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.
*/ */
/* static */ bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noexcept { bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noexcept {
SYSTRACE_CALL(); SYSTRACE_CALL();
@@ -773,24 +948,20 @@ void ShaderCompilerService::executeTickOps() noexcept {
return false; return false;
} }
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE UTILS_NOINLINE
/* static */ void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name, void logCompilationError(io::ostream& out, ShaderStage shaderType,
GLuint shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept { const char* name, GLuint shaderId,
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: case ShaderStage::VERTEX: return "vertex";
return "vertex"; case ShaderStage::FRAGMENT: return "fragment";
case ShaderStage::FRAGMENT: case ShaderStage::COMPUTE: return "compute";
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);
@@ -798,7 +969,8 @@ UTILS_NOINLINE
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() << "\"" << io::endl; << "\"" << infoLog.c_str() << "\""
<< io::endl;
} }
#ifndef NDEBUG #ifndef NDEBUG
@@ -824,7 +996,7 @@ UTILS_NOINLINE
} }
UTILS_NOINLINE UTILS_NOINLINE
/* static */ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept { 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);
@@ -832,204 +1004,9 @@ UTILS_NOINLINE
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() << "\"" << io::endl; << "\"" << infoLog.c_str() << "\""
<< 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,8 +57,6 @@ 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);
@@ -136,9 +134,22 @@ 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, shaders_t& outShaders, shaders_source_t& outShaderSourceCode) noexcept; bool multiview,
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
static GLuint linkProgram(OpenGLContext& context, shaders_t const& shaders, static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept;
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
char* source, size_t len) noexcept;
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
static GLuint linkProgram(OpenGLContext& context,
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept; 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;
-5
View File
@@ -15,7 +15,6 @@
*/ */
#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>
@@ -438,10 +437,6 @@ 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
+7 -14
View File
@@ -70,11 +70,13 @@ struct VulkanRenderPass {
// context are stored in VulkanPlatform. // context are stored in VulkanPlatform.
struct VulkanContext { struct VulkanContext {
public: public:
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties, inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
uint32_t flags, VkFlags reqs) { if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
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 ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) { if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
return i; return i;
} }
} }
@@ -83,13 +85,6 @@ 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;
} }
@@ -123,7 +118,7 @@ public:
} }
inline bool isMultiviewEnabled() const noexcept { inline bool isMultiviewEnabled() const noexcept {
return mPhysicalDeviceVk11Features.multiview == VK_TRUE; return mMultiviewEnabled;
} }
inline bool isClipDistanceSupported() const noexcept { inline bool isClipDistanceSupported() const noexcept {
@@ -147,9 +142,6 @@ 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,
}; };
@@ -162,6 +154,7 @@ 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,33 +282,26 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
fvkutils::DescriptorSetMask curMask = setMask; fvkutils::DescriptorSetMask curMask = setMask;
auto& updateSets = mStashedSets; auto& updateSets = mStashedSets;
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
if (pipelineLayoutIsSame) {
auto& lastBoundSets = mLastBoundInfo.boundSets; auto& lastBoundSets = mLastBoundInfo.boundSets;
setMask.forEachSetBit([&](size_t index) { setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) { if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
curMask.unset(index); curMask.unset(index);
} }
}); });
if (curMask.none() && if (curMask.none() &&
mLastBoundInfo.setMask == setMask && mLastBoundInfo.boundSets == updateSets) { (mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
mLastBoundInfo.boundSets == updateSets)) {
return; return;
} }
} else {
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
curMask.unset(index);
}
});
}
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) { curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
// This code actually binds the descriptor sets. // This code actually binds the descriptor sets.
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->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data()); 1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set); commands->acquire(set);
}); });
@@ -336,7 +329,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->getVkSet(), .dstSet = set->vkSet,
.dstBinding = binding, .dstBinding = binding,
.descriptorCount = 1, .descriptorCount = 1,
.descriptorType = type, .descriptorType = type,
@@ -349,6 +342,10 @@ 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) &&
@@ -358,16 +355,12 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
range.levelCount = 1; range.levelCount = 1;
range.layerCount = 1; range.layerCount = 1;
} }
VkDescriptorImageInfo info{ info.imageView = texture->getView(range);
.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->getVkSet(), .dstSet = set->vkSet,
.dstBinding = binding, .dstBinding = binding,
.descriptorCount = 1, .descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
@@ -385,10 +378,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 = getVkSet(layout); auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count; auto const& count = layout->count;
auto const vklayout = layout->getVkLayout(); auto const vklayout = layout->getVkLayout();
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
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
@@ -397,20 +390,10 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
mDescriptorPool->recycle(count, vklayout, vkSet); mDescriptorPool->recycle(count, vklayout, vkSet);
} }
}); });
set->setVkSet(vkSet);
return set;
} }
VkDescriptorSet VulkanDescriptorSetCache::getVkSet( void VulkanDescriptorSetCache::gc() {
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) { mStashedSets = {};
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,8 +41,6 @@ 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();
@@ -70,21 +68,14 @@ 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,8 +18,6 @@
#include "VulkanHandles.h" #include "VulkanHandles.h"
#include <utils/Hash.h>
namespace filament::backend { namespace filament::backend {
namespace { namespace {
@@ -60,56 +58,6 @@ 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,
@@ -125,44 +73,37 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
} }
} }
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout( fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
VulkanDescriptorSetLayout::Bitmask const& bitmasks, Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
utils::FixedCapacityVector<VkSampler> immutableSamplers) { auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
LayoutKey key = { info);
.bitmask = bitmasks, VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers), auto const& bitmasks = layout->bitmask;
}; if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) { vklayout = itr->second;
return itr->second; } else {
}
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS]; VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0; uint32_t count = 0;
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo); bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo); count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, bitmasks.externalSampler, count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
immutableSamplers); bitmasks.sampler);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment); bitmasks.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout."); assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = { VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count, .bindingCount = count,
.pBindings = toBind, .pBindings = toBind,
}; };
VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout;
return vklayout;
}
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout( vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) { mVkLayouts[bitmasks] = vklayout;
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle, }
info); layout->setVkLayout(vklayout);
layout->setVkLayout(getVkLayout(layout->bitmask));
return layout; return layout;
} }
@@ -26,11 +26,12 @@
#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 {
@@ -40,34 +41,21 @@ 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;
struct LayoutKey { using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
// this describes the layout using bitset. using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
VulkanDescriptorSetLayout::Bitmask bitmask = {}; struct BitmaskGroupEqual {
// number of immutable samplers can be arbitrary; so we hash them into 64-bit. bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
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<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts; tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
}; };
} // namespace filament::backend } // namespace filament::backend
+2 -2
View File
@@ -327,7 +327,7 @@ void VulkanDriver::terminate() {
mStagePool.terminate(); mStagePool.terminate();
mPipelineCache.terminate(); mPipelineCache.terminate();
mFramebufferCache.terminate(); mFramebufferCache.reset();
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.resetFramebuffers(); mFramebufferCache.reset();
} }
if (UTILS_LIKELY(mDefaultRenderTarget)) { if (UTILS_LIKELY(mDefaultRenderTarget)) {
@@ -340,21 +340,15 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
return renderPass; return renderPass;
} }
void VulkanFboCache::resetFramebuffers() noexcept { void VulkanFboCache::reset() noexcept {
for (const auto& pair: mFramebufferCache) { for (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();
} }
+1 -4
View File
@@ -106,11 +106,8 @@ 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 terminate() noexcept; void reset() noexcept;
private: private:
VkDevice mDevice; VkDevice mDevice;
@@ -30,7 +30,6 @@
#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;
@@ -90,9 +89,8 @@ 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;
+9 -24
View File
@@ -66,21 +66,18 @@ 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) == 40); static_assert(sizeof(Bitmask) == 32);
// 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.
@@ -122,16 +119,10 @@ 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 const& getVkLayout() const noexcept { return mVkLayout; } VkDescriptorSetLayout getVkLayout() const { 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;
@@ -146,11 +137,12 @@ 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( VulkanDescriptorSet(VkDescriptorSet rawSet,
fvkutils::UniformBufferBitmask const& dynamicUboMask, fvkutils::UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount, uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn) OnRecycle&& onRecycleFn)
: dynamicUboMask(dynamicUboMask), : vkSet(rawSet),
dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount), uniqueDynamicUboCount(uniqueDynamicUboCount),
mOnRecycleFn(std::move(onRecycleFn)) {} mOnRecycleFn(std::move(onRecycleFn)) {}
@@ -160,13 +152,6 @@ 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);
} }
@@ -178,11 +163,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,15 +33,8 @@ 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{ VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.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),
@@ -55,8 +48,7 @@ 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,6 +108,11 @@ 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();
@@ -141,6 +146,7 @@ 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,9 +49,7 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
void present(); void present();
// Acquire a new image from the swapchain. If the image is not available it would wait until it void acquire(bool& reized);
// 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;
@@ -101,6 +99,7 @@ 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 = fvkutils::getVkFormat(params.format), .format = VK_FORMAT_UNDEFINED,
.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,7 +52,6 @@ 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,
@@ -60,7 +59,6 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
}; };
if (params.externalFormat) { if (params.externalFormat) {
conversionInfo.pNext = &externalFormat; conversionInfo.pNext = &externalFormat;
conversionInfo.format = VK_FORMAT_UNDEFINED;
} }
#endif #endif
@@ -31,8 +31,7 @@ class VulkanYcbcrConversionCache {
public: public:
struct Params { struct Params {
SamplerYcbcrConversion conversion = {}; SamplerYcbcrConversion conversion = {};
TextureFormat format = {}; uint32_t padding = 0;
uint16_t padding = 0;
uint64_t externalFormat = 0; uint64_t externalFormat = 0;
}; };
static_assert(sizeof(Params) == 16); static_assert(sizeof(Params) == 16);
@@ -211,7 +211,6 @@ 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__)
@@ -327,9 +326,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
} }
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
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;
@@ -362,28 +359,14 @@ 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();
@@ -400,7 +383,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 = vk11Features.multiview, .multiview = VK_TRUE,
.multiviewGeometryShader = VK_FALSE, .multiviewGeometryShader = VK_FALSE,
.multiviewTessellationShader = VK_FALSE, .multiviewTessellationShader = VK_FALSE,
}; };
@@ -751,7 +734,6 @@ 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,
@@ -813,8 +795,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
} }
if (mImpl->mDevice == VK_NULL_HANDLE) { if (mImpl->mDevice == VK_NULL_HANDLE) {
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice, mImpl->mDevice =
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features, createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
deviceExts, requestPortabilitySubsetImageView2DOn3DImage); deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
} }
@@ -844,10 +826,12 @@ 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,12 +24,28 @@
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
// Platform specific includes and defines // Platform specific includes and defines
#include <Cocoa/Cocoa.h> #if defined(__APPLE__)
#import <Metal/Metal.h> #include <Cocoa/Cocoa.h>
#import <QuartzCore/CAMetalLayer.h> #import <Metal/Metal.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;
@@ -38,7 +54,11 @@ 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;
} }
@@ -70,6 +90,7 @@ 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.";
@@ -83,6 +104,21 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativ
(VkSurfaceKHR*) &surface); (VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result); << "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
<< "Unable to load vkCreateIOSSurfaceMVK function.";
VkIOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = metalLayer;
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
#endif
return std::make_tuple(surface, VkExtent2D{}); return std::make_tuple(surface, VkExtent2D{});
} }
@@ -668,111 +668,4 @@ 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,12 +69,6 @@ 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,10 +348,6 @@ 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,6 +193,49 @@ 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) {
+103 -179
View File
@@ -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,14 +228,6 @@ 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
} }
@@ -270,6 +262,9 @@ 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,
@@ -286,6 +281,12 @@ 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) {
@@ -295,27 +296,15 @@ 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) {
@@ -331,10 +320,13 @@ 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) {
@@ -344,16 +336,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 {
return allocHandle<WebGPUSwapChain>(); // TODO: use webgpu handle allocator from.
// 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 {
@@ -385,7 +377,7 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
} }
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept { Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++); return allocHandle<WGPUTexture>();
} }
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept { Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
@@ -405,7 +397,7 @@ Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
} }
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept { Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
return allocHandle<WGPURenderPrimitive>(); return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
} }
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept { Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
@@ -413,7 +405,7 @@ Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
} }
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept { Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++); return allocHandle<WGPUTexture>();
} }
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept { Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
@@ -421,7 +413,8 @@ Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
} }
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept { Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
return allocHandle<WebGPUDescriptorSetLayout>(); return Handle<HwDescriptorSetLayout>(
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
} }
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept { Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
@@ -437,15 +430,43 @@ 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) {
mNativeWindow = nativeWindow; // TODO: use webgpu handle allocator from.
// 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();
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow); mSwapChain = std::make_unique<WebGPUSwapChain>(std::move(surface), mAdapter, mDevice, flags);
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter, // TODO configure the surface (maybe before or after creating the swapchain?
mDevice, flags); // how do we get the surface extent?)
assert_invariant(mSwapChain); // TODO actually create the swapchain
// 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."
@@ -458,27 +479,22 @@ 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,
uint32_t height, uint64_t flags) {} uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount, void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) { uint8_t attributeCount, AttributeArray attributes) {}
constructHandle<WGPUVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount, void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) { Handle<HwVertexBufferInfo> vbih) {}
auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vbih);
constructHandle<WGPUVertexBuffer>(vbh, mDevice, vertexCount,vertexBufferInfo->bufferCount, vbih);
}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType, void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) { uint32_t indexCount, BufferUsage usage) {}
auto elementSize = (uint8_t)getElementTypeSize(elementType);
constructHandle<WGPUIndexBuffer>(ibh, mDevice, elementSize, indexCount);
}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount, void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {} BufferObjectBinding bindingType, BufferUsage usage) {}
@@ -488,7 +504,13 @@ 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,
@@ -511,18 +533,7 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType
uint32_t depth, TextureUsage usage) {} uint32_t depth, TextureUsage usage) {}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh, void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) { Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
assert_invariant(mDevice);
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* indexBuffer = handleCast<WGPUIndexBuffer>(ibh);
// auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vertexBuffer->vbih);
// renderPrimitive->setBuffers(vertexBufferInfo, vertexBuffer, indexBuffer);
renderPrimitive->vertexBuffer = vertexBuffer;
renderPrimitive->indexBuffer = indexBuffer;
renderPrimitive->type = pt;
}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {} void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
@@ -541,9 +552,7 @@ 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) {}
@@ -695,8 +704,9 @@ 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->buffers.size()); assert_invariant(index < vertexBuffer->mBuffers.size());
vertexBuffer->setBuffer(bufferObject, index); vertexBuffer->setBuffer(bufferObject, index);
} }
void WebGPUDriver::update3DImage(Handle<HwTexture> th, void WebGPUDriver::update3DImage(Handle<HwTexture> th,
@@ -729,72 +739,48 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
} }
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) { void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { // FWGPU_LOGW << __FUNCTION__<< "\n";
.label = "command_encoder" mTextureView = mSwapChain->GetNextSurfaceTextureView(params.viewport.width, params.viewport.height);
}; wgpu::RenderPassColorAttachment renderPassColorAttachment = {};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor); renderPassColorAttachment.view = mTextureView;
assert_invariant(mCommandEncoder); renderPassColorAttachment.resolveTarget = nullptr;
renderPassColorAttachment.loadOp = wgpu::LoadOp::Clear;
renderPassColorAttachment.storeOp = wgpu::StoreOp::Store;
renderPassColorAttachment.clearValue = wgpu::Color{1, 0 , 0 , 1};
renderPassColorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
// TODO: Remove this code once WebGPU pipeline is implemented wgpu::RenderPassDescriptor renderPassDescriptor = {};
static float red = 1.0f; renderPassDescriptor.nextInChain = nullptr;
if (red - 0.01 > 0) { renderPassDescriptor.colorAttachmentCount = 1;
red -= 0.01; renderPassDescriptor.colorAttachments = &renderPassColorAttachment;
} else { renderPassDescriptor.depthStencilAttachment = nullptr;
red = 1.0f; renderPassDescriptor.timestampWrites = nullptr;
}
assert_invariant(mTextureView);
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
.view = mTextureView,
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
.depthSlice = wgpu::kDepthSliceUndefined,
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
.clearValue = wgpu::Color{red, 0 , 0 , 1},
};
wgpu::RenderPassDescriptor renderPassDescriptor = {
.colorAttachmentCount = 1,
.colorAttachments = &renderPassColorAttachment,
.depthStencilAttachment = nullptr,
.timestampWrites = nullptr,
};
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor); mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom, mRenderPassEncoder.SetViewport((float)params.viewport.left, (float)params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far); (float) params.viewport.width, (float) 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) {
mCommandEncoder = nullptr; wgpu::CommandBufferDescriptor commandBufferDescriptor = {};
mQueue.Submit(1, &mCommandBuffer); commandBufferDescriptor.nextInChain = nullptr;
mCommandBuffer = nullptr; mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
mTextureView = nullptr;
assert_invariant(mSwapChain); // mQueue.Submit(1, &mCommandBuffer);
mSwapChain->present(); // mCommandBuffer = nullptr;
} }
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -848,73 +834,11 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
} }
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) { void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
// VulkanCommandBuffer* commands = mCurrentRenderPass.commandBuffer;
// VkCommandBuffer cmdbuffer = commands->buffer();
// auto prim = resource_ptr<VulkanRenderPrimitive>::cast(&mResourceManager, rph);
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
// commands->acquire(prim);
// This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
(void) vbi;
// mRenderPassEncoder.SetVertexBuffer(uint32_t slot, Buffer const& buffer = nullptr, uint64_t offset = 0, uint64_t size = kWholeSize);
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->buffer, renderPrimitive->indexBuffer->indexFormat);
// uint32_t const bufferCount = vbi->getAttributeCount();
// VkDeviceSize const* offsets = vbi->getOffsets();
// VkBuffer const* buffers = prim->vertexBuffer->getVkBuffers();
//
// // Next bind the vertex buffers and index buffer. One potential performance improvement is to
// // avoid rebinding these if they are already bound, but since we do not (yet) support subranges
// // it would be rare for a client to make consecutive draw calls with the same render primitive.
// vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
// vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->buffer.getGpuBuffer(), 0,
// prim->indexBuffer->indexType);
// METAL
// if (UTILS_UNLIKELY(mContext->currentRenderPassAbandoned)) {
// return;
// }
// FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
// << "bindRenderPrimitive() without a valid command encoder.";
//
// // Bind the user vertex buffers.
// MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
// size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
// size_t maxBufferIndex = 0;
//
// MetalRenderPrimitive const* const primitive = handle_cast<MetalRenderPrimitive>(rph);
// MetalVertexBufferInfo const* const vbi =
// handle_cast<MetalVertexBufferInfo>(primitive->vertexBuffer->vbih);
//
// mContext->currentRenderPrimitive = rph;
//
// auto vb = primitive->vertexBuffer;
// for (auto m : vbi->bufferMapping) {
// assert_invariant(
// m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
// m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
// size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
// vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
// maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
// }
//
// const auto bufferCount = maxBufferIndex + 1;
// MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
// USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, vertexBuffers,
// vertexBufferOffsets, bufferCount);
//
// // Bind the zero buffer, used for missing vertex attributes.
// static const char bytes[16] = { 0 };
// [mContext->currentRenderPassEncoder setVertexBytes:bytes
// length:16
// atIndex:ZERO_VERTEX_BUFFER_BINDING];
} }
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,
+5 -11
View File
@@ -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 "WebGPUHandles.h" #include "webgpu/WebGPUSwapChain.h"
#include <backend/platforms/WebGPUPlatform.h> #include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h" #include "DriverBase.h"
@@ -30,6 +30,8 @@
#include <webgpu/webgpu_cpp.h> #include <webgpu/webgpu_cpp.h>
#include "WebGPUHandles.h"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
@@ -39,8 +41,6 @@
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;
void* mNativeWindow = nullptr; // TODO consider moving to handle allocator when ready
WebGPUSwapChain* mSwapChain = nullptr; std::unique_ptr<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,17 +103,11 @@ 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);
mHandleAllocator.deallocate(handle, p);
}
}; };
}// namespace filament::backend }// namespace filament::backend
+7 -255
View File
@@ -1,267 +1,19 @@
/* //
* Copyright (C) 2025 The Android Open Source Project // Created by Idris Idris Shah on 3/21/25.
* //
* 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);
}
wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool normalized, bool integer) {
using ElementType = filament::backend::ElementType;
// using VertexFormat = wgpu::VertexFormat;
if (normalized) {
switch (type) {
// Single Component Types
case ElementType::BYTE: return wgpu::VertexFormat::Snorm8;
case ElementType::UBYTE: return wgpu::VertexFormat::Unorm8;
case ElementType::SHORT: return wgpu::VertexFormat::Snorm16;
case ElementType::USHORT: return wgpu::VertexFormat::Unorm16;
// Two Component Types
case ElementType::BYTE2: return wgpu::VertexFormat::Snorm8x2;
case ElementType::UBYTE2: return wgpu::VertexFormat::Unorm8x2;
case ElementType::SHORT2: return wgpu::VertexFormat::Snorm16x2;
case ElementType::USHORT2: return wgpu::VertexFormat::Unorm16x2;
// Three Component Types
// There is no vertex format type for 3 byte data in webgpu. Use
// 4 byte signed normalized type and ignore the last byte.
// TODO: This is to be verified.
case ElementType::BYTE3: return wgpu::VertexFormat::Snorm8x4; // NOT MINSPEC
case ElementType::UBYTE3: return wgpu::VertexFormat::Unorm8x4; // NOT MINSPEC
case ElementType::SHORT3: return wgpu::VertexFormat::Snorm16x4; // NOT MINSPEC
case ElementType::USHORT3: return wgpu::VertexFormat::Unorm16x4; // NOT MINSPEC
// Four Component Types
case ElementType::BYTE4: return wgpu::VertexFormat::Snorm8x4;
case ElementType::UBYTE4: return wgpu::VertexFormat::Unorm8x4;
case ElementType::SHORT4: return wgpu::VertexFormat::Snorm16x4;
case ElementType::USHORT4: return wgpu::VertexFormat::Unorm8x4;
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
return wgpu::VertexFormat::Float32x3;
}
}
switch (type) {
// Single Component Types
// There is no direct alternative for SSCALED in webgpu. Convert them to Float32 directly.
// This will result in increased memory on the cpu side.
// TODO: Is Float16 acceptable instead with some potential accuracy errors?
case ElementType::BYTE: return integer ? wgpu::VertexFormat::Sint8 : wgpu::VertexFormat::Float32;
case ElementType::UBYTE: return integer ? wgpu::VertexFormat::Uint8 : wgpu::VertexFormat::Float32;
case ElementType::SHORT: return integer ? wgpu::VertexFormat::Sint16 : wgpu::VertexFormat::Float32;
case ElementType::USHORT: return integer ? wgpu::VertexFormat::Uint16 : wgpu::VertexFormat::Float32;
case ElementType::HALF: return wgpu::VertexFormat::Float16;
case ElementType::INT: return wgpu::VertexFormat::Sint32;
case ElementType::UINT: return wgpu::VertexFormat::Uint32;
case ElementType::FLOAT: return wgpu::VertexFormat::Float32;
// Two Component Types
case ElementType::BYTE2: return integer ? wgpu::VertexFormat::Sint8x2 : wgpu::VertexFormat::Float32x2;
case ElementType::UBYTE2: return integer ? wgpu::VertexFormat::Uint8x2 : wgpu::VertexFormat::Float32x2;
case ElementType::SHORT2: return integer ? wgpu::VertexFormat::Sint16x2 : wgpu::VertexFormat::Float32x2;
case ElementType::USHORT2: return integer ? wgpu::VertexFormat::Uint16x2 : wgpu::VertexFormat::Float32x2;
case ElementType::HALF2: return wgpu::VertexFormat::Float16x2;
case ElementType::FLOAT2: return wgpu::VertexFormat::Float32x2;
// Three Component Types
case ElementType::BYTE3: return wgpu::VertexFormat::Sint8x4; // NOT MINSPEC
case ElementType::UBYTE3: return wgpu::VertexFormat::Uint8x4; // NOT MINSPEC
case ElementType::SHORT3: return wgpu::VertexFormat::Sint16x4; // NOT MINSPEC
case ElementType::USHORT3: return wgpu::VertexFormat::Uint16x4; // NOT MINSPEC
case ElementType::HALF3: return wgpu::VertexFormat::Float16x4; // NOT MINSPEC
case ElementType::FLOAT3: return wgpu::VertexFormat::Float32x3;
// Four Component Types
case ElementType::BYTE4: return integer ? wgpu::VertexFormat::Sint8x4 : wgpu::VertexFormat::Float32x4;
case ElementType::UBYTE4: return integer ? wgpu::VertexFormat::Uint8x4 : wgpu::VertexFormat::Float32x4;
case ElementType::SHORT4: return integer ? wgpu::VertexFormat::Sint16x4 : wgpu::VertexFormat::Float32x4;
case ElementType::USHORT4: return integer ? wgpu::VertexFormat::Uint16x4 : wgpu::VertexFormat::Float32x4;
case ElementType::HALF4: return wgpu::VertexFormat::Float16x4;
case ElementType::FLOAT4: return wgpu::VertexFormat::Float32x4;
}
FILAMENT_CHECK_POSTCONDITION(false) << "Vertex format should always be defined.";
return wgpu::VertexFormat::Float32x3;
}
} // namespace
namespace filament::backend { namespace filament::backend {
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, WGPUVertexBuffer::WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount,
AttributeArray const& attributes) Handle<WGPUVertexBufferInfo> vbih)
: HwVertexBufferInfo(bufferCount, attributeCount),
mVertexBufferLayout(bufferCount),
mAttributes(bufferCount) {
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute attrib = attributes[attribIndex];
// Ignore the attributes which are not bind to vertex buffers.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
continue;
}
assert_invariant(attrib.buffer < bufferCount);
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
mAttributes[attrib.buffer].push_back({
.format = vertexFormat,
.offset = attrib.offset,
.shaderLocation = attribIndex,
});
mVertexBufferLayout[attrib.buffer] = {
.arrayStride = attrib.stride,
};
}
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++) {
mVertexBufferLayout[bufferIndex] = {
.attributeCount = mAttributes[bufferIndex].size(),
.attributes = mAttributes[bufferIndex].data(),
};
}
}
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
uint32_t indexCount)
: buffer(createIndexBuffer(device, elementSize, indexCount)),
indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vextexCount), : HwVertexBuffer(vextexCount),
vbih(vbih), vbih(vbih),
buffers(bufferCount) { mBuffers(MAX_VERTEX_BUFFER_COUNT) {}
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),
bufferObjectBinding(bindingType) {} mBindingType(bindingType) {}
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
if (any(ShaderStageFlags::VERTEX & fFlags)) {
retStages |= wgpu::ShaderStage::Vertex;
}
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
retStages |= wgpu::ShaderStage::Fragment;
}
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
retStages |= wgpu::ShaderStage::Compute;
}
return retStages;
}
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
wgpu::Device const* device) {
assert_invariant(device->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_" + 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() {}
void WGPURenderPrimitive::setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer) {
}
}// namespace filament::backend }// namespace filament::backend
+18 -85
View File
@@ -1,19 +1,6 @@
/* //
* Copyright (C) 2025 The Android Open Source Project // Created by Idris Idris Shah on 3/21/25.
* //
* 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
@@ -22,87 +9,44 @@
#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 <utils/StructureOfArrays.h>
#include <webgpu/webgpu_cpp.h> #include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend { namespace filament::backend {
struct WGPUBufferObject; struct WGPUBufferObject;
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
// VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout
// is equal to bufferCount. Each VertexBufferLayout can contain multiple VertexAttribute. Bind index
// of vertex buffer is implicitly calculated by the position of VertexBufferLayout in an array.
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)
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const { : HwVertexBufferInfo(bufferCount, attributeCount),
return mVertexBufferLayout.data(); attributes(attributes) {}
} AttributeArray attributes;
inline uint32_t getVertexBufferLayoutSize() const {
return mVertexBufferLayout.size();
}
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
return mAttributes[index].data();
}
inline uint32_t getVertexAttributeSize(uint32_t index) const {
return mAttributes[index].size();
}
private:
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout;
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes;
}; };
struct WGPUVertexBuffer : public HwVertexBuffer { struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount, WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount, Handle<WGPUVertexBufferInfo> vbih);
Handle<HwVertexBufferInfo> vbih); void setBuffer(WGPUBufferObject* bufferObject, uint32_t index);
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index); Handle<WGPUVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> mBuffers;
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> buffers;
}; };
struct WGPUIndexBuffer : public HwIndexBuffer { struct WGPUIndexBuffer : public HwIndexBuffer {
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize, WGPUIndexBuffer(BufferUsage usage, uint8_t elementSize, uint32_t indexCount);
uint32_t indexCount);
wgpu::Buffer buffer; wgpu::Buffer buffer;
wgpu::IndexFormat indexFormat;
}; };
// TODO: Currently WGPUBufferObject 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 buffer; wgpu::Buffer mBuffer;
const BufferObjectBinding bufferObjectBinding; const BufferObjectBinding mBindingType;
}; };
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout { class WGPUTexture : public HwTexture {
public: public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const* device);
~WebGPUDescriptorSetLayout();
private:
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
wgpu::BindGroupLayout mLayout;
};
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUTexture : public HwTexture {
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, 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;
@@ -112,22 +56,11 @@ struct WGPUTexture : public HwTexture {
wgpu::Texture texture = nullptr; wgpu::Texture texture = nullptr;
}; };
struct WGPURenderPrimitive : public HwRenderPrimitive { class WGPURenderTarget : public HwRenderTarget {
WGPURenderPrimitive(); public:
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 struct WGPURenderTarget; friend class 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)
+38 -37
View File
@@ -190,13 +190,10 @@ wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
} }
} }
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device, void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device& device,
wgpu::SurfaceCapabilities const& capabilities, wgpu::Extent2D const& surfaceSize, wgpu::SurfaceCapabilities const& capabilities, bool useSRGBColorSpace) {
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 =
@@ -208,8 +205,8 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
namespace filament::backend { namespace filament::backend {
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize, WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter,
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags) 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)) {
@@ -220,55 +217,59 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const&
#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, surfaceSize, useSRGBColorSpace); initConfig(mConfig, device, capabilities, useSRGBColorSpace);
mSurface.Configure(&mConfig);
} }
WebGPUSwapChain::~WebGPUSwapChain() { WebGPUSwapChain::~WebGPUSwapChain() {
if (mConfigured) {
mSurface.Unconfigure(); mSurface.Unconfigure();
mConfigured = false;
}
} }
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) { void WebGPUSwapChain::GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture* texture) {
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0) if (width < 1 || height < 1) {
<< "WebGPUSwapChain::setExtent: Invalid width " << currentSurfaceSize.width PANIC_LOG("WebGPUSwapChain::GetCurrentTexture: Invalid width and/or height requested.");
<< " and/or height " << currentSurfaceSize.height << " requested."; return;
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) { }
mConfig.width = currentSurfaceSize.width; if (mConfig.width != width || mConfig.height != height || !mConfigured) {
mConfig.height = currentSurfaceSize.height; mConfig.width = width;
mConfig.height = height;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) #if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig); printSurfaceConfiguration(mConfig);
#endif #endif
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height
<< utils::io::endl;
// TODO we may need to ensure no surface texture is flight when we do this. some
// synchronization may be necessary
mSurface.Configure(&mConfig); mSurface.Configure(&mConfig);
mConfigured = true;
return;
} }
mSurface.GetCurrentTexture(texture);
} }
wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView( wgpu::TextureView WebGPUSwapChain::GetNextSurfaceTextureView(uint32_t width, uint32_t height) {
wgpu::Extent2D const& currentSurfaceSize) {
setExtent(currentSurfaceSize);
wgpu::SurfaceTexture surfaceTexture; wgpu::SurfaceTexture surfaceTexture;
mSurface.GetCurrentTexture(&surfaceTexture); GetCurrentTexture(width, height, &surfaceTexture);
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) { if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::Success) {
return nullptr; return nullptr;
} }
// Create a view for this surface texture // Create a view for this surface texture
// TODO: review these initiliazations as webgpu pipeline gets mature wgpu::TextureViewDescriptor textureViewDescriptor;
wgpu::TextureViewDescriptor textureViewDescriptor = { textureViewDescriptor.nextInChain = nullptr;
.label = "texture_view", textureViewDescriptor.label = "Surface texture view";
.format = surfaceTexture.texture.GetFormat(), textureViewDescriptor.format = surfaceTexture.texture.GetFormat();
.dimension = wgpu::TextureViewDimension::e2D, textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
.baseMipLevel = 0, textureViewDescriptor.baseMipLevel = 0;
.mipLevelCount = 1, textureViewDescriptor.mipLevelCount = 1;
.baseArrayLayer = 0, textureViewDescriptor.baseArrayLayer = 0;
.arrayLayerCount = 1 textureViewDescriptor.arrayLayerCount = 1;
}; textureViewDescriptor.aspect = wgpu::TextureAspect::All;
return surfaceTexture.texture.CreateView(&textureViewDescriptor); wgpu::TextureView textureView = surfaceTexture.texture.CreateView(&textureViewDescriptor);
return textureView;
} }
void WebGPUSwapChain::present() { void WebGPUSwapChain::Present() {
assert_invariant(mSurface); assert_invariant(mSurface);
mSurface.Present(); mSurface.Present();
} }
@@ -19,28 +19,26 @@
#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 final : public Platform::SwapChain, HwSwapChain { class WebGPUSwapChain : public Platform::SwapChain {
public: public:
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize, WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter, wgpu::Device& device,
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags); uint64_t flags);
~WebGPUSwapChain(); ~WebGPUSwapChain();
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&); void GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture*);
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,7 +18,6 @@
#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>
@@ -29,14 +28,6 @@
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,8 +24,21 @@
#include <cstdint> #include <cstdint>
// Platform specific includes and defines // Platform specific includes and defines
#include <Cocoa/Cocoa.h> #if defined(__APPLE__)
#import <QuartzCore/CAMetalLayer.h> #include <Cocoa/Cocoa.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
@@ -33,19 +46,14 @@
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;
// Both IOS and MacOS expects CAMetalLayer. #if defined(__APPLE__)
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow; auto nsView = (__bridge NSView*) 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 = {
@@ -54,6 +62,19 @@ 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,74 +79,6 @@
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,16 +30,6 @@
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
+3 -17
View File
@@ -43,12 +43,10 @@ using namespace image;
namespace test { namespace test {
Backend BackendTest::sBackend = Backend::NOOP; Backend BackendTest::sBackend = Backend::NOOP;
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
bool BackendTest::sIsMobilePlatform = false; bool BackendTest::sIsMobilePlatform = false;
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) { void BackendTest::init(Backend backend, bool isMobilePlatform) {
sBackend = backend; sBackend = backend;
sOperatingSystem = operatingSystem;
sIsMobilePlatform = isMobilePlatform; sIsMobilePlatform = isMobilePlatform;
} }
@@ -201,18 +199,6 @@ void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, si
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd)); getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
} }
bool BackendTest::matchesEnvironment(Backend backend) {
return sBackend == backend;
}
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
return sOperatingSystem == operatingSystem;
}
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) {
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend);
}
class Environment : public ::testing::Environment { class Environment : public ::testing::Environment {
public: public:
virtual void SetUp() override { virtual void SetUp() override {
@@ -224,8 +210,8 @@ public:
} }
}; };
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) { void initTests(Backend backend, bool isMobile, int& argc, char* argv[]) {
BackendTest::init(backend, operatingSystem, isMobile); BackendTest::init(backend, isMobile);
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new Environment); ::testing::AddGlobalTestEnvironment(new Environment);
} }
+1 -6
View File
@@ -31,10 +31,9 @@ namespace test {
class BackendTest : public ::testing::Test { class BackendTest : public ::testing::Test {
public: public:
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform); static void init(Backend backend, bool isMobilePlatform);
static Backend sBackend; static Backend sBackend;
static OperatingSystem sOperatingSystem;
static bool sIsMobilePlatform; static bool sIsMobilePlatform;
protected: protected:
@@ -72,10 +71,6 @@ protected:
filament::backend::DriverApi& getDriverApi() { return *commandStream; } filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; } filament::backend::Driver& getDriver() { return *driver; }
static bool matchesEnvironment(Backend backend);
static bool matchesEnvironment(OperatingSystem operatingSystem);
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
private: private:
filament::backend::Driver* driver = nullptr; filament::backend::Driver* driver = nullptr;
-60
View File
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PlatformRunner.h"
namespace utils {
template<>
CString to_string<test::Backend>(test::Backend backend) noexcept {
switch (backend) {
case test::Backend::OPENGL: {
return "OpenGL";
}
case test::Backend::VULKAN: {
return "Vulkan";
}
case test::Backend::METAL: {
return "Metal";
}
case test::Backend::WEBGPU: {
return "WebGPU";
}
case test::Backend::NOOP:
default: {
return "No-op";
}
}
}
template<>
CString to_string(test::OperatingSystem os) noexcept {
switch (os) {
case test::OperatingSystem::LINUX: {
return "Linux";
}
case test::OperatingSystem::APPLE: {
return "Apple";
}
case test::OperatingSystem::OTHER:
default: {
return "Other";
}
}
}
} // namespace utils
+2 -13
View File
@@ -19,7 +19,6 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "utils/CString.h"
namespace test { namespace test {
@@ -35,15 +34,6 @@ enum class Backend : uint8_t {
NOOP = 5, NOOP = 5,
}; };
enum class OperatingSystem: uint8_t {
OTHER = 1,
// Also represents android phones.
LINUX = 2,
// Also represents iOS phones.
APPLE = 3,
// TODO: When tests support windows add it here.
};
struct NativeView { struct NativeView {
void* ptr = nullptr; void* ptr = nullptr;
size_t width = 0, height = 0; size_t width = 0, height = 0;
@@ -61,10 +51,9 @@ NativeView getNativeView();
* No tests will be run yet. * No tests will be run yet.
* *
* @param backend The backend to run the tests on. * @param backend The backend to run the tests on.
* @param operatingSystem The operating system the tests are being run on.
* @param isMobile True if the platform is a mobile platform (iOS or Android). * @param isMobile True if the platform is a mobile platform (iOS or Android).
*/ */
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]); void initTests(Backend backend, bool isMobile, int& argc, char* argv[]);
/** /**
* Test runners should call runTests when they are ready for tests to be run. * Test runners should call runTests when they are ready for tests to be run.
@@ -79,6 +68,6 @@ int runTests();
*/ */
Backend parseArgumentsForBackend(int argc, char* argv[]); Backend parseArgumentsForBackend(int argc, char* argv[]);
} // namespace test }
#endif #endif
+15 -25
View File
@@ -23,44 +23,30 @@ namespace test {
using namespace filament::backend; using namespace filament::backend;
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup(cleanup) { Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniforms.size()); utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniformNames.size());
for (unsigned char i = 0; i < config.uniforms.size(); ++i) { for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
kLayouts[i] = { kLayouts[i] =
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER), { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
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;
descriptors[kDescriptorSetIndex] = filamat::DescriptorSetInfo(config.uniforms.size()); for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
for (unsigned char i = 0; i < config.uniforms.size(); ++i) { descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[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) {
Program::DescriptorBindingsInfo bindingsInfo(config.uniforms.size()); prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
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 }));
}
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const { mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
} }
filament::backend::ProgramHandle Shader::getProgram() const { filament::backend::ProgramHandle Shader::getProgram() const {
@@ -71,4 +57,8 @@ filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() co
return mDescriptorSetLayout; return mDescriptorSetLayout;
} }
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
return mDescriptorSet;
}
} // namespace test } // namespace test
+6 -26
View File
@@ -18,22 +18,14 @@
#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<UniformConfig> uniforms; std::vector<utils::CString> uniformNames;
}; };
// All values describing a uniform. // All values describing a uniform.
@@ -43,7 +35,6 @@ 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.
@@ -55,7 +46,6 @@ 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();
@@ -89,14 +79,12 @@ 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>
@@ -107,8 +95,7 @@ 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
}; };
} }
@@ -133,16 +120,9 @@ void Shader::bindUniform(filament::backend::DriverApi& api,
UniformBindingConfig config) const { UniformBindingConfig config) const {
auto resolvedConfig = config.resolve<UniformType>(); auto resolvedConfig = config.resolve<UniformType>();
filament::backend::DescriptorSetHandle descriptorSet; api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
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(descriptorSet, resolvedConfig.set, {}); api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
} }
template<typename UniformType> template<typename UniformType>
-253
View File
@@ -1,253 +0,0 @@
/*
* 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
-50
View File
@@ -1,50 +0,0 @@
/*
* 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
@@ -1,63 +0,0 @@
/*
* 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
-96
View File
@@ -1,96 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Skip.h"
#include <sstream>
namespace test {
SkipEnvironment::SkipEnvironment(test::Backend backend) : backend(backend) {}
SkipEnvironment::SkipEnvironment(test::OperatingSystem os) : os(os) {}
SkipEnvironment::SkipEnvironment(test::OperatingSystem os, test::Backend backend)
: backend(backend),
os(os) {}
bool SkipEnvironment::matches() {
bool backendMatches = !backend.has_value() || *backend == BackendTest::sBackend;
bool osMatches = !os.has_value() || *os == BackendTest::sOperatingSystem;
bool isMobileMatches = !isMobile.has_value() || *isMobile == BackendTest::sIsMobilePlatform;
return backendMatches && osMatches && isMobileMatches;
}
std::string SkipEnvironment::describe() {
std::stringstream result;
if (matches()) {
result << "environment matches because " << describe_actual_environment() << ".";
} else {
result << "environment does not match because " << describe_requirements() << " but "
<< describe_actual_environment() << ".";
}
return result.str();
}
std::string SkipEnvironment::describe_actual_environment() {
bool resultWritten = false;
std::stringstream reality;
if (backend.has_value()) {
reality << "backend was " << utils::to_string(BackendTest::sBackend).c_str();
resultWritten = true;
}
if (os.has_value()) {
if (resultWritten) {
reality << ", and ";
}
reality << "operating system was "
<< utils::to_string(BackendTest::sOperatingSystem).c_str();
resultWritten = true;
}
if (isMobile.has_value()) {
if (resultWritten) {
reality << ", and ";
}
reality << "device " << (BackendTest::sIsMobilePlatform ? "was" : "was not") << " mobile";
resultWritten = true;
}
return reality.str();
}
std::string SkipEnvironment::describe_requirements() {
bool resultWritten = false;
std::stringstream requirement;
if (backend.has_value()) {
requirement << "backend needs to be " << utils::to_string(*backend).c_str();
resultWritten = true;
}
if (os.has_value()) {
if (resultWritten) {
requirement << ", and ";
}
requirement << "operating system needs to be " << utils::to_string(*os).c_str();
resultWritten = true;
}
if (isMobile.has_value() && BackendTest::sIsMobilePlatform != isMobile) {
if (resultWritten) {
requirement << ", and ";
}
requirement << "device needs to " << (*isMobile ? "be" : "not be") << " mobile";
resultWritten = true;
}
return requirement.str();
}
} // namespace test
-55
View File
@@ -1,55 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_SKIP_H
#define TNT_SKIP_H
#include <gtest/gtest.h>
#include "BackendTest.h"
// skipEnvironment must be a test::SkipEnvironment
#define SKIP_IF(skipEnvironment) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
GTEST_SKIP() << "Skipping test as the " << skip.describe(); \
} \
} while (false)
namespace test {
struct SkipEnvironment {
SkipEnvironment(const SkipEnvironment&) = default;
explicit SkipEnvironment(test::Backend backend);
explicit SkipEnvironment(test::OperatingSystem os);
SkipEnvironment(test::OperatingSystem os, test::Backend backend);
std::optional<test::Backend> backend;
std::optional<test::OperatingSystem> os;
std::optional<bool> isMobile;
bool matches();
// Describes the current state of either matching or mismatching.
std::string describe();
// Describe all the non-null requirements.
std::string describe_requirements();
// Describes the environment's status for all the attributes that are non-null.
std::string describe_actual_environment();
};
} // namespace test
#endif// TNT_SKIP_H
+1 -1
View File
@@ -51,6 +51,6 @@ int main(int argc, char* argv[]) {
return 1; return 1;
} }
test::initTests(backend, test::OperatingSystem::LINUX, false, argc, argv); test::initTests(backend, false, argc, argv);
return test::runTests(); return test::runTests();
} }
+1 -1
View File
@@ -98,7 +98,7 @@ test::NativeView getNativeView() {
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
auto backend = test::parseArgumentsForBackend(argc, argv); auto backend = test::parseArgumentsForBackend(argc, argv);
test::initTests(backend, test::OperatingSystem::APPLE, false, argc, argv); test::initTests(backend, false, argc, argv);
AppDelegate* delegate = [AppDelegate new]; AppDelegate* delegate = [AppDelegate new];
delegate.backend = backend; delegate.backend = backend;
NSApplication* app = [NSApplication sharedApplication]; NSApplication* app = [NSApplication sharedApplication];
+39 -11
View File
@@ -19,7 +19,6 @@
#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>
@@ -41,14 +40,48 @@ 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;
@@ -280,11 +313,7 @@ TEST_F(BlitTest, ColorResolve) {
constexpr auto kColorTexFormat = TextureFormat::RGBA8; constexpr auto kColorTexFormat = TextureFormat::RGBA8;
constexpr int kSampleCount = 4; constexpr int kSampleCount = 4;
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{ Shader shader = createShader();
.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);
@@ -327,15 +356,14 @@ 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(SimpleMaterialParams), auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC)); BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// Draw red triangle into srcRenderTarget. // Draw red triangle into srcRenderTarget.
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{ shader.uploadUniform(api, ubuffer, MaterialParams{
.color = float4(1, 0, 0, 1), .color = float4(1, 0, 0, 1),
.scaleMinusOne = float4(0, 0, -0.5, 0), .scale = float4(1, 1, 0.5, 0),
.offset = float4(0.5, 0.5, 0, 0),
}); });
shader.bindUniform<SimpleMaterialParams>(api, ubuffer); shader.bindUniform<MaterialParams>(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.
{ {
+83 -35
View File
@@ -18,18 +18,72 @@
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "Shader.h"
#include "SharedShaders.h" #include "ShaderGenerator.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(SimpleMaterialParams), .dataSize = sizeof(MaterialParams),
.bufferSize = sizeof(SimpleMaterialParams) + 64, .bufferSize = sizeof(MaterialParams) + 64,
.byteOffset = 64 .byteOffset = 64
}; };
@@ -39,10 +93,8 @@ public:
protected: protected:
Shader createShader() { Shader createShader() {
return SharedShaders::makeShader(getDriverApi(), mCleanup, ShaderRequest{ return Shader(getDriverApi(), mCleanup, ShaderConfig{
.mVertexType = VertexShaderType::Simple, vertex, fragment, {"Params"}
.mFragmentType = FragmentShaderType::SolidColored,
.mUniformType = ShaderUniformType::SimpleWithPadding
}); });
} }
@@ -77,7 +129,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;
@@ -92,18 +144,17 @@ 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(SimpleMaterialParams) + 64, auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC)); BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig); shader.bindUniform<MaterialParams>(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, SimpleMaterialParams{ shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
.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 }
}); });
@@ -114,21 +165,19 @@ 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 }, const filament::math::float2 v[3] {{low, low}, {high, low}, {low, high}};
{ 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);
} }
} }
@@ -171,29 +220,28 @@ 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(SimpleMaterialParams) + 64, auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC)); BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig); shader.bindUniform<MaterialParams>(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, SimpleMaterialParams{ shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
.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;
@@ -202,18 +250,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, scaleMinusOne, offset.x, and offset.y. // color.b, color.a, offset.x, and offset.y.
const UniformBindingConfig partialBindingConfig = { shader.uploadUniform(api, ubuffer, UniformBindingConfig{
.dataSize = sizeof(float) * 8, .dataSize = sizeof(std::array<float, 4>),
.bufferSize = sizeof(SimpleMaterialParams) + 64, .bufferSize = kBindingConfig.bufferSize,
.byteOffset = 64 + offsetof(SimpleMaterialParams, color.b) .byteOffset = *kBindingConfig.byteOffset + offsetof(MaterialParams, color.b),
}; },
shader.uploadUniform(api, ubuffer, partialBindingConfig, std::array<float, 4>{
std::array<float, 8>{ // color.b, color.a
1.0f, 1.0f, // color.b, color.a 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 0.0f, // scale // offset.x, offset.y
0.5f, 0.5f // offset.x, offset.y 0.5f, 0.5f }
}); );
params.flags.clear = TargetBufferFlags::NONE; params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE;
-5
View File
@@ -17,7 +17,6 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Skip.h"
using namespace filament; using namespace filament;
using namespace filament::backend; using namespace filament::backend;
@@ -25,8 +24,6 @@ using namespace filament::backend;
namespace test { namespace test {
TEST_F(BackendTest, FrameScheduledCallback) { TEST_F(BackendTest, FrameScheduledCallback) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi(); auto& api = getDriverApi();
Cleanup cleanup(api); Cleanup cleanup(api);
@@ -84,8 +81,6 @@ TEST_F(BackendTest, FrameScheduledCallback) {
} }
TEST_F(BackendTest, FrameCompletedCallback) { TEST_F(BackendTest, FrameCompletedCallback) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi(); auto& api = getDriverApi();
Cleanup cleanup(api); Cleanup cleanup(api);
+50 -23
View File
@@ -17,7 +17,7 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
@@ -96,6 +96,16 @@ 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);
@@ -130,13 +140,38 @@ 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 };
Shader shader = Shader(api, cleanup, ShaderConfig { filamat::DescriptorSets descriptors;
.vertexShader = fullscreenVs, descriptors[1] = {
.fragmentShader = fullscreenFs, { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}, {"Params"}} samplerInfo },
{ "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());
@@ -183,8 +218,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 = shader.getProgram(); state.program = program;
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() }; state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0); api.beginFrame(0, 0, 0);
@@ -198,24 +233,20 @@ 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, {});
UniformBindingConfig uniformBinding{ uploadUniforms(getDriverApi(), ubuffer, {
.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();
@@ -231,24 +262,20 @@ 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, {});
UniformBindingConfig uniformBinding{ uploadUniforms(getDriverApi(), ubuffer, {
.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();
+132 -60
View File
@@ -18,14 +18,16 @@
#include "BackendTestUtils.h" #include "BackendTestUtils.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h" #include "TrianglePrimitive.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>
@@ -40,6 +42,19 @@ 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;
@@ -104,15 +119,6 @@ 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,
@@ -210,7 +216,7 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
} }
} }
TEST_F(LoadImageTest, UpdateImage2D) { TEST_F(BackendTest, 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;
@@ -300,14 +306,28 @@ TEST_F(LoadImageTest, 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);
Shader shader(api, cleanup, ShaderConfig{ ShaderGenerator shaderGen(
.vertexShader = mVertexShader, vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
.fragmentShader= fragment,
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}} Program prog = shaderGen.getProgram(api);
}); 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;
@@ -329,15 +349,14 @@ TEST_F(LoadImageTest, 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{}, shader.getDescriptorSetLayout() }}, renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, shader.getProgram()); defaultRenderTarget, swapChain, program);
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash); readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
@@ -351,9 +370,8 @@ TEST_F(LoadImageTest, UpdateImage2D) {
flushAndWait(); flushAndWait();
} }
TEST_F(LoadImageTest, UpdateImageSRGB) { TEST_F(BackendTest, UpdateImageSRGB) {
auto& api = getDriverApi(); auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture(); api.startCapture();
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA; PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
@@ -361,23 +379,36 @@ TEST_F(LoadImageTest, 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 = cleanup.add(createSwapChain()); auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0)); auto defaultRenderTarget = 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);
Shader shader(api, cleanup, ShaderConfig{ ShaderGenerator shaderGen(
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{ vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
"text_tex", DescriptorType::SAMPLER, samplerInfo Program prog = shaderGen.getProgram(api);
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 = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, Handle<HwTexture> const texture = 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;
@@ -405,7 +436,6 @@ TEST_F(LoadImageTest, 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
@@ -413,8 +443,8 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
api.bindDescriptorSet(descriptorSet, 1, {}); api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }}, renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, shader.getProgram()); defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 359858623; static const uint32_t expectedHash = 359858623;
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash); readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
@@ -423,14 +453,21 @@ TEST_F(LoadImageTest, 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(LoadImageTest, UpdateImageMipLevel) { TEST_F(BackendTest, UpdateImageMipLevel) {
auto& api = getDriverApi(); auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture(); api.startCapture();
PixelDataFormat pixelFormat = PixelDataFormat::RGBA; PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
@@ -438,27 +475,38 @@ TEST_F(LoadImageTest, 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 = cleanup.add(createSwapChain()); auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0)); auto defaultRenderTarget = 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);
Shader shader(api, cleanup, ShaderConfig { ShaderGenerator shaderGen(
.vertexShader = mVertexShader, vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
.fragmentShader = fragment, Program prog = shaderGen.getProgram(api);
.uniforms = {{"test_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 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 = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 3, Handle<HwTexture> texture = 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);
@@ -467,7 +515,6 @@ TEST_F(LoadImageTest, 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
@@ -475,8 +522,8 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
api.bindDescriptorSet(descriptorSet, 1, {}); api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }}, renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, shader.getProgram()); defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 3644679986; static const uint32_t expectedHash = 3644679986;
readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash); readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash);
@@ -485,14 +532,21 @@ TEST_F(LoadImageTest, 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(LoadImageTest, UpdateImage3D) { TEST_F(BackendTest, UpdateImage3D) {
auto& api = getDriverApi(); auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture(); api.startCapture();
PixelDataFormat pixelFormat = PixelDataFormat::RGBA; PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
@@ -502,24 +556,35 @@ TEST_F(LoadImageTest, 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 = cleanup.add(createSwapChain()); auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0)); auto defaultRenderTarget = 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);
Shader shader(api, cleanup, ShaderConfig { ShaderGenerator shaderGen(
.vertexShader = mVertexShader, vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
.fragmentShader = fragment, Program prog = shaderGen.getProgram(api);
.uniforms = {{"test_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> texture = cleanup.add(api.createTexture(samplerType, 1, Handle<HwTexture> texture = 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;
@@ -541,7 +606,6 @@ TEST_F(LoadImageTest, 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
@@ -549,8 +613,8 @@ TEST_F(LoadImageTest, UpdateImage3D) {
api.bindDescriptorSet(descriptorSet, 1, {}); api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }}, renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, shader.getProgram()); defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 3644679986; static const uint32_t expectedHash = 3644679986;
readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash); readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash);
@@ -559,9 +623,17 @@ TEST_F(LoadImageTest, 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
+21 -9
View File
@@ -17,8 +17,7 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
namespace { namespace {
@@ -27,6 +26,21 @@ 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;
@@ -57,12 +71,10 @@ TEST_F(BackendTest, MRT) {
auto swapChain = cleanup.add(createSwapChain()); auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
Shader shader(api, cleanup, ShaderConfig{ // Create a program.
.vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop, ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderUniformType::None), Program p = shaderGen.getProgram(api);
.fragmentShader = fragment, auto program = cleanup.add(api.createProgram(std::move(p)));
.uniforms = {}
});
TrianglePrimitive triangle(api); TrianglePrimitive triangle(api);
@@ -110,7 +122,7 @@ TEST_F(BackendTest, MRT) {
params.flags.discardEnd = TargetBufferFlags::NONE; params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state; PipelineState state;
state.program = shader.getProgram(); state.program = program;
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;
+66 -34
View File
@@ -19,8 +19,7 @@
#include "BackendTestUtils.h" #include "BackendTestUtils.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
@@ -35,7 +34,18 @@ namespace {
// Shaders // Shaders
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
std::string fragmentTexturedLod (R"(#version 450 core 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) out vec4 fragColor;
layout(location = 0) in vec2 uv; layout(location = 0) in vec2 uv;
@@ -47,6 +57,18 @@ 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 {
@@ -66,25 +88,39 @@ TEST_F(BackendTest, TextureViewLod) {
auto swapChain = cleanup.add(createSwapChain()); auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
Shader whiteShader = SharedShaders::makeShader(api, cleanup, ShaderRequest { // Create a program that draws only white.
.mVertexType = VertexShaderType::Textured, Handle<HwProgram> whiteProgram;
.mFragmentType = FragmentShaderType::White, {
.mUniformType = ShaderUniformType::Sampler ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform);
}); 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.
std::string vertexShader = SharedShaders::getVertexShaderText( Handle<HwProgram> textureProgram;
VertexShaderType::Textured, ShaderUniformType::Sampler); {
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "backend_test", "sib_tex", 0,
"backend_test", "sib_tex", 0,
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false }; SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
Shader texturedShader(api, cleanup, ShaderConfig { filamat::DescriptorSets descriptors;
.vertexShader = vertexShader, descriptors[1] = { { "backend_test_sib_tex",
.fragmentShader = fragmentTexturedLod, { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, samplerInfo } };
.uniforms = {{ ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
"backend_test_sib_tex", DescriptorType::SAMPLER, samplerInfo Program p = shaderGen.getProgram(api);
}} p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
}); 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)
@@ -93,10 +129,9 @@ 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, Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
kMipLevels, TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1, TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
| TextureUsage::UPLOADABLE));
// Create image data. // Create image data.
auto pixelFormat = PixelDataFormat::RGBA; auto pixelFormat = PixelDataFormat::RGBA;
@@ -144,7 +179,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 = whiteShader.getProgram(); ps.program = whiteProgram;
ps.rasterState.colorWrite = true; ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false; ps.rasterState.depthWrite = false;
api.beginRenderPass(renderTarget, params); api.beginRenderPass(renderTarget, params);
@@ -152,8 +187,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass(); api.endRenderPass();
} }
backend::Handle<HwRenderTarget> defaultRenderTarget = backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
cleanup.add(api.createDefaultRenderTarget(0));
RenderPassParams params = {}; RenderPassParams params = {};
fullViewport(params); fullViewport(params);
@@ -163,19 +197,18 @@ TEST_F(BackendTest, TextureViewLod) {
params.flags.discardEnd = TargetBufferFlags::NONE; params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state; PipelineState state;
state.program = texturedShader.getProgram(); state.program = textureProgram;
state.pipelineLayout.setLayout = { texturedShader.getDescriptorSetLayout() }; state.pipelineLayout.setLayout = { descriptorSetLayout };
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;
DescriptorSetHandle descriptorSet13 = texturedShader.createDescriptorSet(api); api.updateDescriptorSetTexture(descriptorSet[0], 0, texture13, {
api.updateDescriptorSetTexture(descriptorSet13, 0, texture13, {
.filterMag = SamplerMagFilter::NEAREST, .filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
api.bindDescriptorSet(descriptorSet13, 0, {}); api.bindDescriptorSet(descriptorSet[0], 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
@@ -188,12 +221,11 @@ 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));
DescriptorSetHandle descriptorSet22 = texturedShader.createDescriptorSet(api); api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
api.updateDescriptorSetTexture(descriptorSet22, 0, texture22, {
.filterMag = SamplerMagFilter::NEAREST, .filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
api.bindDescriptorSet(descriptorSet22, 0, {}); api.bindDescriptorSet(descriptorSet[1], 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,8 +17,7 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
namespace { namespace {
@@ -27,7 +26,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;
@@ -47,6 +46,16 @@ 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 {
@@ -69,11 +78,9 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
// Create a program. // Create a program.
Shader shader(api, cleanup, ShaderConfig{ ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
.vertexShader = vertex, Program p = shaderGen.getProgram(api);
.fragmentShader = SharedShaders::getFragmentShaderText(FragmentShaderType::White, auto program = cleanup.add(api.createProgram(std::move(p)));
ShaderUniformType::None),
});
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0)); auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
@@ -82,12 +89,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 = shader.getProgram(); state.program = program;
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,6 @@
#include "Lifetimes.h" #include "Lifetimes.h"
#include "ShaderGenerator.h" #include "ShaderGenerator.h"
#include "Skip.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <utils/Hash.h> #include <utils/Hash.h>
@@ -79,8 +78,6 @@ void main() {
})"; })";
TEST_F(BackendTest, PushConstants) { TEST_F(BackendTest, PushConstants) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi(); auto& api = getDriverApi();
api.startCapture(0); api.startCapture(0);
+53 -43
View File
@@ -18,8 +18,7 @@
#include "BackendTestUtils.h" #include "BackendTestUtils.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <utils/Hash.h> #include <utils/Hash.h>
@@ -31,7 +30,6 @@ 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>
@@ -44,7 +42,20 @@ namespace {
// Shaders // Shaders
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
std::string fragmentFloat(R"(#version 450 core 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 fragmentFloat (R"(#version 450 core
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
@@ -54,7 +65,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;
@@ -97,7 +108,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);
} }
@@ -127,11 +138,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));
@@ -140,13 +151,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();
} }
@@ -229,20 +240,22 @@ TEST_F(ReadPixelsTest, ReadPixels) {
DriverApi& api = getDriverApi(); DriverApi& api = getDriverApi();
Cleanup cleanup(api); Cleanup cleanup(api);
std::string vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop, // Create programs.
ShaderUniformType::None); Handle<HwProgram> programFloat, programUint;
Shader floatShader(api, cleanup, ShaderConfig{ {
.vertexShader = vertexShader, ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
.fragmentShader = fragmentFloat, Program p = shaderGen.getProgram(api);
.uniforms = {} programFloat = cleanup.add(api.createProgram(std::move(p)));
}); }
Shader uintShader(api, cleanup, ShaderConfig{ {
.vertexShader = vertexShader, ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
.fragmentShader = fragmentUint, Program p = shaderGen.getProgram(api);
.uniforms = {} programUint = cleanup.add(api.createProgram(std::move(p)));
}); }
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) {
@@ -279,7 +292,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();
@@ -292,9 +305,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
api.beginRenderPass(renderTarget, params); api.beginRenderPass(renderTarget, params);
PipelineState state; PipelineState state;
state.program = floatShader.getProgram(); state.program = programFloat;
if (isUnsignedIntFormat(t.textureFormat)) { if (isUnsignedIntFormat(t.textureFormat)) {
state.program = uintShader.getProgram(); state.program = programUint;
} }
state.rasterState.colorWrite = true; state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false; state.rasterState.depthWrite = false;
@@ -312,7 +325,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();
} }
@@ -323,27 +336,27 @@ 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();
@@ -363,15 +376,13 @@ 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( auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.makeCurrent(swapChain, swapChain); api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{ // Create a program.
.mVertexType = VertexShaderType::Noop, ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
.mFragmentType = FragmentShaderType::White, Program p = shaderGen.getProgram(api);
.mUniformType = ShaderUniformType::None auto program = cleanup.add(api.createProgram(std::move(p)));
});
// 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;
@@ -400,7 +411,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;
@@ -409,7 +420,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 = shader.getProgram(); state.program = program;
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;
@@ -433,12 +444,11 @@ 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, api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
std::move(descriptor));
api.commit(swapChain); api.commit(swapChain);
api.endFrame(0); api.endFrame(0);
@@ -17,8 +17,7 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
@@ -29,19 +28,42 @@
#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();
@@ -51,10 +73,28 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto swapChain = cleanup.add(createSwapChain()); auto swapChain = cleanup.add(createSwapChain());
Shader shader = createShader(api, cleanup, sBackend); filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
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));
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add( // Create a program that samples a texture.
api.createDefaultRenderTarget(0)); Program p = shaderGen.getProgram(api);
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;
@@ -72,20 +112,18 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
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 = shader.getProgram(); state.program = program;
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() }; state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
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);
@@ -117,11 +155,29 @@ TEST_F(BackendTest, RenderExternalImage) {
auto swapChain = cleanup.add(createSwapChain()); auto swapChain = cleanup.add(createSwapChain());
Shader shader = createShader(api, cleanup, sBackend); filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api); 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));
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add( // Create a program that samples a texture.
api.createDefaultRenderTarget(0)); Program p = shaderGen.getProgram(api);
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
@@ -142,12 +198,10 @@ 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, CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**) keys, (const void**) values, 4, nullptr, nullptr);
(const void**)values, 4, nullptr, nullptr);
CVPixelBufferRef pixBuffer = nullptr; CVPixelBufferRef pixBuffer = nullptr;
CVReturn status = CVReturn status =
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options, CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options, &pixBuffer);
&pixBuffer);
assert(status == kCVReturnSuccess); assert(status == kCVReturnSuccess);
// Fill image with checker-pattern. // Fill image with checker-pattern.
@@ -156,7 +210,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++) {
@@ -176,13 +230,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 = shader.getProgram(); state.program = program;
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() }; state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
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;
@@ -193,6 +247,7 @@ 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.
+28 -14
View File
@@ -17,8 +17,7 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <utils/Hash.h> #include <utils/Hash.h>
@@ -28,6 +27,23 @@ 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();
@@ -69,11 +85,10 @@ 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);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{ // Create a program.
.mVertexType = VertexShaderType::Noop, ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
.mFragmentType = FragmentShaderType::White, Program p = shaderGen.getProgram(api);
.mUniformType = ShaderUniformType::None, ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
});
// 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,
@@ -118,7 +133,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
params.flags.discardEnd = TargetBufferFlags::NONE; params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {}; PipelineState ps = {};
ps.program = shader.getProgram(); ps.program = program;
ps.rasterState.colorWrite = true; ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false; ps.rasterState.depthWrite = false;
@@ -160,11 +175,10 @@ 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);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{ // Create a program.
.mVertexType = VertexShaderType::Noop, ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
.mFragmentType = FragmentShaderType::White, Program p = shaderGen.getProgram(api);
.mUniformType = ShaderUniformType::None, ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
});
// 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,
@@ -206,7 +220,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
params.flags.discardEnd = TargetBufferFlags::NONE; params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {}; PipelineState ps = {};
ps.program = shader.getProgram(); ps.program = program;
ps.rasterState.colorWrite = true; ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false; ps.rasterState.depthWrite = false;
+47 -22
View File
@@ -17,13 +17,43 @@
#include "BackendTest.h" #include "BackendTest.h"
#include "Lifetimes.h" #include "Lifetimes.h"
#include "Shader.h" #include "ShaderGenerator.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.
@@ -34,7 +64,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;
@@ -47,15 +77,13 @@ public:
mSwapChain = mCleanup.add(createSwapChain()); mSwapChain = mCleanup.add(createSwapChain());
api.makeCurrent(mSwapChain, mSwapChain); api.makeCurrent(mSwapChain, mSwapChain);
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{ // Create a program.
.mVertexType = VertexShaderType::Noop, ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
.mFragmentType = FragmentShaderType::White, Program p = shaderGen.getProgram(api);
.mUniformType = ShaderUniformType::None mProgram = mCleanup.add(api.createProgram(std::move(p)));
});
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.
@@ -72,7 +100,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;
@@ -125,7 +153,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);
@@ -143,11 +171,10 @@ 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, TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
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);
@@ -167,17 +194,15 @@ 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, TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
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, TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
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);
@@ -195,7 +220,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;
-8
View File
@@ -75,14 +75,6 @@ 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;
}; };
+2 -4
View File
@@ -269,14 +269,12 @@ 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());
} }
+1 -7
View File
@@ -161,13 +161,7 @@ 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) << "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
<< " 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();
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec| Pod::Spec.new do |spec|
spec.name = "Filament" spec.name = "Filament"
spec.version = "1.59.2" spec.version = "1.59.0"
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.2/filament-v1.59.2-ios.tgz" } spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.0/filament-v1.59.0-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 = {
+1 -3
View File
@@ -704,9 +704,7 @@ 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;
-6
View File
@@ -205,13 +205,7 @@ 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;
-109
View File
@@ -1,109 +0,0 @@
# Copyright (C) 2025 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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
-68
View File
@@ -1,68 +0,0 @@
# 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)
-28
View File
@@ -1,28 +0,0 @@
# Copyright (C) 2025 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#!/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
-15
View File
@@ -33,36 +33,22 @@ 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
@@ -72,7 +58,6 @@ 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 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "filament", "name": "filament",
"version": "1.59.2", "version": "1.59.0",
"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",