Compare commits

..
Author SHA1 Message Date
Andy Hovingh edfdc7fe44 vk: external sampler work 2025-04-08 15:30:56 -07:00
Powei Feng eb483501f0 vk: preparing for Ycbcr conversion (#8596)
- Move utils functions to the right files
 - Add conversion from vk formats to filament
 - Refactor how to enable multiview and external sampler in
   VulkanPlatform
 - Make VuklanDescriptorSet and VuklanDescriptorSetLayout more
   general to allow for mutability
 - Reorder vulkan files in CMakeLists.txt to be sorted in
   alphabetical order.
2025-04-08 14:57:21 -07:00
Powei Feng c54fe53936 vk: add YcbcrConversion to VulkanTexture (#8588)
- Refactor VulkanTextureState and VulkanTexture so that most
  state fields are consts.
- Add a field for VkYcbcrConversion.
- Clear the image view cache when YcbcrConversion changes
- Make sure that getAttachmentView and getView are better defined
  and called.
2025-04-08 14:57:12 -07:00
Powei Feng b915e2f35f vk: add ycbcr conversion cache (#8557)
Introduce a cache for Ycbcr conversion, which is needed for
external textures on Android.
2025-04-08 14:57:04 -07:00
Sungun Park c24e5089c4 Bump version to 1.59.1 2025-03-31 22:23:27 +00:00
9399 changed files with 2044169 additions and 354974 deletions
-34
View File
@@ -19,8 +19,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Run build script
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
@@ -46,8 +44,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
@@ -64,8 +60,6 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Run build script
run: |
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
- name: Run test
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
-3
View File
@@ -334,9 +334,6 @@ if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
endif()
endif()
if (FILAMENT_USE_EXTERNAL_GLES3)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.59.2'
implementation 'com.google.android.filament:filament-android:1.59.1'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.59.2'
pod 'Filament', '~> 1.59.1'
```
## 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
[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
@@ -142,12 +142,6 @@ abstract class MaterialCompiler extends TaskWithBinary {
if (!exclude_vulkan) {
matcArgs += ['-a', 'vulkan']
}
def include_webgpu = providers
.gradleProperty("com.google.android.filament.include-webgpu")
.forUseAtConfigurationTime().present
if (include_webgpu) {
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
}
def mat_no_opt = providers
.gradleProperty("com.google.android.filament.matnopt")
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.59.2
VERSION_NAME=1.59.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+2 -12
View File
@@ -194,6 +194,8 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanDriver.cpp
src/vulkan/VulkanDriver.h
src/vulkan/VulkanDriverFactory.h
src/vulkan/VulkanExternalImageManager.cpp
src/vulkan/VulkanExternalImageManager.h
src/vulkan/VulkanFboCache.cpp
src/vulkan/VulkanFboCache.h
src/vulkan/VulkanHandles.cpp
@@ -255,10 +257,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUConstants.h
src/webgpu/WebGPUDriver.cpp
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
)
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
@@ -308,11 +306,6 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
# add this subproject to the Filament folder
set_target_properties(${TARGET} PROPERTIES FOLDER Filament)
# we need to export the headers properly for backend.lib on windows
if (WIN32 AND FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE "WGPU_IMPLEMENTATION")
endif()
# ==================================================================================================
# Expose a header-only target to minimize dependencies.
# ==================================================================================================
@@ -507,10 +500,7 @@ if (APPLE OR LINUX)
test/Arguments.cpp
test/ImageExpectations.cpp
test/Lifetimes.cpp
test/PlatformRunner.cpp
test/Shader.cpp
test/SharedShaders.cpp
test/Skip.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp
@@ -55,9 +55,4 @@ public:
} // 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
@@ -104,6 +104,10 @@ public:
// Semaphore to be signaled once the image is available.
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();
@@ -294,6 +298,16 @@ public:
VkQueue getProtectedGraphicsQueue() const noexcept;
struct ExternalImageMetadata {
/**
* The Filament texture format.
*/
TextureFormat filamentFormat;
/**
* The Filament texture usage.
*/
TextureUsage filamentUsage;
/**
* The width of the external image
*/
@@ -304,11 +318,6 @@ public:
*/
uint32_t height;
/**
* The layerCount of the external image
*/
uint32_t layerCount;
/**
* The layer count of the external image
*/
@@ -324,11 +333,6 @@ public:
*/
VkFormat format;
/**
* An external buffer can be protected. This tells you if it is.
*/
bool isProtected;
/**
* The type of external format (opaque int) if used.
*/
@@ -348,20 +352,44 @@ public:
* Heap information
*/
uint32_t memoryTypeBits;
/**
* Ycbcr conversion components
*/
VkComponentMapping ycbcrConversionComponents;
/**
* Ycbcr model
*/
VkSamplerYcbcrModelConversion ycbcrModel;
/**
* Ycbcr range
*/
VkSamplerYcbcrRange ycbcrRange;
/**
* Ycbcr x chroma offset
*/
VkChromaLocation xChromaOffset;
/**
* Ycbcr y chroma offset
*/
VkChromaLocation yChromaOffset;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
// Note that the image metadata might change per-frame, hence we need a method for extracting
// it.
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
return {};
}
using ImageData = std::pair<VkImage, VkDeviceMemory>;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat);
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle);
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
return { VK_NULL_HANDLE, VK_NULL_HANDLE };
}
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
@@ -374,20 +402,6 @@ private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
static VkSampler createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma, SamplerParams sampler,
uint32_t internalFormat);
static VkImageView createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
// Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
@@ -26,7 +26,7 @@ namespace filament::backend {
class VulkanPlatformAndroid : public VulkanPlatform {
public:
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
@@ -39,31 +39,26 @@ public:
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
virtual ExternalImageMetadata extractExternalImageMetadata(
ExternalImageHandleRef image) const override;
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
protected:
~ExternalImageVulkanAndroid() override;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = VulkanPlatform::ImageData;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
};
}// namespace filament::backend
@@ -38,12 +38,6 @@ public:
[[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
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
// 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 inline std::string to_string(int i) noexcept { return std::to_string(i); }
static inline std::string to_string(float f) noexcept { return "float(" + std::to_string(f) + ")"; }
static void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
static void logCompilationError(utils::io::ostream& out,
ShaderStage shaderType, const char* name,
GLuint shaderId, CString const& sourceCode) noexcept;
static void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept;
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& 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 void logProgramLinkError(utils::io::ostream& out,
const char* name, GLuint program) noexcept;
static inline std::string to_string(bool b) noexcept {
return b ? "true" : "false";
}
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 ProgramData {
GLuint program{};
shaders_t shaders{};
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
};
~OpenGLProgramToken() override;
@@ -86,10 +90,10 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
ShaderCompilerService& compiler;
utils::CString const& name;
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;
struct {
shaders_t shaders{};
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
GLuint program = 0;
} gl; // 12 bytes
@@ -136,12 +140,11 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() = default;
/* static */ void ShaderCompilerService::setUserData(const program_token_t& token,
void* user) noexcept {
void ShaderCompilerService::setUserData(const program_token_t& token, void* user) noexcept {
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;
}
@@ -265,111 +268,101 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
token->handle = mCallbackManager.get();
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
switch (mMode) {
case Mode::THREAD_POOL: {
// queue a compile job
mCompilerThreadPool.queue(priorityQueue, token,
[this, &gl, program = std::move(program), token]() mutable {
// compile the shaders
shaders_t shaders{};
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
program.isMultiview(),
shaders,
token->shaderSourceCode);
if (mMode == Mode::THREAD_POOL) {
// queue a compile job
mCompilerThreadPool.queue(priorityQueue, token,
[this, &gl, program = std::move(program), token]() mutable {
// compile the shaders
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
program.isMultiview(),
shaders,
token->shaderSourceCode);
// link the program
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
// link the program
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
OpenGLProgramToken::ProgramData programData;
programData.shaders = shaders;
OpenGLProgramToken::ProgramData programData;
programData.shaders = shaders;
// We need to query the link status here to guarantee that the
// program is compiled and linked now (we don't want this to be
// deferred to later). We don't care about the result at this point.
GLint status = GL_FALSE;
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
programData.program = glProgram;
// We need to query the link status here to guarantee that the
// program is compiled and linked now (we don't want this to be
// deferred to later). We don't care about the result at this point.
GLint status = GL_FALSE;
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
programData.program = glProgram;
// we don't need to check for success here, it'll be done on the
// main thread side.
token->set(programData);
// we don't need to check for success here, it'll be done on the
// main thread side.
token->set(programData);
mCallbackManager.put(token->handle);
mCallbackManager.put(token->handle);
// caching must be the last thing we do
if (token->key && status == GL_TRUE) {
// Attempt to cache. This calls glGetProgramBinary.
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
}
});
break;
}
// caching must be the last thing we do
if (token->key && status == GL_TRUE) {
// Attempt to cache. This calls glGetProgramBinary.
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
}
});
case Mode::SYNCHRONOUS:
case Mode::ASYNCHRONOUS: {
// this cannot fail because we check compilation status after linking the program
// shaders[] is filled with id of shader stages present.
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
program.isMultiview(),
token->gl.shaders,
token->shaderSourceCode);
} else {
// this cannot fail because we check compilation status after linking the program
// shaders[] is filled with id of shader stages present.
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
program.isMultiview(),
token->gl.shaders,
token->shaderSourceCode);
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
assert_invariant(mMode != Mode::THREAD_POOL);
if (mMode == Mode::ASYNCHRONOUS) {
// don't attempt to link this program if all shaders are not done compiling
GLint status;
if (token->gl.program) {
glGetProgramiv(token->gl.program, GL_COMPLETION_STATUS, &status);
if (status == GL_FALSE) {
return false;
}
} else {
for (auto shader: token->gl.shaders) {
if (shader) {
glGetShaderiv(shader, GL_COMPLETION_STATUS, &status);
if (status == GL_FALSE) {
return false;
}
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
assert_invariant(mMode != Mode::THREAD_POOL);
if (mMode == Mode::ASYNCHRONOUS) {
// don't attempt to link this program if all shaders are not done compiling
GLint status;
if (token->gl.program) {
glGetProgramiv(token->gl.program, GL_COMPLETION_STATUS, &status);
if (status == GL_FALSE) {
return false;
}
} else {
for (auto shader: token->gl.shaders) {
if (shader) {
glGetShaderiv(shader, GL_COMPLETION_STATUS, &status);
if (status == GL_FALSE) {
return false;
}
}
}
}
}
if (!token->gl.program) {
// link the program, this also cannot fail because status is checked later.
token->gl.program = linkProgram(mDriver.getContext(),
token->gl.shaders, token->attributes);
if (mMode == Mode::ASYNCHRONOUS) {
// wait until the link finishes...
return false;
}
if (!token->gl.program) {
// link the program, this also cannot fail because status is checked later.
token->gl.program = linkProgram(mDriver.getContext(),
token->gl.shaders, token->attributes);
if (mMode == Mode::ASYNCHRONOUS) {
// wait until the link finishes...
return false;
}
}
assert_invariant(token->gl.program);
assert_invariant(token->gl.program);
mCallbackManager.put(token->handle);
mCallbackManager.put(token->handle);
if (token->key) {
// TODO: technically we don't have to cache right now. Is it advantageous to
// do this later, maybe depending on CPU usage?
// attempt to cache if we don't have a thread pool (otherwise it's done
// by the pool).
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
}
if (token->key) {
// TODO: technically we don't have to cache right now. Is it advantageous to
// do this later, maybe depending on CPU usage?
// attempt to cache if we don't have a thread pool (otherwise it's done
// by the pool).
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
}
return true;
});
break;
}
case Mode::UNDEFINED: {
assert_invariant(false);
}
return true;
});
}
return token;
@@ -384,12 +377,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
return program;
}
/* static */ void ShaderCompilerService::terminate(program_token_t& token) {
void ShaderCompilerService::terminate(program_token_t& token) {
assert_invariant(token);
token->canceled = true;
bool const isTickOpCanceled = token->compiler.cancelTickOp(token);
bool const canceled = token->compiler.cancelTickOp(token);
if (token->compiler.mMode == Mode::THREAD_POOL) {
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.
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.
token->compiler.mCallbackManager.put(token->handle);
}
@@ -439,8 +432,7 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
// ------------------------------------------------------------------------------------------------
/* static */ void ShaderCompilerService::getProgramFromCompilerPool(
program_token_t& token) noexcept {
void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) noexcept {
OpenGLProgramToken::ProgramData const& programData{ token->get() };
if (!token->canceled) {
token->gl.shaders = programData.shaders;
@@ -451,53 +443,40 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
SYSTRACE_CALL();
if (!token->gl.program) {
switch (mMode) {
case Mode::THREAD_POOL: {
// we need this program right now, remove it from the queue
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
// if we were able to remove it, we execute the job now, otherwise it means
// it's being executed right now.
job();
}
if (!token->canceled) {
token->compiler.cancelTickOp(token);
}
// Block until we get the program from the pool. Generally this wouldn't block
// because we just compiled the program above, when executing job.
ShaderCompilerService::getProgramFromCompilerPool(token);
break;
if (mMode == Mode::THREAD_POOL) {
// we need this program right now, remove it from the queue
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
// if we were able to remove it, we execute the job now, otherwise it means
// it's being executed right now.
job();
}
case Mode::ASYNCHRONOUS: {
// we force the program link -- which might stall, either here or below in
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
if (!token->canceled) {
token->compiler.cancelTickOp(token);
token->gl.program =
linkProgram(mDriver.getContext(), token->gl.shaders, token->attributes);
assert_invariant(token->gl.program);
mCallbackManager.put(token->handle);
if (token->key) {
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
}
break;
}
case Mode::SYNCHRONOUS: {
// if we don't have a program yet, block until we get it.
tick();
break;
}
// Block until we get the program from the pool. Generally this wouldn't block
// because we just compiled the program above, when executing job.
ShaderCompilerService::getProgramFromCompilerPool(token);
} else if (mMode == Mode::ASYNCHRONOUS) {
// we force the program link -- which might stall, either here or below in
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
token->compiler.cancelTickOp(token);
case Mode::UNDEFINED: {
assert_invariant(false);
token->gl.program = linkProgram(mDriver.getContext(),
token->gl.shaders, token->attributes);
assert_invariant(token->gl.program);
mCallbackManager.put(token->handle);
if (token->key) {
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
}
} else {
// if we don't have a program yet, block until we get it.
tick();
}
}
@@ -540,12 +519,12 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
* checked until after the program is linked.
* 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,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview,
shaders_t& outShaders,
UTILS_UNUSED_IN_RELEASE shaders_source_t& outShaderSourceCode) noexcept {
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
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
* are checked later. This always returns a valid GL program ID (which doesn't mean the
* program itself is valid).
*/
/* static */ GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
shaders_t const& shaders,
GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept {
SYSTRACE_CALL();
@@ -738,7 +913,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
* Checks a program link status and logs errors and frees resources on failure.
* 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();
@@ -773,24 +948,20 @@ void ShaderCompilerService::executeTickOps() noexcept {
return false;
}
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
/* static */ void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
GLuint shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
void logCompilationError(io::ostream& out, ShaderStage shaderType,
const char* name, GLuint shaderId,
UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
auto to_string = [](ShaderStage type) -> const char* {
switch (type) {
case ShaderStage::VERTEX:
return "vertex";
case ShaderStage::FRAGMENT:
return "fragment";
case ShaderStage::COMPUTE:
return "compute";
case ShaderStage::VERTEX: return "vertex";
case ShaderStage::FRAGMENT: return "fragment";
case ShaderStage::COMPUTE: return "compute";
}
};
{// scope for the temporary string storage
{ // scope for the temporary string storage
GLint length = 0;
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);
@@ -798,7 +969,8 @@ UTILS_NOINLINE
glGetShaderInfoLog(shaderId, length, nullptr, infoLog.data());
out << "Compilation error in " << to_string(shaderType) << " shader \"" << name << "\":\n"
<< "\"" << infoLog.c_str() << "\"" << io::endl;
<< "\"" << infoLog.c_str() << "\""
<< io::endl;
}
#ifndef NDEBUG
@@ -824,7 +996,7 @@ 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;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
@@ -832,204 +1004,9 @@ UTILS_NOINLINE
glGetProgramInfoLog(program, length, nullptr, infoLog.data());
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
@@ -57,8 +57,6 @@ class ShaderCompilerService {
public:
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);
@@ -136,9 +134,22 @@ private:
OpenGLContext& context,
Program::ShaderSource shadersSource,
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;
static bool checkProgramStatus(program_token_t const& token) noexcept;
-5
View File
@@ -15,7 +15,6 @@
*/
#include <backend/BufferDescriptor.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include <backend/DescriptorSetOffsetArray.h>
#include <backend/DriverEnums.h>
#include <backend/PipelineState.h>
@@ -438,10 +437,6 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
<< ", 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) {
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
return out << "PixelBufferDescriptor{ " << base
+102 -50
View File
@@ -200,9 +200,7 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept
: mPlatform(platform),
mResourceManager(
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck,
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
driverConfig.disableHeapHandleTags),
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
@@ -221,6 +219,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
@@ -313,7 +313,7 @@ void VulkanDriver::terminate() {
mCurrentSwapChain = {};
mDefaultRenderTarget = {};
mBoundPipeline = {};
mPipelineState = {};
mQueryManager.terminate();
@@ -325,9 +325,13 @@ void VulkanDriver::terminate() {
mCommands.terminate();
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
// descriptorSetLayoutCache
mExternalImageManager.terminate();
mStagePool.terminate();
mPipelineCache.terminate();
mFramebufferCache.terminate();
mFramebufferCache.reset();
mSamplerCache.terminate();
mDescriptorSetLayoutCache.terminate();
mDescriptorSetCache.terminate();
@@ -381,6 +385,10 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
// Do nothing.
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.onBeginFrame();
}
}
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
@@ -421,12 +429,16 @@ void VulkanDriver::updateDescriptorSetTexture(
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
// TODO: YcbcrConversion?
VulkanSamplerCache::Params cacheParams = {
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
if (mExternalImageManager.isExternallySampledTexture(texture)) {
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
mAppState.hasBoundExternalImages = true;
} else {
VulkanSamplerCache::Params cacheParams = {
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
}
}
void VulkanDriver::flush(int) {
@@ -563,41 +575,35 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE();
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
}
auto const& metadata = mPlatform->extractExternalImageMetadata(externalImage);
VkImageUsageFlags vkUsage = metadata.usage;
if (any(usage & TextureUsage::BLIT_SRC)) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
assert_invariant(width == metadata.width);
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t const memoryTypeIndex =
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
<< "failed to find a valid memory type for external image memory.";
// In theory the following are reasonable expectations, but in practice it's hard for client's
// to match up the dimensions of the texture with that of the AHB.
// assert_invariant(width == metadata.width);
// assert_invariant(height == metadata.height);
// assert_invariant(format == metadata.filamentFormat);
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkImage vkimg;
VkDeviceMemory deviceMemory;
std::tie(vkimg, deviceMemory) =
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
std::tie(vkimg, deviceMemory) = mPlatform->createVkImageFromExternal(externalImage);
VkSamplerYcbcrConversion conversion =
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
metadata.layerCount, usage, mStagePool);
metadata.format, conversion, metadata.samples, metadata.width, metadata.height,
metadata.layers, usage, mStagePool);
if (conversion != VK_NULL_HANDLE) {
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
}
// Unlike uploaded textures or swapchains, we need to explicit transition this
// texture into the read layout.
auto& commands = mCommands.get();
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
texture.inc();
}
@@ -630,6 +636,8 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
}
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
texture.dec();
mExternalImageManager.removeExternallySampledTexture(texture);
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
@@ -798,6 +806,11 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetCache.createSet(dsh, layout);
set.inc();
if (layout->hasExternalSamplers()) {
mAppState.hasExternalSamplerLayouts = true;
mExternalImageManager.addDescriptorSet(layout, set);
}
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
@@ -917,11 +930,19 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh) {
auto layout = resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
layout.dec();
if (layout->hasExternalSamplers()) {
mExternalImageManager.removeDescriptorSetLayout(layout);
}
}
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
set.dec();
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.removeDescriptorSet(set);
}
}
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
@@ -1482,7 +1503,6 @@ void VulkanDriver::endRenderPass(int) {
// pipeline barrier between framebuffer writes and shader reads.
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
mRenderPassFboInfo = {};
mCurrentRenderPass.renderTarget = {};
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
@@ -1523,7 +1543,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
swapChain->acquire(resized);
if (resized) {
mFramebufferCache.resetFramebuffers();
mFramebufferCache.reset();
}
if (UTILS_LIKELY(mDefaultRenderTarget)) {
@@ -1542,10 +1562,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
mBoundPipeline.pipelineLayout, stage, index, value);
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
mPipelineState.pipelineLayout, stage, index, value);
}
void VulkanDriver::insertEventMarker(char const* string) {
@@ -1724,6 +1744,27 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
// We need to determine whether to delay bindning until draw().
mPipelineState.bindInDraw.first = false;
if (mAppState.hasExternalSamplers()) {
auto& layouts = pipelineState.pipelineLayout.setLayout;
auto haveExternalSamplers = [&](auto hwHandle) {
if (!hwHandle) {
return false;
}
auto layout =
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, hwHandle);
return layout->hasExternalSamplers();
};
if (std::any_of(layouts.begin(), layouts.end(), haveExternalSamplers)) {
mPipelineState.bindInDraw = { true, pipelineState };
return;
}
}
bindPipelineImpl(pipelineState);
}
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState) {
FVK_SYSTRACE_SCOPE();
auto commands = mCurrentRenderPass.commandBuffer;
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
@@ -1792,10 +1833,11 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
mBoundPipeline = {
mPipelineState = {
.program = program,
.pipelineLayout = pipelineLayout,
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
.bindInDraw = {false, {}},
};
mPipelineCache.bindLayout(pipelineLayout);
@@ -1842,14 +1884,24 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
if (mAppState.hasExternalSamplers()) {
auto const& [bindInDraw, pipelineSt] = mPipelineState.bindInDraw;
bool const hasUpdated =
mExternalImageManager.prepareBindSets(mDescriptorSetCache.getBoundSets());
if (bindInDraw || hasUpdated) {
bindPipelineImpl(pipelineSt);
}
mPipelineState.bindInDraw.first = false;
}
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask);
mPipelineState.pipelineLayout,
mPipelineState.descriptorSetMask);
// Finally, make the actual draw call. TODO: support subranges
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
uint32_t const firstIndex = indexOffset;
constexpr int32_t vertexOffset = 0;
constexpr uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
}
+17 -7
View File
@@ -30,6 +30,7 @@
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanExternalImageManager.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
@@ -119,6 +120,7 @@ private:
private:
void collectGarbage();
void bindPipelineImpl(PipelineState const& pipelineState);
VulkanPlatform* mPlatform = nullptr;
fvkmemory::ResourceManager mResourceManager;
@@ -143,21 +145,29 @@ private:
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager;
// This is necessary for us to write to push constants after binding a pipeline.
struct {
// For push constant
resource_ptr<VulkanProgram> program;
// For push commiting dynamic ubos in draw()
VkPipelineLayout pipelineLayout;
fvkutils::DescriptorSetMask descriptorSetMask;
} mBoundPipeline = {};
// We need to store information about a render pass to enable better barriers at the end of a
// renderpass.
std::pair<bool, PipelineState> bindInDraw = {false, {}};
} mPipelineState = {};
struct {
using AttachmentArray =
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
AttachmentArray attachments;
} mRenderPassFboInfo = {};
// This tracks whether the app has seen external samplers bound to a the descriptor set.
// This will force bindPipeline to take a slow path.
bool hasExternalSamplerLayouts = false;
bool hasBoundExternalImages = false;
bool hasExternalSamplers() const noexcept {
return hasExternalSamplerLayouts && hasBoundExternalImages;
}
} mAppState;
bool const mIsSRGBSwapChainSupported;
backend::StereoscopicType const mStereoscopicType;
@@ -0,0 +1,280 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanExternalImageManager.h"
#include "VulkanDescriptorSetCache.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanSamplerCache.h"
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h"
#include <backend/platforms/VulkanPlatform.h>
#include <algorithm>
namespace filament::backend {
namespace {
template<typename T>
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
auto newEnd = std::remove_if(v.begin(), v.end(), f);
v.erase(newEnd, v.end());
}
} // anonymous
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
: mPlatform(platform),
mSamplerCache(samplerCache),
mYcbcrConversionCache(ycbcrConversionCache),
mDescriptorSetCache(setCache),
mDescriptorSetLayoutCache(layoutCache) {
}
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
void VulkanExternalImageManager::terminate() {
mSetAndLayouts.clear();
mSetBindings.clear();
mImages.clear();
}
void VulkanExternalImageManager::onBeginFrame() {
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
image.hasBeenValidated = false;
});
}
bool VulkanExternalImageManager::prepareBindSets(SetArray const& sets) {
bool hasUpdated = false;
for (auto set: sets) {
if (!set) {
continue;
}
if (auto itr = std::find_if(mSetAndLayouts.begin(), mSetAndLayouts.end(),
[&](auto const& setAndLayout) { return setAndLayout.first == set; });
itr != mSetAndLayouts.end()) {
hasUpdated = updateSetAndLayout(itr->first, itr->second) || hasUpdated;
}
}
return hasUpdated;
}
bool VulkanExternalImageManager::updateSetAndLayout(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto findImage = [&](fvkmemory::resource_ptr<VulkanTexture> texture) -> ImageData* {
auto itr = std::find_if(mImages.begin(), mImages.end(), [&](ImageData const& data) {
return data.ptr == texture;
});
assert_invariant(itr != mImages.end());
return &(*itr);
};
//std::vector<std::pair<uint8_t, ImageData*>> externalImages;
utils::FixedCapacityVector<std::pair<uint8_t, VkSampler>> samplerAndBindings;
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
bool hasImageUpdates = false;
for (auto& bindingInfo : mSetBindings) {
if (bindingInfo.set != set) {
continue;
}
auto imageData = findImage(bindingInfo.image);
hasImageUpdates = updateImage(imageData) || hasImageUpdates;
auto samplerParams = bindingInfo.samplerParams;
// according to spec, these must match chromaFilter
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
samplerParams.filterMag = SamplerMagFilter::NEAREST;
samplerParams.filterMin = SamplerMinFilter::NEAREST;
auto sampler = mSamplerCache->getSampler({
.sampler = samplerParams,
.conversion = imageData->conversion,
});
samplerAndBindings.push_back({ bindingInfo.binding, sampler });
}
// We need to sort by binding number
std::sort(samplerAndBindings.begin(), samplerAndBindings.end());
utils::FixedCapacityVector<VkSampler> outSamplers;
outSamplers.reserve(MAX_SAMPLER_COUNT);
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
[&](auto const& b) { outSamplers.push_back(b.second); });
VkDescriptorSetLayout const oldLayout = layout->getVkLayout();
VkDescriptorSetLayout const newLayout =
mDescriptorSetLayoutCache->getVkLayout(layout->bitmask, outSamplers);
bool const hasLayoutUpdate = oldLayout != newLayout;
layout->setVkLayout(newLayout);
assert_invariant(
(!hasImageUpdates && !hasLayoutUpdate) ||
(hasImageUpdates && hasLayoutUpdate));
if (!hasLayoutUpdate) {
return false;
}
auto foldBitsInHalf = [](auto bitset) {
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(bitset) * 4;
decltype(bitset) outBitset;
bitset.forEachSetBit([&](size_t index) { outBitset.set(index % BITMASK_LOWER_BITS_LEN); });
return outBitset;
};
// We need to build a new descriptor set from the new layout
VkDescriptorSet oldSet = set->getVkSet();
VkDescriptorSet newSet = mDescriptorSetCache->getVkSet(layout);
using Bitmask = fvkutils::UniformBufferBitmask;
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
auto const samplers = layout->bitmask.sampler & (~layout->bitmask.externalSampler);
// each bitmask denotes a binding index, and separated into two stages - vertex and buffer
// We fold the two stages into just the lower half of the bits to denote a combined set of
// bindings.
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
// TODO: fix the size for better memory
std::vector<VkCopyDescriptorSet> copies;
copyBindings.forEachSetBit([&](size_t index) {
copies.push_back({
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
.srcSet = oldSet,
.srcBinding = (uint32_t) index,
.dstSet = newSet,
.dstBinding = (uint32_t) index,
.descriptorCount = 1,
});
});
vkUpdateDescriptorSets(mPlatform->getDevice(), 0, nullptr, copies.size(), copies.data());
set->setVkSet(newSet);
// We need to release the vkset, which is no longer used, back into the pool.
mDescriptorSetCache->manualRecyle(layout->count, oldLayout, oldSet);
// We need to update the external samplers in the set
for (auto& bindingInfo: mSetBindings) {
if (bindingInfo.set != set) {
continue;
}
mDescriptorSetCache->updateSampler(set, bindingInfo.binding, bindingInfo.image,
VK_NULL_HANDLE);
}
return true;
}
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
VulkanPlatform::ExternalImageMetadata const& metadata) {
// This external image does not require external sampler (YUV conversion).
if (metadata.externalFormat == 0) {
return VK_NULL_HANDLE;
}
VulkanYcbcrConversionCache::Params ycbcrParams = {
.conversion = {
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
// Unclear where to get the chromaFilter, we just assume it's nearest.
.chromaFilter = SamplerMagFilter::NEAREST,
},
.format = metadata.filamentFormat,
.externalFormat = metadata.externalFormat,
};
return mYcbcrConversionCache->getConversion(ycbcrParams);
}
bool VulkanExternalImageManager::updateImage(ImageData* image) {
if (image->hasBeenValidated) {
return false;
}
image->hasBeenValidated = true;
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
if (vkYcbcr == image->conversion) {
return false;
}
image->ptr->setYcbcrConversion(vkYcbcr, metadata.externalFormat != 0);
image->conversion = vkYcbcr;
return true;
}
void VulkanExternalImageManager::addDescriptorSet(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
mSetAndLayouts.push_back({set, layout});
}
void VulkanExternalImageManager::removeDescriptorSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
erasep<SetAndLayout>(mSetAndLayouts,
[&](auto const& setLayout) { return (setLayout.first == inSet); });
erasep<SetBindingInfo>(mSetBindings,
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
}
void VulkanExternalImageManager::removeDescriptorSetLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> inLayout) {
erasep<SetAndLayout>(mSetAndLayouts,
[&](auto const& setLayout) { return (setLayout.second == inLayout); });
}
void VulkanExternalImageManager::bindExternallySampledTexture(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
// Should we do duplicate validation here?
mSetBindings.push_back({ bindingPoint, image, set, samplerParams });
}
void VulkanExternalImageManager::addExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image,
Platform::ExternalImageHandleRef platformHandleRef) {
mImages.push_back({ image, platformHandleRef, false });
}
void VulkanExternalImageManager::removeExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image) {
erasep<SetBindingInfo>(mSetBindings,
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
erasep<ImageData>(mImages, [&](auto const& imageData) { return imageData.ptr == image; });
}
bool VulkanExternalImageManager::isExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image) const {
return std::find_if(mImages.begin(), mImages.end(),
[&](auto const& imageData) { return imageData.ptr == image; }) != mImages.end();
}
} // namesapce filament::backend
@@ -0,0 +1,115 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
#include "VulkanHandles.h"
#include <backend/DriverEnums.h>
#include <array>
#include <vector>
namespace filament::backend {
class VulkanYcbcrConversionCache;
class VulkanSamplerCache;
class VulkanDescriptorSetLayoutCache;
class VulkanDescriptorSetCache;
// Manages the logic of external images and their quirks wrt Vulikan.
class VulkanExternalImageManager {
public:
VulkanExternalImageManager(
VulkanPlatform* platform,
VulkanSamplerCache* samplerCache,
VulkanYcbcrConversionCache* ycbcrConversionCache,
VulkanDescriptorSetCache* setCache,
VulkanDescriptorSetLayoutCache* layoutCache);
~VulkanExternalImageManager();
void terminate();
void onBeginFrame();
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
// This sets the currently bound layouts objects for the pipeline
bool prepareBindSets(SetArray const& layouts);
void addDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
fvkmemory::resource_ptr<VulkanDescriptorSet> set);
void removeDescriptorSetLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
SamplerParams samplerParams);
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image,
Platform::ExternalImageHandleRef platformHandleRef);
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
VulkanPlatform::ExternalImageMetadata const& metadata);
private:
struct ImageData {
fvkmemory::resource_ptr<VulkanTexture> ptr;
Platform::ExternalImageHandle platformHandle;
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
};
bool updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
bool updateImage(ImageData* imageData);
VulkanPlatform* mPlatform;
VulkanSamplerCache* mSamplerCache;
VulkanYcbcrConversionCache* mYcbcrConversionCache;
VulkanDescriptorSetCache* mDescriptorSetCache;
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
struct SetBindingInfo {
uint8_t binding = 0;
fvkmemory::resource_ptr<VulkanTexture> image;
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
SamplerParams samplerParams;
};
// Use vectors instead of hash maps because we only expect small number of entries.
std::vector<SetAndLayout> mSetAndLayouts;
std::vector<SetBindingInfo> mSetBindings;
std::vector<ImageData> mImages;
};
} // filament::backend
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
@@ -340,21 +340,15 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
return renderPass;
}
void VulkanFboCache::resetFramebuffers() noexcept {
for (const auto& pair: mFramebufferCache) {
void VulkanFboCache::reset() noexcept {
for (auto pair : mFramebufferCache) {
mRenderPassRefCount[pair.first.renderPass]--;
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
}
mFramebufferCache.clear();
}
void VulkanFboCache::terminate() noexcept {
resetFramebuffers();
for (const auto& pair: mRenderPassCache) {
for (auto pair : mRenderPassCache) {
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
}
mRenderPassRefCount.clear();
mRenderPassCache.clear();
}
+1 -4
View File
@@ -106,11 +106,8 @@ public:
// Evicts old unused Vulkan objects. Call this once per frame.
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.
void terminate() noexcept;
void reset() noexcept;
private:
VkDevice mDevice;
@@ -18,6 +18,7 @@
#include "VulkanConstants.h"
#include "vulkan/utils/Conversion.h"
#include "vulkan/vulkan_core.h"
#include <utils/Panic.h>
@@ -108,6 +108,11 @@ void VulkanSwapChain::present() {
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.
if (!mHeadless) {
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
@@ -141,6 +146,7 @@ void VulkanSwapChain::acquire(bool& resized) {
VulkanPlatform::ImageSyncData imageSyncData;
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex;
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
@@ -49,9 +49,7 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
void present();
// Acquire a new image from the swapchain. If the image is not available it would wait until it
// is.
void acquire(bool& resized);
void acquire(bool& reized);
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
uint32_t const imageIndex = mCurrentSwapIndex;
@@ -101,6 +99,7 @@ private:
VkExtent2D mExtent;
uint32_t mLayerCount;
uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired;
bool mIsFirstRenderPass;
};
@@ -132,6 +132,10 @@ public:
return id() == other.id() && type() == other.type();
}
inline bool operator!=(resource_ptr<D> const& other) const {
return !((*this) == other);
}
inline explicit operator bool() const {
return bool(mRef);
}
@@ -993,30 +993,6 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
return mImpl->mProtectedGraphicsQueue;
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
usage);
}
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat) {
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
}
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle) {
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}
@@ -15,14 +15,14 @@
*/
#include <backend/platforms/VulkanPlatformAndroid.h>
#include "vulkan/VulkanConstants.h"
#include "vulkan/VulkanContext.h"
#include "vulkan/vulkan_core.h"
#include <backend/DriverEnums.h>
#include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h"
#include <utils/Panic.h>
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Conversion.h"
#include <bluevk/BlueVK.h>
@@ -57,7 +57,7 @@ VkFormat transformVkFormat(VkFormat format, bool sRGB) {
}
bool isProtectedFromUsage(uint64_t usage) {
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
return usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
}
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
@@ -120,7 +120,9 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
usage = 0;
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
// We shouldn't be using external samplers as input attachments
// usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
if (isDepthFormat) {
@@ -136,87 +138,38 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
return { format, usage };
}
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage) {
VulkanPlatform::ImageData data;
// if external format we need to specifiy it in the allocation
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
const VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
// pass down the format (external means we don't have it VK defined)
.externalFormat = metadata.externalFormat,
std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardwareBuffer_Desc& desc,
bool sRGB) {
auto const format = mapToFilamentFormat(desc.format, sRGB);
return {
format,
mapToFilamentUsage(desc.usage, format),
};
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = useExternalFormat ? &externalFormat : nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imageInfo.pNext = &externalCreateInfo;
imageInfo.format = metadata.format;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent = {
metadata.width,
metadata.height,
1u,
};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = metadata.layers;
imageInfo.samples = metadata.samples;
imageInfo.usage = usage;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
// Allocate the memory
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.pNext = nullptr,
.buffer = buffer,
};
VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &androidHardwareBufferInfo,
.image = data.first,
.buffer = VK_NULL_HANDLE,
};
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex,
};
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
return data;
}
}// namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
if (__builtin_available(android 26, *)) {
if (aHardwareBuffer) {
AHardwareBuffer_release(aHardwareBuffer);
}
}
}
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
AHardwareBuffer const* buffer, bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
auto bufferImpl = const_cast<AHardwareBuffer*>(buffer);
AHardwareBuffer_acquire(bufferImpl);
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->format = textureFormat;
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return Platform::ExternalImageHandle{ p };
}
@@ -225,23 +178,20 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
auto metadata = extractExternalImageMetadata(externalImage);
return {
.width = fvkExternalImage->width,
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
.width = metadata.width,
.height = metadata.height,
.format = metadata.filamentFormat,
.usage = metadata.filamentUsage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImageMetadata(
ExternalImageHandleRef image) const {
auto const* fvkExternalImage = static_cast<ExternalImageVulkanAndroid const*>(image.get());
ExternalImageMetadata metadata;
ExternalImageMetadata metadata = {};
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc bufferDesc;
@@ -249,16 +199,30 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
metadata.width = bufferDesc.width;
metadata.height = bufferDesc.height;
metadata.layers = bufferDesc.layers;
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
std::tie(metadata.filamentFormat, metadata.filamentUsage) =
getFilamentFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
if (isProtectedFromUsage(bufferDesc.usage)) {
metadata.filamentUsage |= TextureUsage::PROTECTED;
}
// TODO: The following seems unnecessary. we should be able to discern directly from the
// bufferDesc.
if (any(metadata.filamentUsage & TextureUsage::BLIT_SRC)) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(metadata.filamentUsage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
}
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
.pNext = nullptr,
};
VkAndroidHardwareBufferPropertiesANDROID properties = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
@@ -268,138 +232,116 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
<< "mismatched image format( " << metadata.format << ") and queried format("
<< bufferPropertiesFormat << ") for external image (AHB)";
metadata.externalFormat = formatInfo.externalFormat;
// Choose either externalFormat > 0 or metadata.format and prefer the latter.
if (metadata.externalFormat > 0 && metadata.format != VK_FORMAT_UNDEFINED) {
// See VUID-VkImageCreateInfo-pNext-09457
metadata.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
metadata.externalFormat = 0;
}
metadata.allocationSize = properties.allocationSize;
metadata.memoryTypeBits = properties.memoryTypeBits;
metadata.ycbcrConversionComponents = formatInfo.samplerYcbcrConversionComponents;
metadata.ycbcrModel = formatInfo.suggestedYcbcrModel;
metadata.ycbcrRange = formatInfo.suggestedYcbcrRange;
metadata.xChromaOffset = formatInfo.suggestedXChromaOffset;
metadata.yChromaOffset = formatInfo.suggestedYChromaOffset;
return metadata;
}
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
ExternalImageHandleRef externalImage) const {
auto const& metadata = extractExternalImageMetadata(externalImage);
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
memoryTypeIndex, usage);
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return data;
}
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle){
VkExternalFormatANDROID externalFormat = {
// if external format we need to specifiy it in the allocation
bool const useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
VkExternalFormatANDROID const externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = conversion,
};
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.flags = 0,
.image = image,
.viewType = viewType,
.format = VK_FORMAT_UNDEFINED,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create VkImageView."
<< " error=" << static_cast<int32_t>(result);
return imageView;
}
VkSampler VulkanPlatform::createExternalSamplerImpl(
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
uint32_t internalFormat) {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
.externalFormat = metadata.externalFormat,
};
VkExternalMemoryImageCreateInfo const externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = useExternalFormat ? &externalFormat : nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.magFilter = fvkutils::getFilter(params.filterMag),
.minFilter = fvkutils::getFilter(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
.addressModeU = fvkutils::getWrapMode(params.wrapS),
.addressModeV = fvkutils::getWrapMode(params.wrapT),
.addressModeW = fvkutils::getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
VkImageCreateInfo const imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = &externalCreateInfo,
.flags = useExternalFormat ? VK_IMAGE_CREATE_ALIAS_BIT : 0u,
.imageType = VK_IMAGE_TYPE_2D,
.format = metadata.format,
.extent = {
metadata.width,
metadata.height,
1u,
},
.mipLevels = 1,
.arrayLayers = metadata.layers,
.samples = metadata.samples,
.usage = metadata.usage,
};
VkSampler sampler;
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
VkDevice const device = getDevice();
VkImage image;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
return sampler;
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
// Allocate the memory
VkImportAndroidHardwareBufferInfoANDROID const androidHardwareBufferInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.buffer = buffer,
};
VkMemoryDedicatedAllocateInfo const memoryDedicatedAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &androidHardwareBufferInfo,
.image = image,
.buffer = VK_NULL_HANDLE,
};
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(getPhysicalDevice(), &memoryProperties);
VkMemoryPropertyFlags const requiredMemoryFlags =
any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
metadata.memoryTypeBits, requiredMemoryFlags);
VkMemoryAllocateInfo const allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex,
};
VkDeviceMemory memory;
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &memory);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
result = vkBindImageMemory(getDevice(), image, memory, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return { image, memory };
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
@@ -427,20 +369,9 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
// Deprecated platform dependent helper methods
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return ExternalImageMetadata{};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return ImageData{};
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
return SurfaceBundle{};
}
}// namespace filament::backend
} // namespace filament::backend
@@ -24,12 +24,28 @@
#include <bluevk/BlueVK.h>
// Platform specific includes and defines
#include <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#if defined(__APPLE__)
#include <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#ifndef VK_MVK_macos_surface
#error VK_MVK_macos_surface is not defined
#ifndef VK_MVK_macos_surface
#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
using namespace bluevk;
@@ -38,52 +54,48 @@ namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
ExtensionSet const ret = {
#if defined(__APPLE__)
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;
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
NSView* nsview = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
#if defined(__APPLE__)
NSView* nsview = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
<< "Unable to load vkCreateMacOSSurfaceMVK.";
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pView = (__bridge void*) nsview;
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
<< "Unable to load vkCreateMacOSSurfaceMVK.";
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pView = (__bridge void*) nsview;
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
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)
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
return std::make_tuple(surface, VkExtent2D{});
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
#endif
return std::make_tuple(surface, VkExtent2D{});
}
} // namespace filament::backend
@@ -84,30 +84,6 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
+80 -205
View File
@@ -16,7 +16,6 @@
#include "webgpu/WebGPUDriver.h"
#include "WebGPUSwapChain.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>
@@ -36,7 +35,6 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
@@ -118,6 +116,7 @@ void printLimits(wgpu::Limits const& limits) {
printLimit("maxBufferSize", limits.maxBufferSize);
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
printLimit("maxInterStageShaderComponents", limits.maxInterStageShaderComponents);
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
printLimit("maxColorAttachments", limits.maxColorAttachments);
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
@@ -159,6 +158,8 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
FWGPU_LOGI << " compatibility mode: " << bool(adapterInfo.compatibilityMode)
<< utils::io::endl;
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
}
wgpu::SupportedFeatures supportedFeatures{};
@@ -174,17 +175,55 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
wgpu::SupportedLimits supportedLimits{};
if (!adapter.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
printLimits(supportedLimits);
printLimits(supportedLimits.limits);
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
std::stringstream usages_stream{};
usages_stream << capabilities.usages;
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
FWGPU_LOGI << " surface usages: " << usages_stream.str().data() << utils::io::endl;
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
[](wgpu::TextureFormat const format) {
std::stringstream format_stream{};
format_stream << format;
FWGPU_LOGI << " " << format_stream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
<< "):" << utils::io::endl;
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
std::for_each(capabilities.presentModes,
capabilities.presentModes + capabilities.presentModeCount,
[](wgpu::PresentMode const presentMode) {
std::stringstream present_mode_stream{};
present_mode_stream << presentMode;
FWGPU_LOGI << " " << present_mode_stream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
<< "):" << utils::io::endl;
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
std::for_each(capabilities.alphaModes,
capabilities.alphaModes + capabilities.alphaModeCount,
[](wgpu::CompositeAlphaMode const alphaMode) {
std::stringstream alpha_mode_stream{};
alpha_mode_stream << alphaMode;
FWGPU_LOGI << " " << alpha_mode_stream.str().data() << utils::io::endl;
});
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printDeviceDetails(wgpu::Device const& device) {
@@ -201,12 +240,12 @@ void printDeviceDetails(wgpu::Device const& device) {
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
wgpu::SupportedLimits supportedLimits{};
if (!device.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
printLimits(supportedLimits);
printLimits(supportedLimits.limits);
}
}
#endif
@@ -228,14 +267,6 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
driverConfig.disableHeapHandleTags) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
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
}
@@ -265,7 +296,6 @@ void WebGPUDriver::terminate() {
}
void WebGPUDriver::tick(int) {
mDevice.Tick();
}
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
@@ -295,27 +325,15 @@ void WebGPUDriver::finish(int) {
}
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (rph) {
destructHandle<WGPURenderPrimitive>(rph);
}
}
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (vbih) {
destructHandle<WGPUVertexBufferInfo>(vbih);
}
}
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (vbh) {
destructHandle<WGPUVertexBuffer>(vbh);
}
}
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
if (ibh) {
destructHandle<WGPUIndexBuffer>(ibh);
}
}
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
@@ -331,10 +349,6 @@ void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
if (sch) {
destructHandle<WebGPUSwapChain>(sch);
}
mSwapChain = nullptr;
}
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
@@ -344,16 +358,13 @@ void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
if (tqh) {
destructHandle<WebGPUDescriptorSetLayout>(tqh);
}
}
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
return allocHandle<WebGPUSwapChain>();
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
@@ -361,7 +372,7 @@ Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
@@ -389,15 +400,15 @@ Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
}
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
return allocHandle<WGPUBufferObject>();
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
return allocHandle<WGPURenderTarget>();
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
}
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
return allocHandle<WGPUVertexBuffer>();
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
}
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
@@ -405,11 +416,11 @@ Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
}
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
return allocHandle<WGPURenderPrimitive>();
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
}
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
return allocHandle<WGPUVertexBufferInfo>();
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
@@ -417,11 +428,12 @@ Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
return allocHandle<WGPURenderTarget>();
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
}
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
return allocHandle<WebGPUDescriptorSetLayout>();
return Handle<HwDescriptorSetLayout>(
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
@@ -437,15 +449,25 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
mNativeWindow = nativeWindow;
assert_invariant(!mSwapChain);
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
mSurface= mPlatform.createSurface(nativeWindow, flags);
mAdapter = mPlatform.requestAdapter(mSurface);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(mAdapter);
wgpu::SurfaceCapabilities surfaceCapabilities{};
if (!mSurface.GetCapabilities(mAdapter, &surfaceCapabilities)) {
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
} else {
printSurfaceCapabilitiesDetails(surfaceCapabilities);
}
#endif
mDevice = mPlatform.requestDevice(mAdapter);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
mQueue = mDevice.GetQueue();
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
mDevice, flags);
assert_invariant(mSwapChain);
// TODO configure the surface (maybe before or after creating the swapchain?
// how do we get the surface extent?)
// TODO actually create the swapchain
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
@@ -464,21 +486,13 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
constructHandle<WGPUVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
uint8_t attributeCount, AttributeArray attributes) {}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vbih);
constructHandle<WGPUVertexBuffer>(vbh, mDevice, vertexCount,vertexBufferInfo->bufferCount, vbih);
}
Handle<HwVertexBufferInfo> vbih) {}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
constructHandle<WGPUIndexBuffer>(ibh, mDevice, elementSize, indexCount);
}
uint32_t indexCount, BufferUsage usage) {}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {}
@@ -511,26 +525,11 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType
uint32_t depth, TextureUsage usage) {}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
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;
}
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
assert_invariant(mDefaultRenderTarget);
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
@@ -541,9 +540,7 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
constructHandle<WebGPUDescriptorSetLayout>(dslh, std::move(info), &mDevice);
}
backend::DescriptorSetLayout&& info) {}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {}
@@ -693,10 +690,6 @@ void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
Handle<HwBufferObject> boh) {
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
assert_invariant(index < vertexBuffer->buffers.size());
vertexBuffer->setBuffer(bufferObject, index);
}
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
@@ -729,72 +722,18 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
}
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "command_encoder"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
red -= 0.01;
} else {
red = 1.0f;
}
assert_invariant(mTextureView);
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
.view = mTextureView,
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
.depthSlice = wgpu::kDepthSliceUndefined,
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
.clearValue = wgpu::Color{red, 0 , 0 , 1},
};
wgpu::RenderPassDescriptor renderPassDescriptor = {
.colorAttachmentCount = 1,
.colorAttachments = &renderPassColorAttachment,
.depthStencilAttachment = nullptr,
.timestampWrites = nullptr,
};
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
}
void WebGPUDriver::endRenderPass(int) {
mRenderPassEncoder.End();
mRenderPassEncoder = nullptr;
wgpu::CommandBufferDescriptor commandBufferDescriptor {
.label = "command_buffer",
};
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
assert_invariant(mCommandBuffer);
}
void WebGPUDriver::nextSubpass(int) {
}
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) {
mCommandEncoder = nullptr;
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
mTextureView = nullptr;
assert_invariant(mSwapChain);
mSwapChain->present();
}
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -848,70 +787,6 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
}
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) {
+3 -26
View File
@@ -17,7 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include "WebGPUHandles.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
@@ -31,7 +30,6 @@
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <memory>
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
@@ -39,8 +37,6 @@
namespace filament::backend {
class WebGPUSwapChain;
/**
* WebGPU backend (driver) implementation
*/
@@ -59,20 +55,16 @@ private:
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform
WebGPUPlatform& mPlatform;
wgpu::Surface mSurface = nullptr;
wgpu::Adapter mAdapter = nullptr;
wgpu::Device mDevice = nullptr;
wgpu::Queue mQueue = nullptr;
void* mNativeWindow = nullptr;
WebGPUSwapChain* mSwapChain = nullptr;
uint64_t mNextFakeHandle = 1;
wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
/*
* Driver interface
*/
template<typename T>
friend class ConcreteDispatcher;
@@ -99,21 +91,6 @@ private:
return mHandleAllocator.allocate<D>();
}
template<typename D, typename B, typename ... ARGS>
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
}
template<typename D, typename B>
D* handleCast(Handle<B> handle) noexcept {
return mHandleAllocator.handle_cast<D*>(handle);
}
template<typename D, typename B>
void destructHandle(Handle<B>& handle) noexcept {
auto* p = mHandleAllocator.handle_cast<D*>(handle);
mHandleAllocator.deallocate(handle, p);
}
};
}// namespace filament::backend
@@ -1,267 +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 "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 {
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: 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),
vbih(vbih),
buffers(bufferCount) {
wgpu::BufferDescriptor descriptor {
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex,
.size = vextexCount * bufferCount,
.mappedAtCreation = false };
for (uint32_t i = 0; i < bufferCount; ++i) {
descriptor.label = ("vertex_buffer_" + std::to_string(i)).c_str();
buffers[i] = device.CreateBuffer(&descriptor);
}
}
// TODO: Empty function is a place holder for verxtex buffer updates and should be
// updated for that purpose.
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
: HwBufferObject(byteCount),
bufferObjectBinding(bindingType) {}
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
if (any(ShaderStageFlags::VERTEX & fFlags)) {
retStages |= wgpu::ShaderStage::Vertex;
}
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
retStages |= wgpu::ShaderStage::Fragment;
}
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
retStages |= wgpu::ShaderStage::Compute;
}
return retStages;
}
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
wgpu::Device const* device) {
assert_invariant(device->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
-176
View File
@@ -1,176 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/FixedCapacityVector.h>
// #include <utils/StructureOfArrays.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend {
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 {
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const {
return mVertexBufferLayout.data();
}
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 {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih);
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> buffers;
};
struct WGPUIndexBuffer : public HwIndexBuffer {
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
uint32_t indexCount);
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 {
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
wgpu::Buffer buffer;
const BufferObjectBinding bufferObjectBinding;
};
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout {
public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const* device);
~WebGPUDescriptorSetLayout();
private:
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
wgpu::BindGroupLayout mLayout;
};
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUTexture : public HwTexture {
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
// constructors for creating texture views
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
wgpu::Texture texture = nullptr;
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPURenderPrimitive();
void setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
WGPUVertexBuffer* vertexBuffer = nullptr;
WGPUIndexBuffer* indexBuffer = nullptr;
};
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPURenderTarget : public HwRenderTarget {
class Attachment {
public:
friend struct WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->texture),
mWGPUTexture(gpuTexture) {}
uint8_t level = 0;
uint16_t layer = 0;
private:
wgpu::Texture texture = nullptr;
WGPUTexture* mWGPUTexture = nullptr;
};
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
WGPURenderTarget()
: HwRenderTarget(0, 0),
defaultRenderTarget(true) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
const RenderPassParams& params);
math::uint2 getAttachmentSize() noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
private:
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
@@ -1,276 +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 "webgpu/WebGPUSwapChain.h"
#include "webgpu/WebGPUConstants.h"
#include "backend/DriverEnums.h"
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstdint>
#include <sstream>
namespace {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
std::stringstream usagesStream{};
usagesStream << capabilities.usages;
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
FWGPU_LOGI << " surface usages: " << usagesStream.str().data() << utils::io::endl;
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
[](wgpu::TextureFormat const format) {
std::stringstream formatStream{};
formatStream << format;
FWGPU_LOGI << " " << formatStream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
<< "):" << utils::io::endl;
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
std::for_each(capabilities.presentModes,
capabilities.presentModes + capabilities.presentModeCount,
[](wgpu::PresentMode const presentMode) {
std::stringstream presentModeStream{};
presentModeStream << presentMode;
FWGPU_LOGI << " " << presentModeStream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
<< "):" << utils::io::endl;
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
std::for_each(capabilities.alphaModes,
capabilities.alphaModes + capabilities.alphaModeCount,
[](wgpu::CompositeAlphaMode const alphaMode) {
std::stringstream alphaModeStream{};
alphaModeStream << alphaMode;
FWGPU_LOGI << " " << alphaModeStream.str().data() << utils::io::endl;
});
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config) {
std::stringstream formatStream{};
formatStream << config.format;
std::stringstream usageStream{};
usageStream << config.usage;
std::stringstream alphaModeStream{};
alphaModeStream << config.alphaMode;
std::stringstream presentModeStream{};
presentModeStream << config.presentMode;
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
[](wgpu::TextureFormat const viewFormat) {
std::stringstream viewFormatStream{};
viewFormatStream << viewFormat;
FWGPU_LOGI << " " << viewFormatStream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha mode: " << alphaModeStream.str() << utils::io::endl;
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
}
#endif
wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
const std::array expectedColorFormats =
useSRGBColorSpace ?
std::array{
wgpu::TextureFormat::RGBA8UnormSrgb,
wgpu::TextureFormat::BGRA8UnormSrgb }
: std::array{
wgpu::TextureFormat::RGBA8Unorm,
wgpu::TextureFormat::BGRA8Unorm };
auto const firstFoundColorFormat = std::find_first_of(expectedColorFormats.begin(),
expectedColorFormats.end(), availableFormats, availableFormats + availableFormatsCount);
FILAMENT_CHECK_POSTCONDITION(firstFoundColorFormat != expectedColorFormats.end())
<< "Cannot find a suitable WebGPU swapchain "
<< (useSRGBColorSpace ? "sRGB" : "non-standard (e.g. linear) RGB") << " color format";
return *firstFoundColorFormat;
}
wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
wgpu::PresentMode const* availablePresentModes) {
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
const wgpu::PresentMode desiredPresentMode = wgpu::PresentMode::Fifo;
FILAMENT_CHECK_POSTCONDITION(
std::any_of(availablePresentModes, availablePresentModes + availablePresentModesCount,
[](const wgpu::PresentMode availablePresentMode) {
return availablePresentMode == desiredPresentMode;
}))
<< "Cannot find a suitable WebGPU swapchain present mode";
return desiredPresentMode;
}
wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
wgpu::CompositeAlphaMode const* availableAlphaModes) {
bool autoAvailable = false;
bool inheritAvailable = false;
bool opaqueAvailable = false;
bool premultipliedAvailable = false;
bool unpremultipliedAvailable = false;
std::for_each(availableAlphaModes, availableAlphaModes + availableAlphaModesCount,
[&](const wgpu::CompositeAlphaMode alphMode) {
switch (alphMode) {
// in practice, the surface capabilities would not list Auto,
// but for completeness and defensive programming we can leverage it
// if it explicitly comes back as available/supported
case wgpu::CompositeAlphaMode::Auto:
autoAvailable = true;
break;
case wgpu::CompositeAlphaMode::Opaque:
opaqueAvailable = true;
break;
case wgpu::CompositeAlphaMode::Premultiplied:
premultipliedAvailable = true;
break;
case wgpu::CompositeAlphaMode::Unpremultiplied:
unpremultipliedAvailable = true;
break;
case wgpu::CompositeAlphaMode::Inherit:
inheritAvailable = true;
break;
}
});
if (autoAvailable || inheritAvailable || opaqueAvailable) {
return wgpu::CompositeAlphaMode::Auto;
} else if (premultipliedAvailable) {
// In practice, we do not expect this to possibly happen, as opaque should be supported,
// which we select first. However, if the underlying system actually does not support
// opaque we allow it to be tested, but warn that this may not likely work as they expect
// (untested territory).
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
"historically used these. Premultiplied alpha composite mode for "
"transparency is being selected as a fallback, but may not work as expected."
<< utils::io::endl;
return wgpu::CompositeAlphaMode::Premultiplied;
} else {
FILAMENT_CHECK_POSTCONDITION(unpremultipliedAvailable)
<< "No available composite alpha modes? Unknown/unhandled composite alpha mode?";
// Again, we don't expect this in practice, but allow if for the same reason as premultiplied.
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
"historically used these. Unpremultiplied alpha composite mode for "
"transparency is being selected as a fallback "
"(premulitipled is not available either), but may not work as expected."
<< utils::io::endl;
return wgpu::CompositeAlphaMode::Unpremultiplied;
}
}
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
wgpu::SurfaceCapabilities const& capabilities, wgpu::Extent2D const& surfaceSize,
bool useSRGBColorSpace) {
config.device = device;
config.usage = wgpu::TextureUsage::RenderAttachment;
config.width = surfaceSize.width;
config.height = surfaceSize.height;
config.format =
selectColorFormat(capabilities.formatCount, capabilities.formats, useSRGBColorSpace);
config.presentMode =
selectPresentMode(capabilities.presentModeCount, capabilities.presentModes);
config.alphaMode = selectAlphaMode(capabilities.alphaModeCount, capabilities.alphaModes);
}
}// namespace
namespace filament::backend {
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags)
: mSurface(surface) {
wgpu::SurfaceCapabilities capabilities = {};
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
} else {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceCapabilitiesDetails(capabilities);
#endif
}
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
mSurface.Configure(&mConfig);
}
WebGPUSwapChain::~WebGPUSwapChain() {
mSurface.Unconfigure();
}
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
<< "WebGPUSwapChain::setExtent: Invalid width " << currentSurfaceSize.width
<< " and/or height " << currentSurfaceSize.height << " requested.";
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) {
mConfig.width = currentSurfaceSize.width;
mConfig.height = currentSurfaceSize.height;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig);
#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);
}
}
wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
wgpu::Extent2D const& currentSurfaceSize) {
setExtent(currentSurfaceSize);
wgpu::SurfaceTexture surfaceTexture;
mSurface.GetCurrentTexture(&surfaceTexture);
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
return nullptr;
}
// Create a view for this surface texture
// TODO: review these initiliazations as webgpu pipeline gets mature
wgpu::TextureViewDescriptor textureViewDescriptor = {
.label = "texture_view",
.format = surfaceTexture.texture.GetFormat(),
.dimension = wgpu::TextureViewDimension::e2D,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.arrayLayerCount = 1
};
return surfaceTexture.texture.CreateView(&textureViewDescriptor);
}
void WebGPUSwapChain::present() {
assert_invariant(mSurface);
mSurface.Present();
}
}// namespace filament::backend
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
#define TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
#include <webgpu/webgpu_cpp.h>
#include "DriverBase.h"
#include <backend/Platform.h>
#include <cstdint>
namespace filament::backend {
class WebGPUSwapChain final : public Platform::SwapChain, HwSwapChain {
public:
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags);
~WebGPUSwapChain();
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
void present();
private:
void setExtent(wgpu::Extent2D const&);
wgpu::Surface mSurface = {};
wgpu::SurfaceConfiguration mConfig = {};
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
@@ -18,7 +18,6 @@
#include <utils/Panic.h>
#include <android/native_window.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
@@ -29,14 +28,6 @@
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::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
surfaceSourceAndroidWindow.window = nativeWindow;
@@ -24,8 +24,21 @@
#include <cstdint>
// Platform specific includes and defines
#include <Cocoa/Cocoa.h>
#import <QuartzCore/CAMetalLayer.h>
#if defined(__APPLE__)
#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
@@ -33,19 +46,14 @@
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 surface = nullptr;
// Both IOS and MacOS expects CAMetalLayer.
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
#if defined(__APPLE__)
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{};
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
wgpu::SurfaceDescriptor surfaceDescriptor = {
@@ -54,6 +62,19 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
};
surface = mInstance.CreateSurface(&surfaceDescriptor);
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;
}
@@ -79,74 +79,6 @@
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 surface = nullptr;
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
@@ -22,24 +22,12 @@
#include <cstdint>
#include <Windows.h>
/**
* Windows OS specific implementation aspects of the WebGPU 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*/) {
// TODO verify this is necessary for Dawn implementation as well:
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
@@ -53,12 +41,12 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
surfaceSourceWin.hwnd = nativeWindow;
const wgpu::SurfaceDescriptor surfaceDescriptor{
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceWin,
.label = "windows_surface"
};
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface.Get() != nullptr) << "Unable to create Windows-backed surface.";
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Windows-backed surface.";
return surface;
}
+3 -17
View File
@@ -43,12 +43,10 @@ using namespace image;
namespace test {
Backend BackendTest::sBackend = Backend::NOOP;
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
bool BackendTest::sIsMobilePlatform = false;
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
void BackendTest::init(Backend backend, bool isMobilePlatform) {
sBackend = backend;
sOperatingSystem = operatingSystem;
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));
}
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 {
public:
virtual void SetUp() override {
@@ -224,8 +210,8 @@ public:
}
};
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) {
BackendTest::init(backend, operatingSystem, isMobile);
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]) {
BackendTest::init(backend, isMobile);
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new Environment);
}
+1 -6
View File
@@ -31,10 +31,9 @@ namespace test {
class BackendTest : public ::testing::Test {
public:
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform);
static void init(Backend backend, bool isMobilePlatform);
static Backend sBackend;
static OperatingSystem sOperatingSystem;
static bool sIsMobilePlatform;
protected:
@@ -72,10 +71,6 @@ protected:
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; }
static bool matchesEnvironment(Backend backend);
static bool matchesEnvironment(OperatingSystem operatingSystem);
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
private:
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 <stddef.h>
#include "utils/CString.h"
namespace test {
@@ -35,15 +34,6 @@ enum class Backend : uint8_t {
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 {
void* ptr = nullptr;
size_t width = 0, height = 0;
@@ -61,10 +51,9 @@ NativeView getNativeView();
* No tests will be run yet.
*
* @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).
*/
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.
@@ -79,6 +68,6 @@ int runTests();
*/
Backend parseArgumentsForBackend(int argc, char* argv[]);
} // namespace test
}
#endif
+15 -25
View File
@@ -23,44 +23,30 @@ namespace test {
using namespace filament::backend;
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup(cleanup) {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniforms.size());
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
kLayouts[i] = {
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER),
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
}
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniformNames.size());
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
kLayouts[i] =
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
};
// This assumes that the uniforms will all be in a single descriptor set at index 1.
// If there are shaders with uniforms in other sets then ShaderConfig will need to be expanded
// to accommodate that.
size_t kDescriptorSetIndex = 1;
filamat::DescriptorSets descriptors;
descriptors[kDescriptorSetIndex] = filamat::DescriptorSetInfo(config.uniforms.size());
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
descriptors[kDescriptorSetIndex][i] = {
config.uniforms[i].name, kLayouts[i], config.uniforms[i].samplerInfo };
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[i], {}}};
}
ShaderGenerator shaderGen(
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
BackendTest::sIsMobilePlatform, std::move(descriptors));
Program prog = shaderGen.getProgram(api);
Program::DescriptorBindingsInfo bindingsInfo(config.uniforms.size());
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
bindingsInfo[i] = {
config.uniforms[i].name,
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER), i };
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
}
prog.descriptorBindings(1, bindingsInfo);
mProgram = cleanup.add(api.createProgram(std::move(prog)));
mDescriptorSetLayout = cleanup.add(
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
}
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
}
filament::backend::ProgramHandle Shader::getProgram() const {
@@ -71,4 +57,8 @@ filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() co
return mDescriptorSetLayout;
}
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
return mDescriptorSet;
}
} // namespace test
+6 -26
View File
@@ -18,22 +18,14 @@
#define TNT_SHADER_H
#include "Lifetimes.h"
#include "private/filament/SamplerInterfaceBlock.h"
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.
struct ShaderConfig {
std::string vertexShader;
std::string fragmentShader;
std::vector<UniformConfig> uniforms;
std::vector<utils::CString> uniformNames;
};
// All values describing a uniform.
@@ -43,7 +35,6 @@ struct ResolvedUniformBindingConfig {
uint32_t byteOffset;
filament::backend::descriptor_set_t set;
filament::backend::descriptor_binding_t binding;
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
};
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
@@ -55,7 +46,6 @@ struct UniformBindingConfig {
std::optional<uint32_t> byteOffset;
std::optional<filament::backend::descriptor_set_t> set;
std::optional<filament::backend::descriptor_binding_t> binding;
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
template<typename UniformType>
ResolvedUniformBindingConfig resolve();
@@ -89,14 +79,12 @@ public:
filament::backend::ProgramHandle getProgram() const;
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
filament::backend::DescriptorSetHandle createDescriptorSet(
filament::backend::DriverApi& api) const;
filament::backend::DescriptorSetHandle getDescriptorSet() const;
protected:
Cleanup& mCleanup;
filament::backend::ProgramHandle mProgram;
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
filament::backend::DescriptorSetHandle mDescriptorSet;
};
template<typename UniformType>
@@ -107,8 +95,7 @@ ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
.bufferSize = bufferSize.value_or(resolvedDataSize),
.byteOffset = byteOffset.value_or(0),
.set = set.value_or(1),
.binding = binding.value_or(0),
.descriptorSet = descriptorSet
.binding = binding.value_or(0)
};
}
@@ -133,16 +120,9 @@ void Shader::bindUniform(filament::backend::DriverApi& api,
UniformBindingConfig config) const {
auto resolvedConfig = config.resolve<UniformType>();
filament::backend::DescriptorSetHandle descriptorSet;
if (resolvedConfig.descriptorSet.has_value()) {
descriptorSet = *resolvedConfig.descriptorSet;
} else {
descriptorSet = createDescriptorSet(api);
}
api.updateDescriptorSetBuffer(descriptorSet, resolvedConfig.binding, hwBuffer, 0,
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
resolvedConfig.bufferSize);
api.bindDescriptorSet(descriptorSet, resolvedConfig.set, {});
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
}
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;
}
test::initTests(backend, test::OperatingSystem::LINUX, false, argc, argv);
test::initTests(backend, false, argc, argv);
return test::runTests();
}
+1 -1
View File
@@ -98,7 +98,7 @@ test::NativeView getNativeView() {
int main(int argc, char* 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];
delegate.backend = backend;
NSApplication* app = [NSApplication sharedApplication];
+40 -12
View File
@@ -19,7 +19,6 @@
#include "ImageExpectations.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -41,14 +40,48 @@ using namespace filament::backend;
using namespace filament::math;
using namespace utils;
struct MaterialParams {
float4 color;
float4 scale;
};
class BlitTest : public BackendTest {
public:
BlitTest() : mCleanup(getDriverApi()) {}
protected:
Shader createShader();
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) {
color = saturate(color);
uint32_t r = color.r * 255.0f;
@@ -280,11 +313,7 @@ TEST_F(BlitTest, ColorResolve) {
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
constexpr int kSampleCount = 4;
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{
.mVertexType = VertexShaderType::Simple,
.mFragmentType = FragmentShaderType::SolidColored,
.mUniformType = ShaderUniformType::Simple,
});
Shader shader = createShader();
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
TrianglePrimitive const triangle(api);
@@ -327,15 +356,14 @@ TEST_F(BlitTest, ColorResolve) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
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));
// Draw red triangle into srcRenderTarget.
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{
.color = float4(1, 0, 0, 1),
.scaleMinusOne = float4(0, 0, -0.5, 0),
.offset = float4(0.5, 0.5, 0, 0),
shader.uploadUniform(api, ubuffer, MaterialParams{
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
});
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
shader.bindUniform<MaterialParams>(api, ubuffer);
// FIXME: on Metal this triangle is not drawn. Can't understand why.
{
+83 -35
View File
@@ -18,18 +18,72 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.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 {
using namespace filament;
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.
const UniformBindingConfig kBindingConfig = {
.dataSize = sizeof(SimpleMaterialParams),
.bufferSize = sizeof(SimpleMaterialParams) + 64,
.dataSize = sizeof(MaterialParams),
.bufferSize = sizeof(MaterialParams) + 64,
.byteOffset = 64
};
@@ -39,10 +93,8 @@ public:
protected:
Shader createShader() {
return SharedShaders::makeShader(getDriverApi(), mCleanup, ShaderRequest{
.mVertexType = VertexShaderType::Simple,
.mFragmentType = FragmentShaderType::SolidColored,
.mUniformType = ShaderUniformType::SimpleWithPadding
return Shader(getDriverApi(), mCleanup, ShaderConfig{
vertex, fragment, {"Params"}
});
}
@@ -77,7 +129,7 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
RenderPassParams params = {};
fullViewport(params);
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.discardEnd = TargetBufferFlags::NONE;
@@ -92,18 +144,17 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// Create a uniform buffer.
// 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.
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams) + 64,
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
api.startCapture(0);
// Upload the uniform, but with an offset to accommodate the padding in the shader's
// uniform definition.
shader.uploadUniform(api, ubuffer, kBindingConfig, SimpleMaterialParams{
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
.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 }
});
@@ -114,21 +165,19 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
size_t triangleIndex = 0;
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
const float low = i, high = i + 0.2;
const filament::math::float2 v[3]{{ low, low },
{ high, low },
{ low, high }};
const filament::math::float2 v[3] {{low, low}, {high, low}, {low, high}};
triangle.updateVertices(v);
if (updateIndices) {
if (triangleIndex % 2 == 0) {
// 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 + 1, 1, 1);
triangle.updateIndices(i + 2, 1, 2);
} else {
// 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);
}
}
@@ -171,29 +220,28 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
// Create a uniform buffer.
// 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.
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams) + 64,
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
// Create a render target.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
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 the uniform, but with an offset to accommodate the padding in the shader's
// uniform definition.
shader.uploadUniform(api, ubuffer, kBindingConfig, SimpleMaterialParams{
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
.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 }
});
RenderPassParams params = {};
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.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
@@ -202,18 +250,18 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
renderTarget, swapChain, shader.getProgram(), params);
// 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.
const UniformBindingConfig partialBindingConfig = {
.dataSize = sizeof(float) * 8,
.bufferSize = sizeof(SimpleMaterialParams) + 64,
.byteOffset = 64 + offsetof(SimpleMaterialParams, color.b)
};
shader.uploadUniform(api, ubuffer, partialBindingConfig,
std::array<float, 8>{
1.0f, 1.0f, // color.b, color.a
0.0f, 0.0f, 0.0f, 0.0f, // scale
0.5f, 0.5f // offset.x, offset.y
});
// color.b, color.a, offset.x, and offset.y.
shader.uploadUniform(api, ubuffer, UniformBindingConfig{
.dataSize = sizeof(std::array<float, 4>),
.bufferSize = kBindingConfig.bufferSize,
.byteOffset = *kBindingConfig.byteOffset + offsetof(MaterialParams, color.b),
},
std::array<float, 4>{
// color.b, color.a
1.0f, 1.0f,
// offset.x, offset.y
0.5f, 0.5f }
);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
-5
View File
@@ -17,7 +17,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Skip.h"
using namespace filament;
using namespace filament::backend;
@@ -25,8 +24,6 @@ using namespace filament::backend;
namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -84,8 +81,6 @@ TEST_F(BackendTest, FrameScheduledCallback) {
}
TEST_F(BackendTest, FrameCompletedCallback) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
Cleanup cleanup(api);
+55 -28
View File
@@ -17,7 +17,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <backend/DriverEnums.h>
@@ -96,6 +96,16 @@ struct MaterialParams {
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) {
const size_t size = kTexWidth * kTexHeight * 4;
void* buffer = calloc(1, size);
@@ -130,13 +140,38 @@ TEST_F(BackendTest, FeedbackLoops) {
api.makeCurrent(swapChain, swapChain);
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
ProgramHandle program;
{
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
Shader shader = Shader(api, cleanup, ShaderConfig {
.vertexShader = fullscreenVs,
.fragmentShader = fullscreenFs,
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}, {"Params"}}
});
filamat::DescriptorSets descriptors;
descriptors[1] = {
{ "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
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());
@@ -183,8 +218,8 @@ TEST_F(BackendTest, FeedbackLoops) {
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
@@ -198,24 +233,20 @@ TEST_F(BackendTest, FeedbackLoops) {
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
auto descriptorSet = shader.createDescriptorSet(api);
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
});
api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams));
api.bindDescriptorSet(descriptorSet, 1, {});
UniformBindingConfig uniformBinding{
.binding = 1,
.descriptorSet = descriptorSet
};
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
uploadUniforms(getDriverApi(), ubuffer, {
.fbWidth = float(params.viewport.width),
.fbHeight = float(params.viewport.height),
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
@@ -231,24 +262,20 @@ TEST_F(BackendTest, FeedbackLoops) {
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
auto descriptorSet = shader.createDescriptorSet(api);
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
});
api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams));
api.bindDescriptorSet(descriptorSet, 1, {});
UniformBindingConfig uniformBinding{
.binding = 1,
.descriptorSet = descriptorSet
};
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
.fbWidth = float(params.viewport.width),
.fbHeight = float(params.viewport.height),
.sourceLevel = float(sourceLevel),
uploadUniforms(getDriverApi(), ubuffer, {
.fbWidth = float(params.viewport.width),
.fbHeight = float(params.viewport.height),
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
+133 -61
View File
@@ -18,14 +18,16 @@
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "private/filament/SamplerInterfaceBlock.h"
#include <math/half.h>
#include <vector>
#include <stddef.h>
@@ -40,6 +42,19 @@ 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 *= -1.0f;
#endif
}
)");
std::string fragmentTemplate (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
@@ -104,15 +119,6 @@ namespace test {
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,
@@ -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.
static const uint32_t expectedHash = 3644679986;
@@ -300,14 +306,28 @@ TEST_F(LoadImageTest, UpdateImage2D) {
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
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}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
Shader shader(api, cleanup, ShaderConfig{
.vertexShader = mVertexShader,
.fragmentShader= fragment,
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
});
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
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.
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
@@ -329,15 +349,14 @@ TEST_F(LoadImageTest, UpdateImage2D) {
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
}
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
.filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, program);
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
@@ -351,9 +370,8 @@ TEST_F(LoadImageTest, UpdateImage2D) {
flushAndWait();
}
TEST_F(LoadImageTest, UpdateImageSRGB) {
TEST_F(BackendTest, UpdateImageSRGB) {
auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture();
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
@@ -361,23 +379,36 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
TextureFormat const textureFormat = TextureFormat::SRGB8_A8;
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
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}",
getSamplerTypeName(textureFormat), fragmentTemplate);
Shader shader(api, cleanup, ShaderConfig{
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
"text_tex", DescriptorType::SAMPLER, samplerInfo
}}});
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
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.
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
// Create image data.
size_t components; int bpp;
@@ -405,7 +436,6 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
api.beginFrame(0, 0, 0);
// Update samplers.
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -413,8 +443,8 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 359858623;
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
@@ -423,14 +453,21 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
api.commit(swapChain);
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.
api.finish();
api.stopCapture();
flushAndWait();
}
TEST_F(LoadImageTest, UpdateImageMipLevel) {
TEST_F(BackendTest, UpdateImageMipLevel) {
auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture();
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
@@ -438,27 +475,38 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
TextureFormat textureFormat = TextureFormat::RGBA32F;
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
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}",
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
Shader shader(api, cleanup, ShaderConfig {
.vertexShader = mVertexShader,
.fragmentShader = fragment,
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
});
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
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 with 3 mip levels.
// Base level: 1024
// Level 1: 512 <-- upload data and sample from this level
// Level 2: 256
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 3,
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, 3,
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
// Create image data.
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
@@ -467,7 +515,6 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
api.beginFrame(0, 0, 0);
// Update samplers.
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -475,8 +522,8 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 3644679986;
readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash);
@@ -485,14 +532,21 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
api.commit(swapChain);
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.
api.finish();
api.stopCapture();
flushAndWait();
}
TEST_F(LoadImageTest, UpdateImage3D) {
TEST_F(BackendTest, UpdateImage3D) {
auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture();
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
@@ -502,24 +556,35 @@ TEST_F(LoadImageTest, UpdateImage3D) {
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
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}",
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
Shader shader(api, cleanup, ShaderConfig {
.vertexShader = mVertexShader,
.fragmentShader = fragment,
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
});
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
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.
Handle<HwTexture> texture = cleanup.add(api.createTexture(samplerType, 1,
textureFormat, 1, 512, 512, 4, usage));
Handle<HwTexture> texture = api.createTexture(samplerType, 1,
textureFormat, 1, 512, 512, 4, usage);
// Create image data for all 4 layers.
size_t components; int bpp;
@@ -541,7 +606,6 @@ TEST_F(LoadImageTest, UpdateImage3D) {
api.beginFrame(0, 0, 0);
// Update samplers.
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -549,8 +613,8 @@ TEST_F(LoadImageTest, UpdateImage3D) {
api.bindDescriptorSet(descriptorSet, 1, {});
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
defaultRenderTarget, swapChain, program);
static const uint32_t expectedHash = 3644679986;
readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash);
@@ -559,9 +623,17 @@ TEST_F(LoadImageTest, UpdateImage3D) {
api.commit(swapChain);
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.
api.finish();
api.stopCapture();
flushAndWait();
}
} // namespace test
+21 -9
View File
@@ -17,8 +17,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
namespace {
@@ -27,6 +26,21 @@ namespace {
// 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
layout(location = 0) out vec4 fragColor;
@@ -57,12 +71,10 @@ TEST_F(BackendTest, MRT) {
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Shader shader(api, cleanup, ShaderConfig{
.vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
ShaderUniformType::None),
.fragmentShader = fragment,
.uniforms = {}
});
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
TrianglePrimitive triangle(api);
@@ -110,7 +122,7 @@ TEST_F(BackendTest, MRT) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = shader.getProgram();
state.program = program;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
+67 -35
View File
@@ -19,8 +19,7 @@
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <backend/DriverEnums.h>
@@ -35,7 +34,18 @@ namespace {
// 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) 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 {
@@ -66,25 +88,39 @@ TEST_F(BackendTest, TextureViewLod) {
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Shader whiteShader = SharedShaders::makeShader(api, cleanup, ShaderRequest {
.mVertexType = VertexShaderType::Textured,
.mFragmentType = FragmentShaderType::White,
.mUniformType = ShaderUniformType::Sampler
});
// Create a program that draws only white.
Handle<HwProgram> whiteProgram;
{
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.
std::string vertexShader = SharedShaders::getVertexShaderText(
VertexShaderType::Textured, ShaderUniformType::Sampler);
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo {
"backend_test", "sib_tex", 0,
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
Shader texturedShader(api, cleanup, ShaderConfig {
.vertexShader = vertexShader,
.fragmentShader = fragmentTexturedLod,
.uniforms = {{
"backend_test_sib_tex", DescriptorType::SAMPLER, samplerInfo
}}
});
Handle<HwProgram> textureProgram;
{
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "backend_test", "sib_tex", 0,
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
filamat::DescriptorSets descriptors;
descriptors[1] = { { "backend_test_sib_tex",
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, samplerInfo } };
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
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.
// Level 0: 128x128 (red)
@@ -93,10 +129,9 @@ TEST_F(BackendTest, TextureViewLod) {
// Level 3: 16x16 (yellow)
const size_t kTextureSize = 128;
const size_t kMipLevels = 4;
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
kMipLevels, TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT
| TextureUsage::UPLOADABLE));
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
// Create image data.
auto pixelFormat = PixelDataFormat::RGBA;
@@ -144,7 +179,7 @@ TEST_F(BackendTest, TextureViewLod) {
params.flags.discardStart = TargetBufferFlags::NONE;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = whiteShader.getProgram();
ps.program = whiteProgram;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
api.beginRenderPass(renderTarget, params);
@@ -152,8 +187,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
}
backend::Handle<HwRenderTarget> defaultRenderTarget =
cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
RenderPassParams params = {};
fullViewport(params);
@@ -163,19 +197,18 @@ TEST_F(BackendTest, TextureViewLod) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = texturedShader.getProgram();
state.pipelineLayout.setLayout = { texturedShader.getDescriptorSetLayout() };
state.program = textureProgram;
state.pipelineLayout.setLayout = { descriptorSetLayout };
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = SamplerCompareFunc::A;
state.rasterState.culling = CullingMode::NONE;
DescriptorSetHandle descriptorSet13 = texturedShader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet13, 0, texture13, {
api.updateDescriptorSetTexture(descriptorSet[0], 0, texture13, {
.filterMag = SamplerMagFilter::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.
// 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.
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
DescriptorSetHandle descriptorSet22 = texturedShader.createDescriptorSet(api);
api.updateDescriptorSetTexture(descriptorSet22, 0, texture22, {
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
.filterMag = SamplerMagFilter::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.
// This triangle should be yellow striped.
@@ -17,8 +17,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
namespace {
@@ -27,7 +26,7 @@ namespace {
// Shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
std::string vertex(R"(#version 450 core
std::string vertex (R"(#version 450 core
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 {
@@ -69,11 +78,9 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
api.makeCurrent(swapChain, swapChain);
// Create a program.
Shader shader(api, cleanup, ShaderConfig{
.vertexShader = vertex,
.fragmentShader = SharedShaders::getFragmentShaderText(FragmentShaderType::White,
ShaderUniformType::None),
});
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
@@ -82,12 +89,12 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
RenderPassParams params = {};
fullViewport(params);
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.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = shader.getProgram();
state.program = program;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
@@ -18,7 +18,6 @@
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -79,8 +78,6 @@ void main() {
})";
TEST_F(BackendTest, PushConstants) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL));
auto& api = getDriverApi();
api.startCapture(0);
+63 -53
View File
@@ -18,8 +18,7 @@
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -31,7 +30,6 @@ using namespace filament;
using namespace filament::backend;
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
@@ -44,7 +42,20 @@ namespace {
// 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;
@@ -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;
@@ -97,7 +108,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
size_t samples = 1;
// 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);
}
@@ -127,11 +138,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
}
void exportScreenshot(void* pixelData) const {
#ifndef FILAMENT_IOS
#ifndef FILAMENT_IOS
const size_t width = readRect.width, height = readRect.height;
LinearImage image(width, height, 4);
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*) pixelData);
}
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
@@ -140,13 +151,13 @@ TEST_F(ReadPixelsTest, ReadPixels) {
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
png.c_str());
#endif
#endif
}
void exportRawBytes(void* pixelData) const {
std::string out = std::string(testName) + ".raw";
std::ofstream outputStream(out.c_str(), std::ios::binary | std::ios::trunc);
outputStream.write((char*)pixelData, getBufferSizeBytes());
outputStream.write((char*) pixelData, getBufferSizeBytes());
outputStream.close();
}
@@ -229,20 +240,22 @@ TEST_F(ReadPixelsTest, ReadPixels) {
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
std::string vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
ShaderUniformType::None);
Shader floatShader(api, cleanup, ShaderConfig{
.vertexShader = vertexShader,
.fragmentShader = fragmentFloat,
.uniforms = {}
});
Shader uintShader(api, cleanup, ShaderConfig{
.vertexShader = vertexShader,
.fragmentShader = fragmentUint,
.uniforms = {}
});
// Create programs.
Handle<HwProgram> programFloat, programUint;
{
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
programFloat = cleanup.add(api.createProgram(std::move(p)));
}
{
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
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.
Handle<HwSwapChain> swapChain;
if (t.useDefaultRT) {
@@ -279,7 +292,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
RenderPassParams params = {};
fullViewport(params);
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.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = t.getRenderTargetSize();
@@ -292,9 +305,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
api.beginRenderPass(renderTarget, params);
PipelineState state;
state.program = floatShader.getProgram();
state.program = programFloat;
if (isUnsignedIntFormat(t.textureFormat)) {
state.program = uintShader.getProgram();
state.program = programUint;
}
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
@@ -312,7 +325,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
{{ 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.endRenderPass();
}
@@ -322,28 +335,28 @@ TEST_F(ReadPixelsTest, ReadPixels) {
PixelBufferDescriptor descriptor(buffer, t.getBufferSizeBytes(), t.format, t.type,
t.alignment, t.left, t.top, t.getPixelBufferStride(), [](void* buffer, size_t size,
void* user) {
const auto* test = (const TestCase*)user;
void* user) {
const auto* test = (const TestCase*) user;
assert_invariant(test);
test->exportScreenshot(buffer);
//test->exportRawBytes(buffer);
// Hash the contents of the buffer and check that they match.
uint32_t hash = utils::hash::murmur3((const uint32_t*)buffer, size / 4, 0);
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
ASSERT_EQ(test->hash, hash) << test->testName <<
" failed: hashes do not match." << std::endl;
" failed: hashes do not match." << std::endl;
free(buffer);
}, (void*)&t);
}, (void*) &t);
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
t.readRect.height, std::move(descriptor));
// Now render red over what was just rendered. This ensures that readPixels captures the
// 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.endRenderPass();
@@ -363,27 +376,25 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(
api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
.mVertexType = VertexShaderType::Noop,
.mFragmentType = FragmentShaderType::White,
.mUniformType = ShaderUniformType::None
});
// Create a program.
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> texture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
1, // samples
renderTargetSize, // width
renderTargetSize, // height
1, // depth
usage)); // usage
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
1, // samples
renderTargetSize, // width
renderTargetSize, // height
1, // depth
usage)); // usage
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR,
@@ -400,7 +411,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
RenderPassParams params = {};
fullViewport(params);
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.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = renderTargetSize;
@@ -409,7 +420,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
void* buffer = calloc(1, renderTargetSize * renderTargetSize * 4);
PipelineState state;
state.program = shader.getProgram();
state.program = program;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
@@ -433,12 +444,11 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
[](void* buffer, size_t size, void* user) {
ReadPixelsTest* test = (ReadPixelsTest*)user;
ReadPixelsTest* test = (ReadPixelsTest*) user;
test->readPixelsFinished = true;
}, this);
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize,
std::move(descriptor));
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
api.commit(swapChain);
api.endFrame(0);
@@ -17,8 +17,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <backend/DriverEnums.h>
@@ -29,19 +28,42 @@
#include <stddef.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 {
using namespace filament;
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.
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto& api = getDriverApi();
@@ -51,41 +73,57 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
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(
api.createDefaultRenderTarget(0));
// Create a program that samples a texture.
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.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
const NativeView& view = getNativeView();
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_EXTERNAL, // target
1, // levels
TextureFormat::RGBA8, // format
1, // samples
view.width, // width
view.height, // height
1, // depth
usage)); // usage
SamplerType::SAMPLER_EXTERNAL, // target
1, // levels
TextureFormat::RGBA8, // format
1, // samples
view.width, // width
view.height, // height
1, // depth
usage)); // usage
RenderPassParams params = {};
fullViewport(params);
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.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
api.startCapture(0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
@@ -117,11 +155,29 @@ TEST_F(BackendTest, RenderExternalImage) {
auto swapChain = cleanup.add(createSwapChain());
Shader shader = createShader(api, cleanup, sBackend);
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
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(
api.createDefaultRenderTarget(0));
// Create a program that samples a texture.
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
// add a "plane" parameter to setExternalImage
@@ -142,12 +198,10 @@ TEST_F(BackendTest, RenderExternalImage) {
values[1] = values[0];
values[2] = values[0];
values[3] = values[0];
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys,
(const void**)values, 4, nullptr, nullptr);
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**) keys, (const void**) values, 4, nullptr, nullptr);
CVPixelBufferRef pixBuffer = nullptr;
CVReturn status =
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options,
&pixBuffer);
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options, &pixBuffer);
assert(status == kCVReturnSuccess);
// Fill image with checker-pattern.
@@ -156,7 +210,7 @@ TEST_F(BackendTest, RenderExternalImage) {
const uint32_t black = 0xFF000000;
CVReturn lockStatus = CVPixelBufferLockBaseAddress(pixBuffer, 0);
assert(lockStatus == kCVReturnSuccess);
uint32_t* pix = (uint32_t*)CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0);
uint32_t* pix = (uint32_t*) CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0);
assert(pix);
for (size_t r = 0; r < 1024; r++) {
for (size_t c = 0; c < 1024; c++) {
@@ -176,13 +230,13 @@ TEST_F(BackendTest, RenderExternalImage) {
RenderPassParams params = {};
fullViewport(params);
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.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
@@ -193,6 +247,7 @@ TEST_F(BackendTest, RenderExternalImage) {
api.beginFrame(0, 0, 0);
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
api.bindDescriptorSet(descriptorSet, 1, {});
// Render a triangle.
+28 -14
View File
@@ -17,8 +17,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -28,6 +27,23 @@ namespace test {
using namespace filament;
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) {
auto& api = getDriverApi();
@@ -69,11 +85,10 @@ TEST_F(BackendTest, ScissorViewportRegion) {
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
.mVertexType = VertexShaderType::Noop,
.mFragmentType = FragmentShaderType::White,
.mUniformType = ShaderUniformType::None,
});
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
// Create source color and depth textures.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
@@ -118,7 +133,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = shader.getProgram();
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
@@ -160,11 +175,10 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
.mVertexType = VertexShaderType::Noop,
.mFragmentType = FragmentShaderType::White,
.mUniformType = ShaderUniformType::None,
});
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
// Create a source color textures.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
@@ -206,7 +220,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = shader.getProgram();
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
+51 -26
View File
@@ -17,13 +17,43 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
using namespace filament;
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 {
// 1. Clear the stencil buffer to all zeroes.
@@ -34,7 +64,7 @@ namespace test {
class BasicStencilBufferTest : public BackendTest {
public:
Handle <HwSwapChain> mSwapChain;
Handle<HwSwapChain> mSwapChain;
ProgramHandle mProgram;
Cleanup mCleanup;
@@ -47,23 +77,21 @@ public:
mSwapChain = mCleanup.add(createSwapChain());
api.makeCurrent(mSwapChain, mSwapChain);
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{
.mVertexType = VertexShaderType::Noop,
.mFragmentType = FragmentShaderType::White,
.mUniformType = ShaderUniformType::None
});
mProgram = shader.getProgram();
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
mProgram = mCleanup.add(api.createProgram(std::move(p)));
}
void RunTest(Handle <HwRenderTarget> renderTarget) {
void RunTest(Handle<HwRenderTarget> renderTarget) {
auto& api = getDriverApi();
// We'll be using a triangle as geometry.
TrianglePrimitive smallTriangle(api);
static filament::math::float2 vertices[3] = {
{ -0.5, -0.5 },
{ 0.5, -0.5 },
{ -0.5, 0.5 }
{ 0.5, -0.5 },
{ -0.5, 0.5 }
};
smallTriangle.updateVertices(vertices);
TrianglePrimitive triangle(api);
@@ -72,7 +100,7 @@ public:
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
RenderPassParams params = {};
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.clearStencil = 0u;
params.flags.discardStart = TargetBufferFlags::ALL;
@@ -125,7 +153,7 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{ colorTexture }}, {}, {{ stencilTexture }}));
{{colorTexture}}, {}, {{stencilTexture}}));
RunTest(renderTarget);
@@ -143,11 +171,10 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1,
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{ colorTexture }}, { depthStencilTexture }, {{ depthStencilTexture }}));
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
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.
// Performs an auto-resolve on the color.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1,
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1,
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget0 = cleanup.add(api.createRenderTarget(
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{}}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
auto renderTarget1 = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{ colorTexture }}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
api.startCapture(0);
@@ -185,8 +210,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
TrianglePrimitive smallTriangle(api);
static filament::math::float2 vertices[3] = {
{ -0.5, -0.5 },
{ 0.5, -0.5 },
{ -0.5, 0.5 }
{ 0.5, -0.5 },
{ -0.5, 0.5 }
};
smallTriangle.updateVertices(vertices);
TrianglePrimitive triangle(api);
@@ -195,7 +220,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::STENCIL;
params.viewport = { 0, 0, 512, 512 };
params.viewport = {0, 0, 512, 512};
params.clearStencil = 0u;
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
-8
View File
@@ -75,14 +75,6 @@ public:
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:
utils::CString mName;
};
-11
View File
@@ -791,17 +791,6 @@ int FEngine::loop() {
}
}
#if FILAMENT_ENABLE_MATDBG
if(debug.server) {
delete debug.server;
}
#endif
#if FILAMENT_ENABLE_FGVIEWER
if(debug.fgviewerServer) {
delete debug.fgviewerServer;
}
#endif
// terminate() is a synchronous API
getDriverApi().terminate();
return 0;
+2 -4
View File
@@ -269,14 +269,12 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
if (texture && texture->textureHandleCanMutate()) {
mTextureParameters[binding] = { texture, sampler.getSamplerParams() };
} else {
// Ensure to erase the binding from mTextureParameters since it will not
// be updated.
mTextureParameters.erase(binding);
Handle<HwTexture> handle{};
if (texture) {
handle = texture->getHwHandleForSampling();
assert_invariant(handle == texture->getHwHandle());
} else {
mTextureParameters.erase(binding);
}
mDescriptorSet.setSampler(binding, handle, sampler.getSamplerParams());
}
+27 -46
View File
@@ -1364,7 +1364,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
xvp.left = xvp.bottom = 0;
svp = xvp;
}
if (scaled) {
mightNeedFinalBlit = false;
auto viewport = DEBUG_DYNAMIC_SCALING ? xvp : vp;
@@ -1386,59 +1385,34 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
SamplerMagFilter::NEAREST, SamplerMinFilter::NEAREST);
}
if (UTILS_UNLIKELY((input == postProcessInput && viewRenderTarget == mRenderTargetHandle) &&
view.isStencilBufferEnabled())) {
// FIXME: I think this check is incomplete, if we're rendering into a custom rendertarget
// we need to check that it (not the swapchain) has a stencil buffer.
assert_invariant(mSwapChain);
FILAMENT_CHECK_PRECONDITION(mSwapChain->hasStencilBuffer())
<< "View has stencil buffer enabled, but SwapChain does not have "
"SwapChain::CONFIG_HAS_STENCIL_BUFFER flag set.";
}
/*
* Here we're ready to present the output of the framegraph, which is held in `input`.
* The presentation itself happens by forwarding `input` to the render target, which can
* either be the SwapChain's or the View's custom render target. This is done below with
* `forwardResource()`.
*
* There are however a few situations where `input` cannot be forwarded in this manner, and
* an intermediate buffer is needed instead.
*
* 1. Blending is needed (blendModeTranslucent)
* 2. Dimensions don't match, e.g.: because we have guard bands (xvp != svp)
* 3. MRT is needed (colorGradingConfig.asSubpass)
* 4. Frame history is needed (hasScreenSpaceRefraction)
* 5. Refraction is used because how clear flags work (ssReflectionsOptions.enabled)
* 6. `input` and the render target are not compatible:
* - MSAA doesn't match. Note: auto-resolve could work if the SwapChain was configured
* this way.
*
* One complication is that post-processing passes handle some of these issues automatically:
* - each post-processing pass will create an intermediate buffer, which takes care of
* - MSAA (because of auto-resolve)
* - MRT is needed
* - Frame history is needed
* - Refraction
* - the upscaling pass takes care of all the above plus blending and dimensions mismatch.
*
* TODO: we could make this work with custom render targets because we can access the texture
* for those and it could behave just like a regular resource. This would lift
* some (but not all) of those limitations. For instance we could use MRTs.
*/
// We need to do special processing when rendering directly into the swap-chain, that is when
// the viewRenderTarget is the default render target (mRenderTarget) and we're rendering into
// it.
// * This is because the default render target is not multi-sampled, so we need an
// intermediate buffer when MSAA is enabled.
// * We also need an extra buffer for blending the result to the framebuffer if the view
// is translucent AND we've not already done it as part of upscaling.
// * And we can't use the default rendertarget if MRT is required (e.g. with color grading
// as a subpass)
// * And we also can't use the default rendertarget if frame history is needed (e.g. with
// screen-space reflections)
// * And we also can't use the default rendertarget with refractions, which need to reuse
// the rendertarget due to how the clear flags work.
// * We also need an extra blit if we haven't yet handled "xvp"
// TODO: in that specific scenario it would be better to just not use xvp
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
const bool outputIsSwapChain =
(input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle);
if (mightNeedFinalBlit) {
assert_invariant(!scaled);
// Determine if our `input` is in fact the output of the color pass, in which case
// many of the caveat above apply.
bool const inputIsColorPass = (input == postProcessInput);
if (blendModeTranslucent ||
xvp != svp ||
(inputIsColorPass &&
(outputIsSwapChain &&
(msaaSampleCount > 1 ||
colorGradingConfig.asSubpass ||
hasScreenSpaceRefraction ||
ssReflectionsOptions.enabled))) {
assert_invariant(!scaled);
input = ppm.blit(fg, blendModeTranslucent, input, xvp, {
.width = vp.width, .height = vp.height,
.format = colorGradingConfig.ldrFormat },
@@ -1446,6 +1420,13 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
}
}
if (UTILS_UNLIKELY(outputIsSwapChain && view.isStencilBufferEnabled())) {
assert_invariant(mSwapChain);
FILAMENT_CHECK_PRECONDITION(mSwapChain->hasStencilBuffer())
<< "View has stencil buffer enabled, but SwapChain does not have "
"SwapChain::CONFIG_HAS_STENCIL_BUFFER flag set.";
}
if (UTILS_UNLIKELY(engine.debug.shadowmap.display_shadow_texture)) {
auto shadowmap = blackboard.get<FrameGraphTexture>("shadowmap");
input = ppm.debugDisplayShadowTexture(fg, input, shadowmap,
+1 -7
View File
@@ -161,13 +161,7 @@ Texture::Builder& Texture::Builder::name(StaticString const& name) noexcept {
Texture* Texture::Builder::build(Engine& engine) {
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
<< "Texture format " << uint16_t(mImpl->mFormat)
<< " not supported on this platform, texture name="
<< getNameOrDefault().c_str_safe();
FILAMENT_CHECK_PRECONDITION(mImpl->mWidth > 0 && mImpl->mHeight > 0)
<< "Texture has invalid dimensions: (" << mImpl->mWidth << ", " << mImpl->mHeight
<< "), texture name=" << getNameOrDefault().c_str_safe();
<< "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
}
const bool isProtectedTexturesSupported =
downcast(engine).getDriverApi().isProtectedTexturesSupported();
-5
View File
@@ -88,12 +88,7 @@ void minLeftMaxRight(inout vec4 a, inout vec4 b, inout vec4 c, inout vec4 d, ino
// - we can't use a function because offset must be a constant expression
// - we can't use line continuation because that's not supported on desktop
#ifdef TARGET_WEBGPU_ENVIRONMENT
//WebGPU can not handle texelFetchOffset without an error about "ConstOffset is only permitted...".
#define makeSample(ij, o) vec4(texelFetch(materialParams_dof, ij+o, 0).rgb, texelFetch(materialParams_alpha, ij+o, 0).r)
#else
#define makeSample(ij, o) vec4(texelFetchOffset(materialParams_dof, ij, 0, o).rgb, texelFetchOffset(materialParams_alpha, ij, 0, o).r)
#endif
vec4 median(ivec2 ij) {
vec4 v[9];
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.59.2"
spec.version = "1.59.1"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
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.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.1/filament-v1.59.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+21 -95
View File
@@ -518,89 +518,28 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
}
}
void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t> &spirv) {
constexpr size_t HEADER_SIZE = 5;
size_t const dataSize = spirv.size();
uint32_t *data = spirv.data();
std::set<uint32_t> samplerTargetIDs;
auto pass = [&](uint32_t targetOp, std::function<void(uint32_t)> f) {
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
uint32_t const firstWord = data[cursor];
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
if (targetOp == op) {
f(cursor + 1);
}
cursor += wordCount;
}
};
//Parse through debug name info to determine which bindings are samplers and which are not.
// This is possible because the sampler splitting pass outputs sampler and texture pairs of the form:
// `uniform sampler2D var_x` => `uniform sampler var_sampler` and `uniform texture2D var_texture`;
// TODO: This works, but may limit what optimizations can be done and has the potential to collide with user
// variable names. Ideally, trace usage to determine binding type.
pass(spv::Op::OpName, [&](uint32_t pos) {
auto target = data[pos];
char *name = (char *) &data[pos + 1];
std::string_view view(name);
if (view.find("_sampler") != std::string_view::npos) {
samplerTargetIDs.insert(target);
}
});
// Write out the offset bindings
pass(spv::Op::OpDecorate, [&](uint32_t pos) {
uint32_t const type = data[pos + 1];
if (type == spv::Decoration::DecorationBinding) {
uint32_t const targetVar = data[pos];
if (samplerTargetIDs.find(targetVar) != samplerTargetIDs.end()) {
data[pos + 2] = data[pos + 2] * 2 + 1;
} else {
data[pos + 2] = data[pos + 2] * 2;
}
}
});
}
bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
#if FILAMENT_SUPPORTS_WEBGPU
//We need to run some opt-passes at all times to transpile to WGSL
auto optimizer = createEmptyOptimizer();
optimizer->RegisterPass(CreateSplitCombinedImageSamplerPass());
optimizeSpirv(optimizer, *spirv);
// We need to remove dead code for our variant-filter workaround for WebGPU to work
// This is especially relevant for removing the push constants that morphing uses when not disabled
spv::spirvbin_t remapper(0);
remapper.remap(*spirv, spv::spirvbin_base_t::DCE_ALL);
//After splitting the image samplers, we need to remap the bindings to separate them.
rebindImageSamplerForWGSL(*spirv);
//Allow non-uniform derivitives due to our nested shaders. See https://github.com/gpuweb/gpuweb/issues/3479
const tint::spirv::reader::Options readerOpts{true};
//Currently no options we want to use
const tint::spirv::reader::Options readerOpts{};
tint::wgsl::writer::Options writerOpts{};
tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);
if (tintRead.Diagnostics().ContainsErrors()) {
//We know errors can potentially crop up, and want the ability to ignore them if needed for sample bringup
#ifndef FILAMENT_WEBGPU_IGNORE_TNT_READ_ERRORS
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
spv_context context = spvContextCreate(SPV_ENV_VULKAN_1_1_SPIRV_1_4);
spv_text text = nullptr;
spv_diagnostic diagnostic = nullptr;
spv_result_t result = spvBinaryToText(
context,
spirv->data(),
spirv->size(),
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_COLOR,
&text,
&diagnostic);
slog.e << "Beginning SpirV-output dump with ret " << result << "\n\n" << text->str << "\n\nEndSPIRV\n" <<
io::endl;
spvTextDestroy(text);
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
#endif
//TODO remove this if block once combined image sampler conversion works.
if (tintRead.Diagnostics().Str().rfind("error: WGSL does not support combined image-samplers") != std::string::npos) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup: " << tintRead.Diagnostics().Str() << io::endl;
}
else {
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
}
}
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
@@ -608,7 +547,7 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
tint::SuccessType tintSuccess;
if (wgslOut != tintSuccess) {
slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl;
slog.e << "Tint writer error: " << wgslOut.Failure().reason.Str() << io::endl;
return false;
}
*outWsl = wgslOut->wgsl;
@@ -948,8 +887,10 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
return true;
}
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
MaterialBuilder::Optimization optimization, Config const& config) {
auto optimizer = std::make_shared<spvtools::Optimizer>(SPV_ENV_UNIVERSAL_1_3);
optimizer->SetMessageConsumer([](spv_message_level_t level,
const char* source, const spv_position_t& position, const char* message) {
if (!filterSpvOptimizerMessage(level)) {
@@ -958,12 +899,6 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
slog.e << stringifySpvOptimizerMessage(level, source, position, message)
<< io::endl;
});
return optimizer;
}
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
MaterialBuilder::Optimization optimization, Config const& config) {
auto optimizer = createEmptyOptimizer();
if (optimization == MaterialBuilder::Optimization::SIZE) {
// When optimizing for size, we don't run the SPIR-V through any size optimization passes
@@ -988,7 +923,7 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
return optimizer;
}
void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) {
void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const {
if (!optimizer->Run(spirv.data(), spirv.size(), &spirv)) {
slog.e << "SPIR-V optimizer pass failed" << io::endl;
return;
@@ -1020,9 +955,6 @@ void GLSLPostProcessor::fixupClipDistance(
// - triggers a segfault with AMD OpenGL drivers on macOS
// - triggers a crash on some Adreno drivers (b/291140208, b/289401984, b/289393290)
// However Metal requires this pass in order to correctly generate half-precision MSL
// CreateMergeReturnPass() also creates issues with Tint conversion related to the
// bitwise "<<" Operator used in shaders/src/surface_light_directional.fs against
// a signed integer.
//
// CreateSimplificationPass() creates a lot of problems:
// - Adreno GPU show artifacts after running simplification passes (Vulkan)
@@ -1033,11 +965,6 @@ void GLSLPostProcessor::fixupClipDistance(
// However, the simplification passes below are necessary when targeting Metal, otherwise the
// result is mismatched half / float assignments in MSL.
// CreateInlineExhaustivePass() expects CreateMergeReturnPass() to be run beforehand
// (Throwing many warnings if this is not the case), but we don't consistently do so for the above
// reasons. While running it alone may have some value, we will disable it for the new WebGPU backend
// while minimizing other changes.
void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config const& config) {
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
@@ -1051,7 +978,7 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
RegisterPass(CreateWrapOpKillPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateMergeReturnPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateInlineExhaustivePass(), MaterialBuilder::TargetApi::ALL & ~MaterialBuilder::TargetApi::WEBGPU);
RegisterPass(CreateInlineExhaustivePass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreatePrivateToLocalPass());
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
@@ -1095,8 +1022,7 @@ void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& c
RegisterPass(CreateWrapOpKillPass());
RegisterPass(CreateDeadBranchElimPass());
// Disable for WebGPU, see comment above registerPerformancePasses()
RegisterPass(CreateInlineExhaustivePass(), MaterialBuilder::TargetApi::ALL & ~MaterialBuilder::TargetApi::WEBGPU);
RegisterPass(CreateInlineExhaustivePass());
RegisterPass(CreateEliminateDeadFunctionsPass());
RegisterPass(CreatePrivateToLocalPass());
RegisterPass(CreateScalarReplacementPass(0));
+1 -7
View File
@@ -114,20 +114,14 @@ private:
* Retrieve an optimizer instance tuned for the given optimization level and shader configuration.
*/
using OptimizerPtr = std::shared_ptr<spvtools::Optimizer>;
static OptimizerPtr createOptimizer(
MaterialBuilder::Optimization optimization,
Config const& config);
static OptimizerPtr createEmptyOptimizer();
static void registerSizePasses(spvtools::Optimizer& optimizer, Config const& config);
static void registerPerformancePasses(spvtools::Optimizer& optimizer, Config const& config);
static void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob &spirv);
static void rebindImageSamplerForWGSL(std::vector<uint32_t>& spirv);
void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const;
void fixupClipDistance(SpirvBlob& spirv, GLSLPostProcessor::Config const& config) const;
@@ -937,15 +937,6 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
std::string const constantString = std::visit(SpecializationConstantFormatter(), value);
static const char* types[] = { "int", "float", "bool" };
// Spec constants aren't fully supported in Tint,
// workaround until https://issues.chromium.org/issues/42250586 is resolved
if (mTargetApi == TargetApi::WEBGPU) {
out << "layout (constant_id = " << id << ") const "
<< types[value.index()] << " " << name << "_hack = " << constantString << ";\n"
<< types[value.index()] << " " << name << " = " << name << "_hack;\n";
return out;
}
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
out << "layout (constant_id = " << id << ") const "
<< types[value.index()] << " " << name << " = " << constantString << ";\n";
+1 -3
View File
@@ -704,9 +704,7 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
::prepareNativeWindow(mWindow);
void* metalLayer = nullptr;
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN
|| config.backend == filament::Engine::Backend::WEBGPU) {
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN) {
metalLayer = setUpMetalLayer(nativeWindow);
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
nativeSwapChain = metalLayer;
-2
View File
@@ -17,8 +17,6 @@
#ifndef TNT_UTILS_STATICSTRING_H
#define TNT_UTILS_STATICSTRING_H
#include <utils/compiler.h>
#include <type_traits>
#include <string_view>
-6
View File
@@ -205,13 +205,7 @@ void setup_window(Window& w, Engine* engine) {
void* nativeSwapChain = nativeWindow;
#if defined(__APPLE__)
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) {
#endif
metalLayer = setUpMetalLayer(nativeWindow);
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
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
-17
View File
@@ -1,17 +0,0 @@
# Bzlmod lockfile
MODULE.bazel.lock
# Ignore all bazel-* symlinks.
/bazel-*
# Ignore Bazel verbose explanations
--verbose_explanations
# Ignore CMake usual build directory
build
# Ignore Vim files
*.swp
# Ignore QtCreator Project file
CMakeLists.txt.user
# Ignore VS Code files
.vscode/*
# Ignore generated python artifacts
*.pyc
copts/__pycache__/
+374
View File
@@ -0,0 +1,374 @@
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Flags specified here must not impact ABI. Code compiled with and without these
# opts will be linked together, and in some cases headers compiled with and
# without these options will be part of the same program.
import("//build/config/c++/c++.gni")
import("//build/config/nacl/config.gni")
import("//build/config/sanitizers/sanitizers.gni")
import("//build/toolchain/toolchain.gni")
import("//build_overrides/build.gni")
import("//third_party/abseil-cpp/absl.gni")
config("absl_component_build") {
defines = [ "ABSL_CONSUME_DLL" ]
}
assert(!is_nacl || is_nacl_saigo,
"base must not be built in most nacl toolchains")
component("absl") {
public_deps = [ ":absl_component_deps" ]
if (is_component_build) {
public_configs = [ ":absl_component_build" ]
if (is_win && is_clang) {
if (current_cpu == "x64") {
if (is_debug) {
sources = [ "symbols_x64_dbg.def" ]
} else {
if (is_asan) {
sources = [ "symbols_x64_rel_asan.def" ]
} else {
sources = [ "symbols_x64_rel.def" ]
}
}
}
if (current_cpu == "x86") {
if (is_debug) {
sources = [ "symbols_x86_dbg.def" ]
} else {
sources = [ "symbols_x86_rel.def" ]
}
}
if (current_cpu == "arm64") {
if (is_debug) {
sources = [ "symbols_arm64_dbg.def" ]
} else {
sources = [ "symbols_arm64_rel.def" ]
}
}
}
}
}
group("absl_component_deps") {
public_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
"//third_party/abseil-cpp/absl/base",
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/base:no_destructor",
"//third_party/abseil-cpp/absl/base:nullability",
"//third_party/abseil-cpp/absl/base:prefetch",
"//third_party/abseil-cpp/absl/cleanup",
"//third_party/abseil-cpp/absl/container:btree",
"//third_party/abseil-cpp/absl/container:fixed_array",
"//third_party/abseil-cpp/absl/container:flat_hash_map",
"//third_party/abseil-cpp/absl/container:flat_hash_set",
"//third_party/abseil-cpp/absl/container:inlined_vector",
"//third_party/abseil-cpp/absl/container:node_hash_map",
"//third_party/abseil-cpp/absl/container:node_hash_set",
"//third_party/abseil-cpp/absl/debugging:failure_signal_handler",
"//third_party/abseil-cpp/absl/debugging:stacktrace",
"//third_party/abseil-cpp/absl/debugging:symbolize",
"//third_party/abseil-cpp/absl/functional:any_invocable",
"//third_party/abseil-cpp/absl/functional:bind_front",
"//third_party/abseil-cpp/absl/functional:function_ref",
"//third_party/abseil-cpp/absl/functional:overload",
"//third_party/abseil-cpp/absl/hash",
"//third_party/abseil-cpp/absl/log:absl_check",
"//third_party/abseil-cpp/absl/log:absl_log",
"//third_party/abseil-cpp/absl/log:die_if_null",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/meta:type_traits",
"//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/numeric:int128",
"//third_party/abseil-cpp/absl/random",
"//third_party/abseil-cpp/absl/random:distributions",
"//third_party/abseil-cpp/absl/status",
"//third_party/abseil-cpp/absl/status:statusor",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/strings:cord",
"//third_party/abseil-cpp/absl/strings:has_ostream_operator",
"//third_party/abseil-cpp/absl/strings:str_format",
"//third_party/abseil-cpp/absl/strings:string_view",
"//third_party/abseil-cpp/absl/synchronization",
"//third_party/abseil-cpp/absl/time",
"//third_party/abseil-cpp/absl/types:optional",
"//third_party/abseil-cpp/absl/types:span",
"//third_party/abseil-cpp/absl/types:variant",
"//third_party/abseil-cpp/absl/utility",
]
if (is_component_build) {
public_deps += [ ":absl_full_deps" ]
}
# The following dependencies currently don't build with NaCl.
# TODO(https://crbug.com/1114625): Fix build errors and remove this section.
if (is_nacl) {
public_deps -=
[ "//third_party/abseil-cpp/absl/debugging:failure_signal_handler" ]
}
visibility = [ ":absl" ]
}
# Some third-party libraries use additional parts of absl that are banned in
# Chrome (e.g. flags generates static initializers that are undesirable in
# non-test binaries). These third-party libraries can depend on `absl_full`
# instead of `absl` to use those banned subsets of Abseil as well.
source_set("absl_full") {
public_deps = [ ":absl" ]
# In component builds, `absl` itself depends on `absl_full_deps` directly so
# that all of absl is built as a single shared library. This is to prevent
# globals from being duplicated between both `absl` and `absl_full` (e.g.
# inline variables), and the subtle breakage that could cause.
#
# Deps violations will still be enforced by the non-component build.
if (!is_component_build) {
public_deps += [ ":absl_full_deps" ]
}
if (build_with_chromium) {
visibility = [
"//third_party/fuzztest:*",
# GoogleTest doesn't actually need absl_full, but this makes gn check
# happier.
"//third_party/googletest:*",
]
}
}
group("absl_full_deps") {
public_deps = [
# Banned because each flag registration generates a static initializer, and
# the flag registration mechanism is problematic in component builds.
"//third_party/abseil-cpp/absl/flags:config",
"//third_party/abseil-cpp/absl/flags:flag",
"//third_party/abseil-cpp/absl/flags:parse",
"//third_party/abseil-cpp/absl/flags:reflection",
"//third_party/abseil-cpp/absl/flags:usage",
# Banned because the LOG and CHECK macros conflict with Chrome's version of
# those macros.
"//third_party/abseil-cpp/absl/log",
"//third_party/abseil-cpp/absl/log:check",
# TODO(danilchap): Investigate if these targets need to be banned in 3rd party library code.
"//third_party/abseil-cpp/absl/log:globals",
"//third_party/abseil-cpp/absl/log:initialize",
"//third_party/abseil-cpp/absl/random:bit_gen_ref",
]
visibility = [
":absl_component_deps",
":absl_full",
]
public_configs = [ "//third_party/abseil-cpp/absl/flags:absl_flags_config" ]
}
config("absl_include_config") {
include_dirs = [ "." ]
}
config("absl_define_config") {
defines = [ "ABSL_ALLOCATOR_NOTHROW=1" ]
if (is_win && !use_custom_libcxx) {
defines += [
# See crbug.com/1101367: Acknowledge extended alignment when using
# MSVC's standard library.
"_ENABLE_EXTENDED_ALIGNED_STORAGE",
]
}
}
config("absl_default_cflags_cc") {
cflags_cc = []
if (is_clang) {
cflags_cc += [
# TODO(crbug.com/588506): Explicitly enable conversion warnings.
"-Wbool-conversion",
"-Wconstant-conversion",
"-Wenum-conversion",
"-Wint-conversion",
"-Wliteral-conversion",
"-Wnon-literal-null-conversion",
"-Wnull-conversion",
"-Wobjc-literal-conversion",
"-Wstring-conversion",
]
if (!is_nacl) {
cflags_cc += [ "-Wbitfield-enum-conversion" ]
}
}
}
config("absl_public_cflags_cc") {
# Non-chromium users of abseil have more lax style guides and use additional features in
# abseil, Remove a few extra warnings for that use case.
if (!build_with_chromium && is_clang) {
cflags = [
# Allow the use of enable_if()
"-Wno-gcc-compat",
"-Wno-unreachable-code-break",
]
}
}
config("absl_test_cflags_cc") {
cflags_cc = []
if (is_clang || !is_win) {
cflags_cc += [
"-Wno-conversion-null",
"-Wno-missing-declarations",
"-Wno-unused-function",
"-Wno-unused-parameter",
"-Wno-unused-private-field",
]
}
if (is_win) {
cflags_cc += [
"/wd4101", # unreferenced local variable
]
}
}
config("absl_test_config") {
cflags_cc = []
if (is_win && !is_clang) {
cflags_cc += [ "/wd4996" ]
}
if (is_clang) {
cflags_cc += [
"-Wno-deprecated-declarations",
"-Wno-implicit-const-int-float-conversion",
"-Wno-unused-private-field",
"-Wno-unused-function",
]
}
}
if (absl_build_tests) {
import("//testing/test.gni")
if (!is_component_build) {
test("absl_tests") {
testonly = true
deps = [
"absl/algorithm:algorithm_test",
"absl/algorithm:container_test",
"absl/base:config_test",
"absl/base:no_destructor_test",
"absl/base:nullability_test",
"absl/base:prefetch_test",
"absl/cleanup:cleanup_test",
# TODO(mbonadei): Fix issue with EXPECT_DEATH and uncomment.
# "absl/container:btree_test",
"absl/container:common_policy_traits_test",
"absl/container:fixed_array_test",
"absl/container:flat_hash_map_test",
"absl/container:flat_hash_set_test",
"absl/container:hash_function_defaults_test",
"absl/container:inlined_vector_test",
"absl/container:node_slot_policy_test",
"absl/container:raw_hash_set_allocator_test",
"absl/container:raw_hash_set_test",
"absl/container:sample_element_size_test",
"absl/crc:crc32c_test",
"absl/crc:crc_cord_state_test",
"absl/crc:crc_memcpy_test",
"absl/crc:non_temporal_memcpy_test",
"absl/debugging:demangle_rust_test",
"absl/debugging:stacktrace_test",
"absl/flags:flag_test",
"absl/functional:any_invocable_test",
"absl/functional:function_ref_test",
"absl/functional:overload_test",
"absl/hash:hash_instantiated_test",
"absl/hash:hash_test",
"absl/hash:low_level_hash_test",
"absl/log:absl_check_test",
"absl/log:absl_log_basic_test",
"absl/log:die_if_null_test",
"absl/log:flags_test",
"absl/log:globals_test",
"absl/log:log_entry_test",
"absl/log:log_format_test",
"absl/log:log_macro_hygiene_test",
"absl/log:log_modifier_methods_test",
"absl/log:log_sink_test",
"absl/log:log_streamer_test",
"absl/log:scoped_mock_log_test",
"absl/log:stripping_test",
"absl/log:vlog_is_on_test",
"absl/log/internal:fnmatch_test",
"absl/log/internal:stderr_log_sink_test",
"absl/memory:memory_test",
"absl/meta:type_traits_test",
"absl/numeric:int128_test",
"absl/profiling:exponential_biased_test",
"absl/profiling:periodic_sampler_test",
"absl/status:status_matchers_test",
"absl/status:status_test",
"absl/status:statusor_test",
"absl/strings:ascii_test",
"absl/strings:char_formatting_test",
"absl/strings:charset_test",
"absl/strings:cord_buffer_test",
"absl/strings:cord_data_edge_test",
"absl/strings:cord_rep_btree_navigator_test",
"absl/strings:cord_rep_btree_reader_test",
"absl/strings:cord_rep_btree_test",
"absl/strings:cord_rep_crc_test",
"absl/strings:cordz_functions_test",
"absl/strings:cordz_info_statistics_test",
"absl/strings:cordz_info_test",
"absl/strings:cordz_test",
"absl/strings:cordz_update_scope_test",
"absl/strings:cordz_update_tracker_test",
"absl/strings:damerau_levenshtein_distance_test",
"absl/strings:has_absl_stringify_test",
"absl/strings:has_ostream_operator_test",
"absl/strings:match_test",
"absl/strings:str_format_arg_test",
"absl/strings:str_format_bind_test",
"absl/strings:str_format_checker_test",
"absl/strings:str_format_convert_test",
"absl/strings:str_format_extension_test",
"absl/strings:str_format_output_test",
"absl/strings:str_format_parser_test",
"absl/strings:str_format_test",
"absl/strings:str_replace_test",
"absl/strings:string_view_test",
"absl/synchronization:kernel_timeout_internal_test",
"absl/synchronization:waiter_test",
"absl/time:time_test",
"absl/types:optional_test",
"absl/types:span_test",
"absl/types:variant_test",
"absl/utility:if_constexpr_test",
"//third_party/googletest:gtest_main",
]
}
}
test("absl_hardening_tests") {
testonly = true
sources = [ "absl_hardening_test.cc" ]
deps = [
":absl",
"//base/test:run_all_unittests",
"//third_party/googletest:gtest",
]
}
}
+15 -87
View File
@@ -15,7 +15,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/cycleclock.h"
"base/internal/cycleclock_config.h"
"base/internal/direct_mmap.h"
"base/internal/dynamic_annotations.h"
"base/internal/endian.h"
"base/internal/errno_saver.h"
"base/internal/fast_type_id.h"
@@ -28,8 +27,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/low_level_scheduling.h"
"base/internal/nullability_impl.h"
"base/internal/per_thread_tls.h"
"base/internal/poison.cc"
"base/internal/poison.h"
"base/prefetch.h"
"base/internal/pretty_function.h"
"base/internal/raw_logging.cc"
@@ -49,8 +46,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/thread_identity.h"
"base/internal/throw_delegate.cc"
"base/internal/throw_delegate.h"
"base/internal/tracing.cc"
"base/internal/tracing.h"
"base/internal/tsan_mutex_interface.h"
"base/internal/unaligned_access.h"
"base/internal/unscaledcycleclock.cc"
@@ -126,9 +121,6 @@ set(ABSL_INTERNAL_DLL_FILES
"debugging/symbolize.h"
"debugging/internal/address_is_readable.cc"
"debugging/internal/address_is_readable.h"
"debugging/internal/bounded_utf8_length_sequence.h"
"debugging/internal/decode_rust_punycode.cc"
"debugging/internal/decode_rust_punycode.h"
"debugging/internal/demangle.cc"
"debugging/internal/demangle.h"
"debugging/internal/demangle_rust.cc"
@@ -141,8 +133,6 @@ set(ABSL_INTERNAL_DLL_FILES
"debugging/internal/stack_consumption.h"
"debugging/internal/stacktrace_config.h"
"debugging/internal/symbolize.h"
"debugging/internal/utf8_for_code_point.cc"
"debugging/internal/utf8_for_code_point.h"
"debugging/internal/vdso_support.cc"
"debugging/internal/vdso_support.h"
"functional/any_invocable.h"
@@ -193,8 +183,6 @@ set(ABSL_INTERNAL_DLL_FILES
"log/internal/proto.cc"
"log/internal/strip.h"
"log/internal/structured.h"
"log/internal/structured_proto.cc"
"log/internal/structured_proto.h"
"log/internal/vlog_config.cc"
"log/internal/vlog_config.h"
"log/internal/voidify.h"
@@ -325,6 +313,7 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/internal/string_constant.h"
"strings/internal/stringify_sink.h"
"strings/internal/stringify_sink.cc"
"strings/internal/has_absl_stringify.h"
"strings/has_absl_stringify.h"
"strings/has_ostream_operator.h"
"strings/match.cc"
@@ -450,47 +439,9 @@ set(ABSL_INTERNAL_DLL_FILES
"debugging/leak_check.cc"
)
if(NOT MSVC)
list(APPEND ABSL_INTERNAL_DLL_FILES
"flags/commandlineflag.cc"
"flags/commandlineflag.h"
"flags/config.h"
"flags/declare.h"
"flags/flag.h"
"flags/internal/commandlineflag.cc"
"flags/internal/commandlineflag.h"
"flags/internal/flag.cc"
"flags/internal/flag.h"
"flags/internal/parse.h"
"flags/internal/path_util.h"
"flags/internal/private_handle_accessor.cc"
"flags/internal/private_handle_accessor.h"
"flags/internal/program_name.cc"
"flags/internal/program_name.h"
"flags/internal/registry.h"
"flags/internal/sequence_lock.h"
"flags/internal/usage.cc"
"flags/internal/usage.h"
"flags/marshalling.cc"
"flags/marshalling.h"
"flags/parse.cc"
"flags/parse.h"
"flags/reflection.cc"
"flags/reflection.h"
"flags/usage.cc"
"flags/usage.h"
"flags/usage_config.cc"
"flags/usage_config.h"
"log/flags.cc"
"log/flags.h"
"log/internal/flags.h"
)
endif()
set(ABSL_INTERNAL_DLL_TARGETS
"absl_check"
"absl_log"
"absl_vlog_is_on"
"algorithm"
"algorithm_container"
"any"
@@ -556,7 +507,6 @@ set(ABSL_INTERNAL_DLL_TARGETS
"log_internal_check_op"
"log_internal_conditions"
"log_internal_config"
"log_internal_fnmatch"
"log_internal_format"
"log_internal_globals"
"log_internal_log_impl"
@@ -636,7 +586,6 @@ set(ABSL_INTERNAL_DLL_TARGETS
"strerror"
"strings"
"strings_internal"
"string_view"
"symbolize"
"synchronization"
"thread_pool"
@@ -647,30 +596,8 @@ set(ABSL_INTERNAL_DLL_TARGETS
"type_traits"
"utility"
"variant"
"vlog_config_internal"
"vlog_is_on"
)
if(NOT MSVC)
list(APPEND ABSL_INTERNAL_DLL_TARGETS
"flags"
"flags_commandlineflag"
"flags_commandlineflag_internal"
"flags_config"
"flags_internal"
"flags_marshalling"
"flags_parse"
"flags_path_util"
"flags_private_handle_accessor"
"flags_program_name"
"flags_reflection"
"flags_usage"
"flags_usage_internal"
"log_internal_flags"
"log_flags"
)
endif()
set(ABSL_INTERNAL_TEST_DLL_FILES
"hash/hash_testing.h"
"log/scoped_mock_log.cc"
@@ -747,7 +674,12 @@ function(absl_internal_dll_contains)
STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_DLL_TARGET})
if (_target IN_LIST ABSL_INTERNAL_DLL_TARGETS)
list(FIND
ABSL_INTERNAL_DLL_TARGETS
"${_target}"
_index)
if (${_index} GREATER -1)
set(${ABSL_INTERNAL_DLL_OUTPUT} 1 PARENT_SCOPE)
else()
set(${ABSL_INTERNAL_DLL_OUTPUT} 0 PARENT_SCOPE)
@@ -764,7 +696,12 @@ function(absl_internal_test_dll_contains)
STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_TEST_DLL_TARGET})
if (_target IN_LIST ABSL_INTERNAL_TEST_DLL_TARGETS)
list(FIND
ABSL_INTERNAL_TEST_DLL_TARGETS
"${_target}"
_index)
if (${_index} GREATER -1)
set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 1 PARENT_SCOPE)
else()
set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 0 PARENT_SCOPE)
@@ -816,12 +753,7 @@ function(absl_make_dll)
else()
set(_dll "abseil_dll")
set(_dll_files ${ABSL_INTERNAL_DLL_FILES})
set(_dll_libs
Threads::Threads
# TODO(#1495): Use $<LINK_LIBRARY:FRAMEWORK,CoreFoundation> once our
# minimum CMake version >= 3.24
$<$<PLATFORM_ID:Darwin>:-Wl,-framework,CoreFoundation>
)
set(_dll_libs "")
set(_dll_compile_definitions "")
set(_dll_includes "")
set(_dll_consume "ABSL_CONSUME_DLL")
@@ -838,12 +770,8 @@ function(absl_make_dll)
PRIVATE
${_dll_libs}
${ABSL_DEFAULT_LINKOPTS}
$<$<BOOL:${ANDROID}>:-llog>
)
set_target_properties(${_dll} PROPERTIES
LINKER_LANGUAGE "CXX"
SOVERSION ${ABSL_SOVERSION}
)
set_property(TARGET ${_dll} PROPERTY LINKER_LANGUAGE "CXX")
target_include_directories(
${_dll}
PUBLIC
+9 -16
View File
@@ -186,16 +186,16 @@ function(absl_cc_library)
endif()
endif()
endforeach()
set(skip_next_cflag OFF)
foreach(cflag ${ABSL_CC_LIB_COPTS})
# Strip out the CMake-specific `SHELL:` prefix, which is used to construct
# a group of space-separated options.
# https://cmake.org/cmake/help/v3.30/command/target_compile_options.html#option-de-duplication
string(REGEX REPLACE "^SHELL:" "" cflag "${cflag}")
if(${cflag} MATCHES "^-Xarch_")
if(skip_next_cflag)
set(skip_next_cflag OFF)
elseif(${cflag} MATCHES "^-Xarch_")
# An -Xarch_ flag implies that its successor only applies to the
# specified platform. Such option groups are each specified in a single
# `SHELL:`-prefixed string in the COPTS list, which we simply ignore.
elseif(${cflag} MATCHES "^(-Wno-|/wd)")
# specified platform. Filter both of them out before the successor
# reaches the "^-m" filter.
set(skip_next_cflag ON)
elseif(${cflag} MATCHES "^(-Wno|/wd)")
# These flags are needed to suppress warnings that might fire in our headers.
set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
@@ -258,13 +258,6 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
elseif(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
add_library(${_NAME} "")
target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS})
if(APPLE)
set_target_properties(${_NAME} PROPERTIES
INSTALL_RPATH "@loader_path")
elseif(UNIX)
set_target_properties(${_NAME} PROPERTIES
INSTALL_RPATH "$ORIGIN")
endif()
target_link_libraries(${_NAME}
PUBLIC ${ABSL_CC_LIB_DEPS}
PRIVATE
@@ -313,7 +306,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
if(ABSL_ENABLE_INSTALL)
set_target_properties(${_NAME} PROPERTIES
OUTPUT_NAME "absl_${_NAME}"
SOVERSION "${ABSL_SOVERSION}"
SOVERSION 0
)
endif()
else()
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.10)
project(googletest-external NONE)
+2 -2
View File
@@ -39,7 +39,7 @@ section of your executable or of your library.<br>
Here is a short CMakeLists.txt example of an application project using Abseil.
```cmake
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.10)
project(my_app_project)
# Pick the C++ standard to compile with.
@@ -62,7 +62,7 @@ will control Abseil library targets) is set to at least that minimum. For
example:
```cmake
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.10)
project(my_lib_project)
# Leave C++ standard up to the root application, so set it only if this is the
@@ -15,7 +15,7 @@
# A simple CMakeLists.txt for testing cmake installation
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.10)
project(absl_cmake_testing CXX)
add_executable(simple simple.cc)
+4 -5
View File
@@ -22,8 +22,7 @@ set -euox pipefail
absl_dir=/abseil-cpp
absl_build_dir=/buildfs
googletest_builddir=/googletest_builddir
googletest_archive="googletest-${ABSL_GOOGLETEST_VERSION}.tar.gz"
project_dir="${absl_dir}/CMake/install_test_project"
project_dir="${absl_dir}"/CMake/install_test_project
project_build_dir=/buildfs/project-build
build_shared_libs="OFF"
@@ -34,9 +33,9 @@ fi
# Build and install GoogleTest
mkdir "${googletest_builddir}"
pushd "${googletest_builddir}"
curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${googletest_archive}"
tar -xz -f "${googletest_archive}"
pushd "googletest-${ABSL_GOOGLETEST_VERSION}"
curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${ABSL_GOOGLETEST_COMMIT}".zip
unzip "${ABSL_GOOGLETEST_COMMIT}".zip
pushd "googletest-${ABSL_GOOGLETEST_COMMIT}"
mkdir build
pushd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" ..
+54 -44
View File
@@ -15,16 +15,50 @@
#
# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md
# As of 2024-07-01, CMake 3.16 is the minimum supported version.
cmake_minimum_required(VERSION 3.16)
# As of 2022-09-06, CMake 3.10 is the minimum supported version.
cmake_minimum_required(VERSION 3.10)
# Compiler id for Apple Clang is now AppleClang.
if (POLICY CMP0025)
cmake_policy(SET CMP0025 NEW)
endif (POLICY CMP0025)
# if command can use IN_LIST
if (POLICY CMP0057)
cmake_policy(SET CMP0057 NEW)
endif (POLICY CMP0057)
# Project version variables are the empty string if version is unspecified
if (POLICY CMP0048)
cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)
# Honor the GTest_ROOT variable if specified
if (POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif (POLICY CMP0074)
# option() honor variables
if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif (POLICY CMP0077)
# Allow the user to specify the MSVC runtime
if (POLICY CMP0091)
cmake_policy(SET CMP0091 NEW)
endif (POLICY CMP0091)
# try_compile() honors the CMAKE_CXX_STANDARD value
if (POLICY CMP0067)
cmake_policy(SET CMP0067 NEW)
endif (POLICY CMP0067)
# Allow the user to specify the CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
endif (POLICY CMP0141)
project(absl LANGUAGES CXX VERSION 20250127)
set(ABSL_SOVERSION "2501.0.0")
project(absl LANGUAGES CXX)
include(CTest)
# Output directory is correct by default for most build setups. However, when
@@ -41,13 +75,12 @@ else()
option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
endif()
set(CMAKE_INSTALL_RPATH "$ORIGIN")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
option(ABSL_PROPAGATE_CXX_STD
"Use CMake C++ standard meta features (e.g. cxx_std_14) that propagate to targets that link to Abseil"
ON)
OFF) # TODO: Default to ON for CMake 3.8 and greater.
if(NOT ABSL_PROPAGATE_CXX_STD)
message(WARNING "A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly.")
endif()
option(ABSL_USE_SYSTEM_INCLUDES
"Silence warnings in Abseil headers by marking them as SYSTEM includes"
@@ -58,15 +91,6 @@ list(APPEND CMAKE_MODULE_PATH
${CMAKE_CURRENT_LIST_DIR}/absl/copts
)
option(ABSL_MSVC_STATIC_RUNTIME
"Link static runtime libraries"
OFF)
if(ABSL_MSVC_STATIC_RUNTIME)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
else()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
include(AbseilDll)
@@ -125,14 +149,6 @@ set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH
"If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout."
)
option(ABSL_BUILD_MONOLITHIC_SHARED_LIBS
"Build Abseil as a single shared library (always enabled for Windows)"
OFF
)
if(NOT BUILD_SHARED_LIBS AND ABSL_BUILD_MONOLITHIC_SHARED_LIBS)
message(WARNING "Not building a shared library because BUILD_SHARED_LIBS is not set. Ignoring ABSL_BUILD_MONOLITHIC_SHARED_LIBS.")
endif()
if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS)
if (ABSL_USE_EXTERNAL_GOOGLETEST)
if (ABSL_FIND_GOOGLETEST)
@@ -170,7 +186,17 @@ endif()
add_subdirectory(absl)
if(ABSL_ENABLE_INSTALL)
# absl:lts-remove-begin(system installation is supported for LTS releases)
# We don't support system-wide installation
list(APPEND SYSTEM_INSTALL_DIRS "/usr/local" "/usr" "/opt/" "/opt/local" "c:/Program Files/${PROJECT_NAME}")
if(NOT DEFINED CMAKE_INSTALL_PREFIX OR CMAKE_INSTALL_PREFIX IN_LIST SYSTEM_INSTALL_DIRS)
message(WARNING "\
The default and system-level install directories are unsupported except in LTS \
releases of Abseil. Please set CMAKE_INSTALL_PREFIX to install Abseil in your \
source or build tree directly.\
")
endif()
# absl:lts-remove-end
# install as a subdirectory only
install(EXPORT ${PROJECT_NAME}Targets
@@ -201,13 +227,11 @@ if(ABSL_ENABLE_INSTALL)
)
endif() # absl_VERSION
# Install the headers except for "options.h" which is installed separately.
install(DIRECTORY absl
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.inc"
PATTERN "*.h"
PATTERN "options.h" EXCLUDE
PATTERN "copts" EXCLUDE
PATTERN "testdata" EXCLUDE
)
@@ -247,21 +271,7 @@ if(ABSL_ENABLE_INSTALL)
ABSL_INTERNAL_OPTIONS_H_PINNED
"${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
# If the file already exists, check if it matches the new contents.
# This avoids writing the file if it is already up-to-date when the CMake
# generation is triggered and triggering unnecessary rebuilds.
set(ABSL_INTERNAL_OPTIONS_H_PINNED_NEEDS_UPDATE TRUE)
if (EXISTS "${CMAKE_BINARY_DIR}/options-pinned.h")
file(READ "${CMAKE_BINARY_DIR}/options-pinned.h" ABSL_INTERNAL_OPTIONS_PINNED_H_CONTENTS)
if ("${ABSL_INTERNAL_OPTIONS_H_PINNED}" STREQUAL "${ABSL_INTERNAL_OPTIONS_PINNED_H_CONTENTS}")
set(ABSL_INTERNAL_OPTIONS_H_PINNED_NEEDS_UPDATE FALSE)
endif()
endif()
# If the file needs an update, generate it.
if (ABSL_INTERNAL_OPTIONS_H_PINNED_NEEDS_UPDATE)
file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/options-pinned.h" CONTENT "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
endif()
file(WRITE "${CMAKE_BINARY_DIR}/options-pinned.h" "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
install(FILES "${CMAKE_BINARY_DIR}/options-pinned.h"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/absl/base
+6
View File
@@ -0,0 +1,6 @@
monorail: {
component: "Internals>Core"
}
buganizer_public: {
component_id: 1456128
}
+14 -18
View File
@@ -16,31 +16,27 @@
module(
name = "abseil-cpp",
version = "20250127.1",
version = "head",
compatibility_level = 1,
)
cc_configure = use_extension("@rules_cc//cc:extensions.bzl",
"cc_configure_extension",
dev_dependency = True)
cc_configure = use_extension("@bazel_tools//tools/cpp:cc_configure.bzl", "cc_configure_extension")
use_repo(cc_configure, "local_config_cc")
# Only direct dependencies need to be listed below.
# Please keep the versions in sync with the versions in the WORKSPACE file.
bazel_dep(name = "rules_cc", version = "0.0.17")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "bazel_skylib",
version = "1.5.0")
bazel_dep(
name = "google_benchmark",
version = "1.8.5",
dev_dependency = True,
)
bazel_dep(name = "google_benchmark",
version = "1.8.3",
repo_name = "com_github_google_benchmark",
dev_dependency = True)
# Note: Googletest is NOT a dev_dependency. Some Abseil test utilities
# intended to be used by Abseil users depend on GoogleTest.
bazel_dep(
name = "googletest",
version = "1.15.2",
)
bazel_dep(name = "googletest",
version = "1.14.0.bcr.1",
repo_name = "com_google_googletest")
bazel_dep(name = "platforms",
version = "0.0.8")
+5
View File
@@ -0,0 +1,5 @@
danilchap@chromium.org
mbonadei@chromium.org
# For .def files updates:
hans@chromium.org
+43
View File
@@ -0,0 +1,43 @@
Name: Abseil
Short Name: absl
URL: https://github.com/abseil/abseil-cpp
License: Apache 2.0
License File: LICENSE
Version: N/A
Revision: eba8db7baf6c326870f28e58977075b7b2fb243d
Security Critical: yes
Shipped: yes
Description:
This directory contains the source code of Abseil for C++. This can be used by
Chromium, subject to the guidance at
https://chromium.googlesource.com/chromium/src/+/main/styleguide/c++/c++-features.md;
it can be used without restriction by Chromium's dependencies, except that
objects compiled into Chromium itself cannot use anything relying on
absl::base_internal::FastTypeId (see https://crbug.com/1096380).
How to update Abseil:
1. From your source root run third_party/abseil-cpp/roll_abseil.py to download the code from the Abseil git repository and apply chromium specific patches.
2. Update BUILD.gn files to match changes in BUILD.bazel
If there is a new test, add dependcy on it from absl_tests in the
third_party/abseil-cpp/BUILD.gn target
3. From your source root run third_party/abseil-cpp/generate_def_files.py to
regenerate Windows symbol definition files.
4. If abseil adds new features, update styleguide/c++/c++-features.md and add headers for that feature into exclude list in root DEPS
Local Modifications:
* absl/copts.bzl has been translated to //third_party/absl-cpp/BUILD.gn. Both
files contain lists of compiler flags in order to reduce duplication.
* All the BUILD.bazel files have been translated to BUILD.gn files.
* Patches from //third_party/abseil-cpp/patches have been applied.
* Increment this number to silence presubmits about modifying files in
third_party when regenerating absl .def files: 2
+20 -37
View File
@@ -14,63 +14,46 @@
# limitations under the License.
#
workspace(name = "abseil-cpp")
workspace(name = "com_google_absl")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# GoogleTest/GoogleMock framework. Used by most unit-tests.
http_archive(
name = "googletest",
sha256 = "7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926",
strip_prefix = "googletest-1.15.2",
# Keep this URL in sync with the version in ci/cmake_common.sh and
# ci/windows_msvc_cmake.bat.
urls = ["https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz"],
# Now that Abseil is using the canonical names from the Bazel Central Registry, map
# GoogleTest's old names to the new canonical names.
repo_mapping = {
"@com_google_absl": "@",
"@com_googlesource_code_re2": "@re2",
},
name = "com_google_googletest",
sha256 = "8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7",
strip_prefix = "googletest-1.14.0",
# Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh and
# ci/windows_msvc_cmake.bat.
urls = ["https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz"],
)
# RE2 (the regular expression library used by GoogleTest)
http_archive(
name = "re2",
sha256 = "eb2df807c781601c14a260a507a5bb4509be1ee626024cb45acbd57cb9d4032b",
strip_prefix = "re2-2024-07-02",
urls = ["https://github.com/google/re2/releases/download/2024-07-02/re2-2024-07-02.tar.gz"],
name = "com_googlesource_code_re2",
sha256 = "828341ad08524618a626167bd320b0c2acc97bd1c28eff693a9ea33a7ed2a85f",
strip_prefix = "re2-2023-11-01",
urls = ["https://github.com/google/re2/releases/download/2023-11-01/re2-2023-11-01.zip"],
)
# Google benchmark.
http_archive(
name = "google_benchmark",
sha256 = "d26789a2b46d8808a48a4556ee58ccc7c497fcd4c0af9b90197674a81e04798a",
strip_prefix = "benchmark-1.8.5",
urls = ["https://github.com/google/benchmark/archive/refs/tags/v1.8.5.tar.gz"],
name = "com_github_google_benchmark",
sha256 = "6bc180a57d23d4d9515519f92b0c83d61b05b5bab188961f36ac7b06b0d9e9ce",
strip_prefix = "benchmark-1.8.3",
urls = ["https://github.com/google/benchmark/archive/refs/tags/v1.8.3.tar.gz"],
)
# Bazel Skylib.
http_archive(
name = "bazel_skylib",
sha256 = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f",
urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz"],
)
# C++ rules for Bazel
http_archive(
name = "rules_cc",
urls = ["https://github.com/bazelbuild/rules_cc/releases/download/0.1.0/rules_cc-0.1.0.tar.gz"],
sha256 = "4b12149a041ddfb8306a8fd0e904e39d673552ce82e4296e96fac9cbf0780e59",
strip_prefix = "rules_cc-0.1.0",
name = "bazel_skylib",
sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"],
)
# Bazel platform rules.
http_archive(
name = "platforms",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
"https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
],
sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee",
sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74",
urls = ["https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz"],
)
+128
View File
@@ -0,0 +1,128 @@
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This file contains the definition of the template absl_source_set which
# should be the only type of target needed in abseil's BUILD.gn files.
# This template will correctly set "configs" and "public_configs" in order
# to correctly compile abseil in Chromium.
#
# Usage:
# Most of the times its usage will be similar to the example below but all
# the arguments avilable in source_set are also available for absl_source_set.
#
# absl_source_set("foo") {
# sources = [ "foo.cc" ]
# public = [ "foo.h" ]
# deps = [ ":bar" ]
# }
import("//build_overrides/build.gni")
declare_args() {
absl_build_tests = build_with_chromium
}
template("absl_source_set") {
source_set(target_name) {
if (defined(invoker.testonly) && invoker.testonly && !absl_build_tests) {
not_needed(invoker, "*")
} else {
forward_variables_from(invoker, "*")
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
"//build/config/compiler:no_chromium_code",
"//build/config/compiler:prevent_unsafe_narrowing",
"//third_party/abseil-cpp:absl_default_cflags_cc",
"//third_party/abseil-cpp:absl_define_config",
]
if (is_component_build) {
defines = [ "ABSL_BUILD_DLL" ]
if (!is_win && current_os != "aix") {
configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
configs += [ "//build/config/gcc:symbol_visibility_default" ]
}
}
if (!defined(public_configs)) {
public_configs = []
}
public_configs += [
"//third_party/abseil-cpp:absl_include_config",
"//third_party/abseil-cpp:absl_public_cflags_cc",
]
if (!defined(visibility)) {
# Within Chromium builds, restrict direct visibility of Abseil sources, so
# users must depend on //third_party/abseil-cpp:absl. This prevents use of
# banned targets like absl/types:any. A few targets require exceptions.
# TODO(crbug.com/1096380): Consider replacing build_with_chromium with
# is_component_build for a narrower, more accurate condition.
if (build_with_chromium) {
visibility = [
# Abseil itself.
"//third_party/abseil-cpp/*",
# GTest. It unconditionally #includes any.h if pretty-print support
# for absl types is enabled.
"//third_party/googletest/*",
# WebRTC binary to run PSNR and SSIM video quality analysis. It
# statically links absl and it is used by "browser_tests" when
# is_component_build=false but it cannot depend on the absl
# component because it uses absl/flags.
"//third_party/webrtc/rtc_tools:frame_analyzer",
# WebRTC binaries used by //:chromium_builder_asan. They both
# statically link absl (because they depend on absl/flags) and are
# used by Chromium only when is_component_build=false.
"//third_party/webrtc/rtc_tools:rtp_generator",
"//third_party/webrtc/rtc_tools:video_replay",
# Not used by Chromium directly.
"//chromecast/internal/*",
"//libassistant/*",
# Not built into Chrome.
"//components/optimization_guide/internal/*",
]
} else {
visibility = [ "*" ]
}
}
}
}
}
template("absl_test") {
source_set(target_name) {
if (!absl_build_tests) {
not_needed(invoker, "*")
} else {
forward_variables_from(invoker, "*")
testonly = true
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
"//build/config/compiler:no_chromium_code",
"//third_party/abseil-cpp:absl_default_cflags_cc",
"//third_party/abseil-cpp:absl_define_config",
"//third_party/abseil-cpp:absl_test_config",
]
if (!defined(public_configs)) {
public_configs = []
}
public_configs += [
"//third_party/abseil-cpp:absl_include_config",
"//third_party/abseil-cpp:absl_public_cflags_cc",
]
visibility = [ "//third_party/abseil-cpp/:*" ]
deps += [
"//third_party/googletest:gmock",
"//third_party/googletest:gtest",
]
}
}
}
+91 -2
View File
@@ -28,6 +28,14 @@ config_setting(
visibility = [":__subpackages__"],
)
config_setting(
name = "gcc_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "gcc",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "mingw_unspecified_compiler",
flag_values = {
@@ -45,10 +53,91 @@ config_setting(
)
config_setting(
name = "fuchsia",
name = "msvc_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "clang-cl_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "clang-cl",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "osx",
constraint_values = [
"@platforms//os:fuchsia",
"@platforms//os:osx",
],
)
config_setting(
name = "ios",
constraint_values = [
"@platforms//os:ios",
],
)
config_setting(
name = "ppc",
values = {
"cpu": "ppc",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "cpu_wasm",
values = {
"cpu": "wasm",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "cpu_wasm32",
values = {
"cpu": "wasm32",
},
visibility = [":__subpackages__"],
)
config_setting(
name = "platforms_wasm32",
constraint_values = [
"@platforms//cpu:wasm32",
],
visibility = [":__subpackages__"],
)
config_setting(
name = "platforms_wasm64",
constraint_values = [
"@platforms//cpu:wasm64",
],
visibility = [":__subpackages__"],
)
selects.config_setting_group(
name = "wasm",
match_any = [
":cpu_wasm",
":cpu_wasm32",
":platforms_wasm32",
":platforms_wasm64",
],
visibility = [":__subpackages__"],
)
config_setting(
name = "fuchsia",
values = {
"cpu": "fuchsia",
},
visibility = [":__subpackages__"],
)
+2 -2
View File
@@ -36,9 +36,9 @@ add_subdirectory(time)
add_subdirectory(types)
add_subdirectory(utility)
if (ABSL_BUILD_DLL)
if (${ABSL_BUILD_DLL})
absl_make_dll()
if ((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS)
if (${ABSL_BUILD_TEST_HELPERS})
absl_make_dll(TEST ON)
endif()
endif()
+4 -6
View File
@@ -51,8 +51,8 @@ cc_test(
deps = [
":algorithm",
"//absl/base:config",
"@googletest//:gtest",
"@googletest//:gtest_main",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -65,7 +65,6 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":algorithm",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:nullability",
"//absl/meta:type_traits",
@@ -80,11 +79,10 @@ cc_test(
deps = [
":container",
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/memory",
"//absl/types:span",
"@googletest//:gtest",
"@googletest//:gtest_main",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
+39
View File
@@ -0,0 +1,39 @@
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/abseil-cpp/absl.gni")
absl_source_set("algorithm") {
public = [ "algorithm.h" ]
deps = [ "//third_party/abseil-cpp/absl/base:config" ]
}
absl_source_set("container") {
public = [ "container.h" ]
deps = [
":algorithm",
"//third_party/abseil-cpp/absl/base:nullability",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/meta:type_traits",
]
}
absl_test("algorithm_test") {
sources = [ "algorithm_test.cc" ]
deps = [
":algorithm",
"//third_party/abseil-cpp/absl/base:config",
]
}
absl_test("container_test") {
sources = [ "container_test.cc" ]
deps = [
":container",
"//third_party/abseil-cpp/absl/base",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/types:span",
]
}
-2
View File
@@ -48,7 +48,6 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::algorithm
absl::config
absl::core_headers
absl::meta
absl::nullability
@@ -65,7 +64,6 @@ absl_cc_test(
DEPS
absl::algorithm_container
absl::base
absl::config
absl::core_headers
absl::memory
absl::span

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