Compare commits

..
9332 changed files with 2043021 additions and 345816 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
name: build-android
# We intentially use a larger runner here to enable larger disk space
# (standard linux runner will fail on disk space and faster build time).
runs-on: 'ubuntu-24.04-32core'
runs-on: ubuntu-22.04-32core
steps:
- uses: actions/checkout@v4.1.6
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-linux:
name: build-linux
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
+4 -4
View File
@@ -40,7 +40,7 @@ jobs:
build-android:
name: build-android
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
@@ -69,7 +69,7 @@ jobs:
build-web:
name: build-web
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
@@ -79,7 +79,7 @@ jobs:
validate-docs:
name: validate-docs
runs-on: 'ubuntu-24.04-4core'
runs-on: ubuntu-22.04-4core
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -114,7 +114,7 @@ jobs:
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'ubuntu-24.04-8core'
runs-on: ubuntu-22.04-8core
steps:
- uses: actions/checkout@v4.1.6
+2 -2
View File
@@ -70,7 +70,7 @@ jobs:
build-web:
name: build-web
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
steps:
@@ -103,7 +103,7 @@ jobs:
build-android:
name: build-android
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
steps:
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-web:
name: build-web
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
-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 -1
View File
@@ -8,4 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Fix build/compile errors when upgrading to MacOS 15.4
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.59.0'
implementation 'com.google.android.filament:filament-android:1.58.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.0'
pod 'Filament', '~> 1.58.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.1
## v1.59.0
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
## v1.58.2
- engine: Generate 1D instead of 3D LUTs for color grading whenever possible.
@@ -143,15 +143,6 @@ abstract class MaterialCompiler extends TaskWithBinary {
matcArgs += ['-a', 'vulkan']
}
def include_webgpu = providers
.gradleProperty("com.google.android.filament.include-webgpu")
.forUseAtConfigurationTime().present
if (!include_webgpu) {
matcArgs += ['-a', 'webgpu']
}
def mat_no_opt = providers
.gradleProperty("com.google.android.filament.matnopt")
.forUseAtConfigurationTime().present
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.59.0
VERSION_NAME=1.58.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
File diff suppressed because one or more lines are too long
Binary file not shown.
+1 -15
View File
@@ -233,8 +233,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h
src/vulkan/VulkanYcbcrConversionCache.cpp
src/vulkan/VulkanYcbcrConversionCache.h
)
if (LINUX OR WIN32)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
@@ -255,10 +253,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUConstants.h
src/webgpu/WebGPUDriver.cpp
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
)
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
@@ -308,11 +302,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.
# ==================================================================================================
@@ -505,9 +494,7 @@ if (APPLE OR LINUX)
test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp
test/Arguments.cpp
test/ImageExpectations.cpp
test/Lifetimes.cpp
test/Shader.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp
@@ -523,7 +510,6 @@ if (APPLE OR LINUX)
test/test_Handles.cpp
)
set(BACKEND_TEST_LIBS
absl::str_format
backend
getopt
gtest
@@ -539,7 +525,7 @@ if (APPLE AND NOT IOS)
list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
set(BACKEND_TEST_DEPS
OSDependent
@@ -361,7 +361,8 @@ public:
VkImageUsageFlags usage);
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat);
SamplerParams sampler,
uint32_t internalFormat);
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
@@ -40,11 +40,9 @@
#include <stddef.h>
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
// TODO WebGPU examine right size of handles
#define HandleAllocatorWGPU HandleAllocator<64, 160, 552> // ~1820 / pool / MiB
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
namespace filament::backend {
-4
View File
@@ -211,8 +211,4 @@ template class HandleAllocatorVK;
template class HandleAllocatorMTL;
#endif
#if defined (FILAMENT_SUPPORTS_WEBGPU)
template class HandleAllocatorWGPU;
#endif
} // namespace filament::backend
+3 -11
View File
@@ -1177,17 +1177,9 @@ size_t MetalDriver::getMaxUniformBufferSize() {
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
}
size_t MetalDriver::getMaxTextureSize(SamplerType type) {
if (type == SamplerType::SAMPLER_3D) {
return 2048;
}
if (mContext->highestSupportedGpuFamily.apple >= 3 ||
mContext->highestSupportedGpuFamily.mac >= 2) {
return 16384;
} else if (mContext->highestSupportedGpuFamily.apple >= 2) {
return 8192;
}
return 4096;
size_t MetalDriver::getMaxTextureSize(SamplerType) {
// TODO: return the actual size instead of hardcoding the minspec
return 2048;
}
size_t MetalDriver::getMaxArrayTextureLayers() {
+1 -3
View File
@@ -241,9 +241,7 @@ size_t NoopDriver::getMaxUniformBufferSize() {
}
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
// NoopDriver is being actively used for other purposes. This needs to be resolved before we
// can change it to 2048. b/406832484
return 16384u;
return 4096u;
}
size_t NoopDriver::getMaxArrayTextureLayers() {
+3 -18
View File
@@ -566,11 +566,7 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "AMD") ||
strstr(renderer, "ATI")) {
// AMD/ATI GPU
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
}
if (strstr(vendor, "Mesa")) {
} else if (strstr(vendor, "Mesa")) {
// Seen on
// [Mesa],
// [llvmpipe (LLVM 17.0.6, 256 bits)],
@@ -578,6 +574,8 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
// [4.50]
// not known which version are affected
bugs->rebind_buffer_after_deletion = true;
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
}
} else {
// When running under ANGLE, it's a different set of workaround that we need.
@@ -589,19 +587,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
}
}
if (strstr(vendor, "Mozilla")) {
// Seen on
// [Mozilla],
// [GeForce GTX 980, or similar]
// or [ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar]
// or anything else,
// [OpenGL ES 3.0 (WebGL 2.0)],
// [OpenGL ES GLSL ES 3.00 (WebGL GLSL ES 3.00)]
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
// ANGLE or not. (b/376125497)
bugs->rebind_buffer_after_deletion = true;
}
#ifdef BACKEND_OPENGL_VERSION_GLES
# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x
if (UTILS_UNLIKELY(major == 2)) {
+2 -2
View File
@@ -320,8 +320,8 @@ public:
// a glFinish. So we must delay the destruction until we know the GPU is finished.
bool delay_fbo_destruction;
// Mesa and Mozilla(web) sometimes clear the generic buffer binding when *another* buffer
// is destroyed, if that other buffer is bound on an *indexed* buffer binding.
// Mesa sometimes clears the generic buffer binding when *another* buffer is destroyed,
// if that other buffer is bound on an *indexed* buffer binding.
bool rebind_buffer_after_deletion;
// Force feature level 0. Typically used for low end ES3 devices with significant driver
@@ -17,6 +17,9 @@
#include "VulkanBlitter.h"
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanFboCache.h"
#include "VulkanHandles.h"
#include "VulkanSamplerCache.h"
#include "VulkanTexture.h"
#include "vulkan/utils/Image.h"
@@ -24,6 +24,13 @@
namespace filament::backend {
class VulkanBuffer;
class VulkanFboCache;
class VulkanPipelineCache;
class VulkanSamplerCache;
struct VulkanProgram;
class VulkanBlitter {
public:
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
@@ -56,9 +56,9 @@ VkImageView VulkanAttachment::getImageView() {
assert_invariant(texture);
VkImageSubresourceRange range = getSubresourceRange();
if (range.layerCount > 1) {
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY);
return texture->getMultiviewAttachmentView(range);
}
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D);
return texture->getAttachmentView(range);
}
bool VulkanAttachment::isDepth() const {
@@ -68,11 +68,11 @@ bool VulkanAttachment::isDepth() const {
VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
assert_invariant(texture);
return {
.aspectMask = texture->getImageAspect(),
.baseMipLevel = uint32_t(level),
.levelCount = 1,
.baseArrayLayer = uint32_t(layer),
.layerCount = layerCount,
.aspectMask = texture->getImageAspect(),
.baseMipLevel = uint32_t(level),
.levelCount = 1,
.baseArrayLayer = uint32_t(layer),
.layerCount = layerCount,
};
}
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
#define TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
#include "VulkanConstants.h"
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Definitions.h"
@@ -29,6 +30,8 @@
#include <bluevk/BlueVK.h>
#include <memory>
VK_DEFINE_HANDLE(VmaAllocator)
VK_DEFINE_HANDLE(VmaPool)
@@ -260,6 +260,7 @@ VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
void VulkanDescriptorSetCache::terminate() noexcept{
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
@@ -329,6 +330,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
}
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->vkSet,
.dstBinding = binding,
.descriptorCount = 1,
@@ -344,19 +346,20 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
.sampler = sampler,
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
};
VkImageSubresourceRange range = texture->getPrimaryViewRange();
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
VkImageViewType const expectedType = texture->getViewType();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
// an attachment, then the range for this view has exactly one level and one layer.
range.levelCount = 1;
range.layerCount = 1;
// an attachment, then the sampler for this texture has the same view properties as a
// view for an attachment. Therefore, we can use getAttachmentView to get a
// corresponding VkImageView.
info.imageView = texture->getAttachmentView(range);
} else {
info.imageView = texture->getViewForType(range, expectedType);
}
info.imageView = texture->getView(range);
info.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout());
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
@@ -392,7 +395,7 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
});
}
void VulkanDescriptorSetCache::gc() {
void VulkanDescriptorSetCache::clearHistory() {
mStashedSets = {};
}
@@ -68,7 +68,7 @@ public:
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void gc();
void clearHistory();
private:
class DescriptorInfinitePool;
+20 -35
View File
@@ -25,12 +25,10 @@
#include "VulkanHandles.h"
#include "VulkanMemory.h"
#include "VulkanTexture.h"
#include "vulkan/VulkanSamplerCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h"
#include "vulkan/utils/Definitions.h"
#include "vulkan/vulkan_core.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
@@ -214,7 +212,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mPipelineCache(mPlatform->getDevice()),
mStagePool(mAllocator, &mCommands),
mFramebufferCache(mPlatform->getDevice()),
mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
@@ -366,7 +363,7 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE();
// Command buffers need to be submitted and completed before other resources can be gc'd.
mCommands.gc();
mDescriptorSetCache.gc();
mDescriptorSetCache.clearHistory();
mStagePool.gc();
mFramebufferCache.gc();
mPipelineCache.gc();
@@ -421,11 +418,7 @@ 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);
VkSampler const vksampler = mSamplerCache.getSampler(params);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
}
@@ -563,7 +556,7 @@ 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);
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
}
@@ -573,7 +566,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
if (any(usage & (TextureUsage::BLIT_DST & TextureUsage::UPLOADABLE))) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
@@ -589,15 +582,13 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
<< "failed to find a valid memory type for external image memory.";
VkImage vkimg;
VkDeviceMemory deviceMemory;
std::tie(vkimg, deviceMemory) =
const auto& data =
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
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);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
metadata.samples, metadata.width, metadata.height, metadata.layerCount, usage,
mStagePool);
texture.inc();
}
@@ -606,14 +597,13 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
FVK_SYSTRACE_SCOPE();
assert_invariant(false && "Not supported in Vulkan backend");
// not supported in this backend
}
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
assert_invariant(false && "Not supported in Vulkan backend");
}
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
@@ -621,7 +611,6 @@ void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
// not supported in this backend
assert_invariant(false && "Not supported in Vulkan backend");
}
void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
@@ -1145,28 +1134,24 @@ math::float2 VulkanDriver::getClipSpaceParams() {
}
uint8_t VulkanDriver::getMaxDrawBuffers() {
return mContext.getPhysicalDeviceLimits().maxColorAttachments;
return MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; // TODO: query real value
}
size_t VulkanDriver::getMaxUniformBufferSize() {
return mContext.getPhysicalDeviceLimits().maxUniformBufferRange;
// TODO: return the actual size instead of hardcoded value
// TODO: devices that return less than 32768 should be rejected. This represents only 3%
// of android devices.
return 32768;
}
size_t VulkanDriver::getMaxTextureSize(SamplerType type) {
switch (type) {
case SamplerType::SAMPLER_2D:
return mContext.getPhysicalDeviceLimits().maxImageDimension2D;
case SamplerType::SAMPLER_3D:
return mContext.getPhysicalDeviceLimits().maxImageDimension3D;
case SamplerType::SAMPLER_CUBEMAP:
return mContext.getPhysicalDeviceLimits().maxImageDimensionCube;
default:
return mContext.getPhysicalDeviceLimits().maxImageDimension1D;
}
size_t VulkanDriver::getMaxTextureSize(SamplerType) {
// TODO: return the actual size instead of hardcoded value
return 4096;
}
size_t VulkanDriver::getMaxArrayTextureLayers() {
return mContext.getPhysicalDeviceLimits().maxImageArrayLayers;
// TODO: return the actual size instead of hardcoded value
return 256;
}
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
@@ -27,7 +27,6 @@
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanQueryManager.h"
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
@@ -136,7 +135,6 @@ private:
VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool;
VulkanFboCache mFramebufferCache;
VulkanYcbcrConversionCache mYcbcrConversionCache;
VulkanSamplerCache mSamplerCache;
VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels;
@@ -18,18 +18,24 @@
#define TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
#include "VulkanCommands.h"
#include "VulkanMemory.h"
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include "backend/Program.h"
#include <bluevk/BlueVK.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/Hash.h>
#include <list>
#include <tsl/robin_map.h>
#include <type_traits>
#include <vector>
#include <unordered_map>
namespace filament::backend {
@@ -14,9 +14,7 @@
* limitations under the License.
*/
#include "VulkanSamplerCache.h"
#include "VulkanConstants.h"
#include "vulkan/VulkanSamplerCache.h"
#include "vulkan/utils/Conversion.h"
#include <utils/Panic.h>
@@ -28,40 +26,41 @@ namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;
}
auto const& samplerParams = params.sampler;
VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
.addressModeU = fvkutils::getWrapMode(samplerParams.wrapS),
.addressModeV = fvkutils::getWrapMode(samplerParams.wrapT),
.addressModeW = fvkutils::getWrapMode(samplerParams.wrapR),
.anisotropyEnable = samplerParams.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float) (1u << samplerParams.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(samplerParams.compareMode),
.compareOp = fvkutils::getCompareOp(samplerParams.compareFunc),
VkSamplerCreateInfo samplerInfo {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.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(samplerParams.filterMin),
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE };
.unnormalizedCoordinates = VK_FALSE
};
VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, sampler });
mCache.insert({params, sampler});
return sampler;
}
void VulkanSamplerCache::terminate() noexcept {
for (auto pair: mCache) {
for (auto pair : mCache) {
vkDestroySampler(mDevice, pair.second, VKALLOC);
}
mCache.clear();
}
}// namespace filament::backend
} // namespace filament::backend
@@ -17,11 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#include <backend/DriverEnums.h>
#include "VulkanContext.h"
#include <utils/Hash.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
namespace filament::backend {
@@ -29,28 +26,12 @@ namespace filament::backend {
// Simple manager for VkSampler objects.
class VulkanSamplerCache {
public:
struct Params {
SamplerParams sampler = {};
uint32_t padding = 0;
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
};
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(Params params) noexcept;
VkSampler getSampler(SamplerParams params) noexcept;
void terminate() noexcept;
private:
VkDevice mDevice;
struct SamplerEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
SamplerParams::EqualTo equal;
return equal(lhs.sampler, rhs.sampler) && lhs.conversion == rhs.conversion;
}
};
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
tsl::robin_map<SamplerParams, VkSampler, SamplerParams::Hasher, SamplerParams::EqualTo> mCache;
};
} // namespace filament::backend
@@ -32,7 +32,6 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
VkExtent2D extent)
: mPlatform(platform),
mContext(context),
mResourceManager(resourceManager),
mCommands(commands),
mAllocator(allocator),
@@ -78,16 +77,16 @@ void VulkanSwapChain::update() {
}
for (auto const color: bundle.colors) {
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
mContext, device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
bundle.colorFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
bundle.extent.height, bundle.layerCount, colorUsage, mStagePool);
device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, colorUsage,
mStagePool);
mColors.push_back(colorTexture);
}
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, mContext, device,
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
bundle.depthFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
bundle.extent.height, bundle.layerCount, depthUsage, mStagePool);
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device,
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, depthUsage,
mStagePool);
mExtent = bundle.extent;
mLayerCount = bundle.layerCount;
@@ -19,7 +19,6 @@
#include "DriverBase.h"
#include "VulkanConstants.h"
#include "VulkanContext.h"
#include "VulkanTexture.h"
#include "vulkan/memory/Resource.h"
@@ -29,6 +28,7 @@
#include <bluevk/BlueVK.h>
#include <utils/FixedCapacityVector.h>
#include <memory>
using namespace bluevk;
@@ -83,7 +83,6 @@ private:
void update();
VulkanPlatform* mPlatform;
VulkanContext const& mContext;
fvkmemory::ResourceManager* mResourceManager;
VulkanCommands* mCommands;
VmaAllocator mAllocator;
+168 -209
View File
@@ -15,7 +15,6 @@
*/
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanMemory.h"
#include "VulkanTexture.h"
#include "vulkan/memory/ResourcePointer.h"
@@ -55,6 +54,7 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A,
};
auto const compose = [](VkComponentMapping const& prev,
VkComponentMapping const& next) -> VkComponentMapping {
VkComponentSwizzle vals[4] = { next.r, next.g, next.b, next.a };
@@ -151,152 +151,35 @@ uint8_t getLayerCountFromDepth(uint32_t const depth) {
return getLayerCount(getSamplerTypeFromDepth(depth), depth);
}
VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
VkPhysicalDevice physicalDevice, VkFormat vkFormat, TextureUsage tusage) {
VkImageUsageFlags usage = {};
if (any(tusage & TextureUsage::BLIT_SRC)) {
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(tusage & TextureUsage::BLIT_DST)) {
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
// Determine if we can use the transient usage flag combined with lazily allocated memory.
const bool useTransientAttachment =
// Lazily allocated memory is available.
context.isLazilyAllocatedMemorySupported() &&
// Usage consists of attachment flags only.
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
// Depth resolve cannot use transient attachment because it uses a custom shader.
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
// restriction.
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
// and above to be able to support stencil resolve (along with depth).
!(any(tusage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
const VkImageUsageFlags transientFlag =
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
if (any(tusage & TextureUsage::SAMPLEABLE)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
if (physicalDevice != VK_NULL_HANDLE) {
// Validate that the format is actually sampleable.
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
}
#endif
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(tusage & TextureUsage::COLOR_ATTACHMENT)) {
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
if (any(tusage & TextureUsage::SUBPASS_INPUT)) {
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
}
if (any(tusage & TextureUsage::STENCIL_ATTACHMENT)) {
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
}
if (any(tusage & TextureUsage::UPLOADABLE)) {
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(tusage & TextureUsage::DEPTH_ATTACHMENT)) {
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
if (samples > 1) {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
}
return usage;
}
} // anonymous namespace
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands,
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory,
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat, VkImageUsageFlags usage,
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
bool isProtected)
: mStagePool(stagePool),
mCommands(commands),
mAllocator(allocator),
mDevice(device),
mTextureImage(image),
mTextureImageMemory(deviceMemory),
mVkFormat(format),
: mVkFormat(format),
mViewType(viewType),
mFullViewRange{ fvkutils::getImageAspect(format), 0, levels, 0, layerCount },
mYcbcr{ ycbcrConversion, isExternalFormat },
mDefaultLayout(getDefaultLayoutImpl(usage)),
mUsage(usage),
mIsProtected(isProtected) {}
mFullViewRange{fvkutils::getImageAspect(format), 0, levels, 0, layerCount},
mDefaultLayout(defaultLayout),
mIsProtected(isProtected),
mStagePool(stagePool),
mDevice(device),
mAllocator(allocator),
mCommands(commands),
mIsTransientAttachment(false) {}
VulkanTextureState::~VulkanTextureState() {
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
clearCachedImageViews();
}
void VulkanTextureState::clearCachedImageViews() noexcept {
for (auto entry: mCachedImageViews) {
vkDestroyImageView(mDevice, entry.second, VKALLOC);
}
mCachedImageViews.clear();
}
VkImageView VulkanTextureState::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
ImageViewKey const key{ range, viewType, swizzle };
if (auto iter = mCachedImageViews.find(key); iter != mCachedImageViews.end()) {
return iter->second;
}
VkSamplerYcbcrConversionInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = mYcbcr.conversion,
};
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = mYcbcr.conversion != VK_NULL_HANDLE ? &conversionInfo : nullptr,
.flags = 0,
.image = mTextureImage,
.viewType = viewType,
.format = mYcbcr.isExternalFormat ? VK_FORMAT_UNDEFINED : mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
vkCreateImageView(mDevice, &viewInfo, VKALLOC, &imageView);
mCachedImageViews.emplace(key, imageView);
return imageView;
}
// Constructor for internally passed VkImage - including swapchain images and external images.
VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
// Constructor for internally passed VkImage
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage tusage,
VulkanStagePool& stagePool)
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth,
TextureFormat::UNUSED, tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
commands, allocator, device, image, memory, format,
fvkutils::getViewType(SamplerType::SAMPLER_2D),
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion,
/*isExternalFormat=*/false,
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
any(usage & TextureUsage::PROTECTED))) {
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width,
uint32_t height, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, TextureFormat::UNUSED,
tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, format, fvkutils::getViewType(SamplerType::SAMPLER_2D),
/*mipLevels=*/1, getLayerCountFromDepth(depth), getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
mState->mTextureImage = image;
mState->mTextureImageMemory = memory;
mPrimaryViewRange = mState->mFullViewRange;
}
@@ -306,19 +189,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage) {
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, fvkutils::getVkFormat(tformat),
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
VkFormat const vkFormat = fvkutils::getVkFormat(tformat);
bool const isProtected = any(tusage & TextureUsage::PROTECTED);
VkImageCreateInfo imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
.format = vkFormat,
.format = mState->mVkFormat,
.extent = {w, h, depth},
.mipLevels = levels,
.arrayLayers = 1,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = getUsage(context, samples, physicalDevice, vkFormat, tusage),
.usage = 0,
};
if (target == SamplerType::SAMPLER_3D && any(tusage & TextureUsage::ALL_ATTACHMENTS)) {
if (context.isImageView2DOn3DImageSupported()) {
@@ -348,17 +233,79 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.arrayLayers = depth * 6;
imageInfo.extent.depth = 1;
}
if (isProtected) {
if (any(usage & TextureUsage::PROTECTED)) {
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
}
if (any(usage & TextureUsage::BLIT_SRC)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & TextureUsage::BLIT_DST)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
// Determine if we can use the transient usage flag combined with lazily allocated memory.
const bool useTransientAttachment =
// Lazily allocated memory is available.
context.isLazilyAllocatedMemorySupported() &&
// Usage consists of attachment flags only.
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
// Depth resolve cannot use transient attachment because it uses a custom shader.
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
// restriction.
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
// and above to be able to support stencil resolve (along with depth).
!(any(usage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
mState->mIsTransientAttachment = useTransientAttachment;
const VkImageUsageFlags transientFlag =
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
if (any(usage & TextureUsage::SAMPLEABLE)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
// Validate that the format is actually sampleable.
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, mState->mVkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
#endif
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
}
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
}
if (any(usage & TextureUsage::UPLOADABLE)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
if (samples > 1) {
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
}
// Constrain the sample count according to the sample count masks in VkPhysicalDeviceProperties.
// Note that VulkanRenderTarget holds a single MSAA count, so we play it safe if this is used as
// any kind of attachment (color or depth).
auto const& limits = context.getPhysicalDeviceLimits();
const auto& limits = context.getPhysicalDeviceLimits();
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
samples = fvkutils::reduceSampleCount(samples,
fvkutils::isVkDepthFormat(vkFormat)
fvkutils::isVkDepthFormat(mState->mVkFormat)
? limits.sampledImageDepthSampleCounts
: limits.sampledImageColorSampleCounts);
}
@@ -372,36 +319,33 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
this->samples = samples;
imageInfo.samples = (VkSampleCountFlagBits) samples;
VkImage textureImage;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &textureImage);
VkResult result = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
if (result != VK_SUCCESS || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
FVK_LOGD << "vkCreateImage: "
<< "image = " << textureImage << ", "
<< "image = " << mState->mTextureImage << ", "
<< "result = " << result << ", "
<< "handle = " << utils::io::hex << textureImage << utils::io::dec << ", "
<< "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
<< "extent = " << w << "x" << h << "x"<< depth << ", "
<< "mipLevels = " << int(levels) << ", "
<< "TextureUsage = " << static_cast<int>(tusage) << ", "
<< "TextureUsage = " << static_cast<int>(usage) << ", "
<< "usage = " << imageInfo.usage << ", "
<< "samples = " << imageInfo.samples << ", "
<< "type = " << imageInfo.imageType << ", "
<< "flags = " << imageInfo.flags << ", "
<< "target = " << static_cast<int>(target) <<", "
<< "format = " << vkFormat << utils::io::endl;
<< "format = " << mState->mVkFormat << utils::io::endl;
}
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image."
<< " error=" << static_cast<int32_t>(result);
// Allocate memory for the VkImage and bind it.
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device, textureImage, &memReqs);
VkMemoryRequirements memReqs = {};
vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
bool const useTransientAttachment = imageInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
VkFlags const requiredMemoryFlags =
const VkFlags requiredMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
@@ -413,22 +357,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
.allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex,
};
VkDeviceMemory textureImageMemory;
result = vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory);
result = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory."
<< " error=" << static_cast<int32_t>(result);
result = vkBindImageMemory(device, textureImage, textureImageMemory, 0);
result = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image."
<< " error=" << static_cast<int32_t>(result);
mState = fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
commands, allocator, device, textureImage, textureImageMemory, vkFormat,
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
VK_NULL_HANDLE /* ycbcrConversion */, false /*isExternalFormat*/, imageInfo.usage,
isProtected);
// Spec out the "primary" VkImageView that shaders use to sample from the image.
mPrimaryViewRange = mState->mFullViewRange;
// Go ahead and create the primary image view.
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
}
// Constructor for creating a texture view
@@ -437,8 +380,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
uint8_t levelCount)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage),
mState(src->mState) {
src->format, src->usage) {
mState = src->mState;
mPrimaryViewRange = src->mPrimaryViewRange;
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
mPrimaryViewRange.levelCount = levelCount;
@@ -449,10 +392,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage),
mState(src->mState),
mPrimaryViewRange(src->mPrimaryViewRange),
mSwizzle(composeSwizzle(src->mSwizzle, swizzle)) {}
src->format, src->usage) {
mState = src->mState;
mPrimaryViewRange = src->mPrimaryViewRange;
mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
}
VulkanTextureState::~VulkanTextureState() {
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
for (auto entry: mCachedImageViews) {
vkDestroyImageView(mDevice, entry.second, VKALLOC);
}
}
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
@@ -559,20 +513,20 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
// TODO: support blit-based format conversion for 3D images and cubemaps.
constexpr int layer = 0;
const int layer = 0;
VkOffset3D const rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
const VkOffset3D rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
VkImageAspectFlags const aspect = getImageAspect();
const VkImageAspectFlags aspect = getImageAspect();
VkImageBlit const blitRegions[1] = {{
const VkImageBlit blitRegions[1] = {{
.srcSubresource = { aspect, 0, 0, 1 },
.srcOffsets = { rect[0], rect[1] },
.dstSubresource = { aspect, uint32_t(miplevel), layer, 1 },
.dstOffsets = { rect[0], rect[1] }
}};
VkImageSubresourceRange const range = { aspect, miplevel, 1, layer, 1 };
const VkImageSubresourceRange range = { aspect, miplevel, 1, layer, 1 };
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
VulkanLayout const oldLayout = getLayout(layer, miplevel);
@@ -588,21 +542,41 @@ VulkanLayout VulkanTexture::getDefaultLayout() const {
return mState->mDefaultLayout;
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type) {
assert_invariant(mState->mYcbcr.conversion == VK_NULL_HANDLE &&
"We are not yet supporting external image as attachments.");
if (type == VK_IMAGE_VIEW_TYPE_2D) {
VkImageSubresourceRange copy = range;
copy.levelCount = 1;
copy.layerCount = 1;
return mState->getImageView(copy, type, {});
} else {
return mState->getImageView(range, type, {});
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
range.levelCount = 1;
range.layerCount = 1;
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
}
VkImageView VulkanTexture::getView(VkImageSubresourceRange const& range) {
return mState->getImageView(range, mState->mViewType, mSwizzle);
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
}
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
return getImageView(range, type, mSwizzle);
}
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
auto iter = mState->mCachedImageViews.find(key);
if (iter != mState->mCachedImageViews.end()) {
return iter->second;
}
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = mState->mTextureImage,
.viewType = viewType,
.format = mState->mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
mState->mCachedImageViews.emplace(key, imageView);
return imageView;
}
VkImageAspectFlags VulkanTexture::getImageAspect() const {
@@ -764,21 +738,6 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
}
}
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternalFormat) {
// Note that this comparison is valid because we only ever create VkSamplerYcbcrConversion from
// a cache. So for each set of parameters, there is exactly one conversion (similar to
// samplers).
VulkanTextureState::Ycbcr ycbcr = {
.conversion = conversion,
.isExternalFormat = isExternalFormat,
};
if (mState->mYcbcr != ycbcr) {
mState->mYcbcr = ycbcr;
mState->clearCachedImageViews();
}
}
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
assert_invariant(level <= 0xffff && layer <= 0xffff);
const uint32_t key = (layer << 16) | level;
+37 -56
View File
@@ -19,10 +19,7 @@
#include "DriverBase.h"
#include "VulkanCommands.h"
#include "VulkanConstants.h"
#include "VulkanMemory.h"
#include "VulkanStagePool.h"
#include "VulkanBuffer.h"
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Image.h"
@@ -34,14 +31,10 @@
namespace filament::backend {
struct VulkanTexture;
struct VulkanTextureState : public fvkmemory::Resource {
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat,
VkImageUsageFlags usage, bool isProtected);
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
~VulkanTextureState();
@@ -64,50 +57,29 @@ struct VulkanTextureState : public fvkmemory::Resource {
// No implicit padding allowed due to it being a hash key.
static_assert(sizeof(ImageViewKey) == 40);
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
private:
void clearCachedImageViews() noexcept;
VulkanStagePool& mStagePool;
VulkanCommands* const mCommands;
VmaAllocator const mAllocator;
VkDevice const mDevice;
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
// The texture with the sidecar owns the sidecar.
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
VkImage const mTextureImage;
VkDeviceMemory const mTextureImageMemory;
VkFormat const mVkFormat;
VkImageViewType const mViewType;
VkImageSubresourceRange const mFullViewRange;
VkImage mTextureImage = VK_NULL_HANDLE;
VulkanLayout mDefaultLayout;
// Note that this parameter is not constant due to the fact that AHB can force a change in the
// conversion matrix per-frame.
struct Ycbcr {
VkSamplerYcbcrConversion conversion;
bool isExternalFormat;
bool operator==(Ycbcr const& other) const {
return conversion == other.conversion && isExternalFormat == other.isExternalFormat;
}
bool operator!=(Ycbcr const& other) const {
return !((*this) == other);
}
} mYcbcr;
VulkanLayout const mDefaultLayout;
VkImageUsageFlags const mUsage;
bool const mIsProtected;
bool mIsProtected = false;
// Track the image layout of each subresource using a sparse range map.
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
friend struct VulkanTexture;
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
VulkanStagePool& mStagePool;
VkDevice mDevice;
VmaAllocator mAllocator;
VulkanCommands* mCommands;
bool mIsTransientAttachment;
};
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
@@ -119,10 +91,10 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
VulkanStagePool& stagePool);
// Specialized constructor for internally created textures (e.g. from a swap chain)
VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
// The texture will never destroy the given VkImage, but it does manages its subresources.
VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool);
// Constructor for creating a texture view for wrt specific mip range
@@ -141,6 +113,11 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel);
// Returns the primary image view, which is used for shader sampling.
VkImageView getPrimaryImageView() {
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
}
VkImageViewType getViewType() const {
return mState->mViewType;
}
@@ -155,11 +132,20 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// time of the call.
VulkanLayout getDefaultLayout() const;
// Gets or creates a cached VkImageView for a subresource that can be used as a render
// target attachment. Unlike the primary image view, this always the identity swizzle.
VkImageView getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type);
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment. Unlike the primary image view, this always has type VK_IMAGE_VIEW_TYPE_2D
// and the identity swizzle.
VkImageView getAttachmentView(VkImageSubresourceRange range);
VkImageView getView(VkImageSubresourceRange const& range);
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment when rendering with multiview.
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
// This is a workaround for the first few frames where we're waiting for the texture to actually
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
// imageView needs to be of the right type. Hence, we provide an option to indicate the
// view type. Swizzle option does not matter in this case.
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
VkFormat getVkFormat() const {
return mState->mVkFormat;
@@ -179,7 +165,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
}
bool isTransientAttachment() const {
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
return mState->mIsTransientAttachment;
}
bool getIsProtected() const {
@@ -206,11 +192,6 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// manually (outside of calls to transitionLayout).
void setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout);
// This is used in the case of external images and external samplers. AHB might update the
// conversion per-frame. This implies that we need to invalidate the view cache when that
// happens.
void setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternal);
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
void print() const;
#endif
@@ -1,82 +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 "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/utils/Conversion.h"
#include <utils/Panic.h>
#if defined(__ANDROID__)
#include <vulkan/vulkan_android.h> // for VkExternalFormatANDROID
#endif
using namespace bluevk;
namespace filament::backend {
VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
: mDevice(device) {}
VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
VulkanYcbcrConversionCache::Params params) noexcept {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;
}
auto const& chroma = params.conversion;
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.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),
};
#if defined(__ANDROID__)
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = params.externalFormat,
};
if (params.externalFormat) {
conversionInfo.pNext = &externalFormat;
}
#endif
VkSamplerYcbcrConversion conversion;
VkResult result =
vkCreateSamplerYcbcrConversion(mDevice, &conversionInfo, nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, conversion });
return conversion;
}
void VulkanYcbcrConversionCache::terminate() noexcept {
for (auto& [param, conv]: mCache) {
vkDestroySamplerYcbcrConversion(mDevice, conv, VKALLOC);
}
mCache.clear();
}
} // namespace filament::backend
@@ -1,59 +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_VULKANYCBCRCONVERSIONCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
#include <backend/DriverEnums.h>
#include <utils/Hash.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
namespace filament::backend {
// Simple manager for VkSamplerYcbcrConversion objects.
class VulkanYcbcrConversionCache {
public:
struct Params {
SamplerYcbcrConversion conversion = {};
uint32_t padding = 0;
uint64_t externalFormat = 0;
};
static_assert(sizeof(Params) == 16);
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params) noexcept;
void terminate() noexcept;
private:
VkDevice mDevice;
struct ConversionEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
SamplerYcbcrConversion::EqualTo equal;
return equal(lhs.conversion, rhs.conversion) &&
lhs.externalFormat == rhs.externalFormat;
}
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
@@ -295,8 +295,9 @@ VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle){
VkExternalFormatANDROID externalFormat = {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = internalFormat,
};
@@ -321,6 +322,7 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, Sampler
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
};
@@ -348,6 +350,7 @@ VkSampler VulkanPlatform::createExternalSamplerImpl(
uint32_t internalFormat) {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = internalFormat,
};
@@ -18,12 +18,12 @@
#define TNT_FILAMENT_BACKEND_VULKANSWAPCHAIN_IMPL_H
#include "vulkan/VulkanContext.h"
#include "vulkan/VulkanConstants.h"
#include <backend/platforms/VulkanPlatform.h>
#include <bluevk/BlueVK.h>
#include <tuple>
#include <unordered_map>
using namespace bluevk;
+69 -125
View File
@@ -22,7 +22,6 @@
#include "CommandStreamDispatcher.h"
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.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,31 +240,24 @@ 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
}// namespace
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
return new WebGPUDriver(platform, validConfig);
Driver* WebGPUDriver::create(WebGPUPlatform& platform) noexcept {
return new WebGPUDriver(platform);
}
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(platform),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck,
driverConfig.disableHeapHandleTags) {
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform) noexcept
: mPlatform(platform) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(mPlatform.getInstance());
#endif
@@ -257,14 +289,10 @@ void WebGPUDriver::terminate() {
}
void WebGPUDriver::tick(int) {
mDevice.Tick();
}
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor{};
commandEncoderDescriptor.nextInChain = nullptr;
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
}
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
@@ -281,12 +309,6 @@ void WebGPUDriver::setPresentationTime(int64_t monotonic_clock_ns) {
}
void WebGPUDriver::endFrame(uint32_t frameId) {
// FWGPU_LOGW << __FUNCTION__<< "\n";
mQueue.Submit(1, &mCommandBuffer);
mCommandEncoder = nullptr;
mCommandBuffer = nullptr;
mTextureView = nullptr;
mSwapChain->Present();
}
void WebGPUDriver::flush(int) {
@@ -320,13 +342,6 @@ void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
mSwapChain = nullptr;
// TODO: use webgpu handle allocator from
// https://github.com/google/filament/pull/8566
// if (sch) {
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
// destruct(sch, hwSwapChain);
// }
}
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
@@ -342,9 +357,6 @@ void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
// TODO: use webgpu handle allocator from.
// https://github.com/google/filament/pull/8566
// return allocAndConstructHandle<HwSwapChain>();
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
}
@@ -353,7 +365,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 {
@@ -373,23 +385,23 @@ Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
}
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
return allocHandle<HwIndexBuffer>();
return Handle<HwIndexBuffer>((Handle<HwIndexBuffer>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
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 {
@@ -401,15 +413,15 @@ Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
}
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
return allocHandle<WGPUVertexBufferInfo>();
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return allocHandle<WGPUTexture>();
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
return allocHandle<WGPURenderTarget>();
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
}
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
@@ -430,43 +442,25 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
// TODO: use webgpu handle allocator from.
// https://github.com/google/filament/pull/8566
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
assert_invariant(!mSwapChain);
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
mAdapter = mPlatform.requestAdapter(surface);
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();
mSwapChain = std::make_unique<WebGPUSwapChain>(std::move(surface), mAdapter, mDevice, flags);
// TODO configure the surface (maybe before or after creating the swapchain?
// how do we get the surface extent?)
// TODO actually create the swapchain
// auto onQueueWorkDone = [](wgpu::QueueWorkDoneStatus status, void* /* pUserData */) {
// FWGPU_LOGW << "Queued work finished with status: " << status << std::endl;
// };
// mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus status, void* pUserdata) {
// FWGPU_LOGW << "Queued work finished with status:\n";
// }, nullptr /* pUserData */);
void* userDataPtr = nullptr;
mQueue.OnSubmittedWorkDone(
wgpu::CallbackMode::AllowProcessEvents,
[](wgpu::QueueWorkDoneStatus status, void* pUserData) {
FWGPU_LOGW << "Queued work finished with status: " << static_cast<int>(status) << "\n";
if (pUserData == nullptr) {
// Expected case
} else {
FWGPU_LOGW << "Unexpected non-null pUserData received.\n";
}
},
userDataPtr /* pUserData */
);
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
@@ -479,9 +473,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
"rebuilding Filament with that flag, e.g. ./build.sh -x "
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
#endif
// TODO: use webgpu handle allocator from.
// https://github.com/google/filament/pull/8566
// hwSwapChain->swapChain = mSwapChain.get();
}
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
@@ -504,13 +495,7 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
TextureUsage usage) {}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {
// FWGPU_LOGW << __FUNCTION__<< "\n";
WGPUTexture const* src = handleCast<WGPUTexture>(srch);
(void) src;
// textures.insert(
// constructHandle<WGPUTexture>(th, src, baseLevel, levelCount));
}
uint8_t baseLevel, uint8_t levelCount) {}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
@@ -537,11 +522,7 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<
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,
@@ -702,11 +683,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->mBuffers.size());
vertexBuffer->setBuffer(bufferObject, index);
}
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
@@ -739,33 +715,9 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
}
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
// FWGPU_LOGW << __FUNCTION__<< "\n";
mTextureView = mSwapChain->GetNextSurfaceTextureView(params.viewport.width, params.viewport.height);
wgpu::RenderPassColorAttachment renderPassColorAttachment = {};
renderPassColorAttachment.view = mTextureView;
renderPassColorAttachment.resolveTarget = nullptr;
renderPassColorAttachment.loadOp = wgpu::LoadOp::Clear;
renderPassColorAttachment.storeOp = wgpu::StoreOp::Store;
renderPassColorAttachment.clearValue = wgpu::Color{1, 0 , 0 , 1};
renderPassColorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
wgpu::RenderPassDescriptor renderPassDescriptor = {};
renderPassDescriptor.nextInChain = nullptr;
renderPassDescriptor.colorAttachmentCount = 1;
renderPassDescriptor.colorAttachments = &renderPassColorAttachment;
renderPassDescriptor.depthStencilAttachment = nullptr;
renderPassDescriptor.timestampWrites = nullptr;
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport((float)params.viewport.left, (float)params.viewport.bottom,
(float) params.viewport.width, (float) params.viewport.height, params.depthRange.near, params.depthRange.far);
// (float) 1024/*params.viewport.width*/, (float) 640/*params.viewport.height*/, params.depthRange.near, params.depthRange.far);
}
void WebGPUDriver::endRenderPass(int) {
// FWGPU_LOGW << __FUNCTION__<< "\n";
mRenderPassEncoder.End();
mRenderPassEncoder = nullptr;
}
void WebGPUDriver::nextSubpass(int) {
@@ -775,12 +727,6 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
}
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
wgpu::CommandBufferDescriptor commandBufferDescriptor = {};
commandBufferDescriptor.nextInChain = nullptr;
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
// mQueue.Submit(1, &mCommandBuffer);
// mCommandBuffer = nullptr;
}
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -837,8 +783,6 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
}
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
+6 -40
View File
@@ -17,27 +17,18 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include "webgpu/WebGPUSwapChain.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include "private/backend/Driver.h"
#include "private/backend/HandleAllocator.h"
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <webgpu/webgpu_cpp.h>
#include "WebGPUHandles.h"
#include <cstdint>
#include <memory>
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
#endif
namespace filament::backend {
@@ -49,30 +40,26 @@ public:
~WebGPUDriver() noexcept override;
[[nodiscard]] Dispatcher getDispatcher() const noexcept final;
[[nodiscard]] static Driver* create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
[[nodiscard]] static Driver* create(WebGPUPlatform& platform) noexcept;
private:
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
explicit WebGPUDriver(WebGPUPlatform& platform) noexcept;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
// 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;
// TODO consider moving to handle allocator when ready
std::unique_ptr<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;
@@ -87,29 +74,8 @@ private:
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl);
#include "private/backend/DriverAPI.inc"
/*
* Memory management
*/
HandleAllocatorWGPU mHandleAllocator;
template<typename D>
Handle<D> allocHandle() {
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);
}
};
}// namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#endif// TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
@@ -1,19 +0,0 @@
//
// Created by Idris Idris Shah on 3/21/25.
//
#include "WebGPUHandles.h"
namespace filament::backend {
WGPUVertexBuffer::WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih)
: HwVertexBuffer(vextexCount),
vbih(vbih),
mBuffers(MAX_VERTEX_BUFFER_COUNT) {}
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
: HwBufferObject(byteCount),
mBindingType(bindingType) {}
}// namespace filament::backend
-109
View File
@@ -1,109 +0,0 @@
//
// Created by Idris Idris Shah on 3/21/25.
//
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/FixedCapacityVector.h>
#include <webgpu/webgpu_cpp.h>
namespace filament::backend {
struct WGPUBufferObject;
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {}
AttributeArray attributes;
};
struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount, Handle<WGPUVertexBufferInfo> vbih);
void setBuffer(WGPUBufferObject* bufferObject, uint32_t index);
Handle<WGPUVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> mBuffers;
};
struct WGPUIndexBuffer : public HwIndexBuffer {
WGPUIndexBuffer(BufferUsage usage, uint8_t elementSize, uint32_t indexCount);
wgpu::Buffer buffer;
};
struct WGPUBufferObject : HwBufferObject {
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
wgpu::Buffer mBuffer;
const BufferObjectBinding mBindingType;
};
class WGPUTexture : public HwTexture {
public:
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;
};
class WGPURenderTarget : public HwRenderTarget {
public:
class Attachment {
public:
friend class 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,277 +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& device,
wgpu::SurfaceCapabilities const& capabilities, bool useSRGBColorSpace) {
config.device = device;
config.usage = wgpu::TextureUsage::RenderAttachment;
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::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, useSRGBColorSpace);
}
WebGPUSwapChain::~WebGPUSwapChain() {
if (mConfigured) {
mSurface.Unconfigure();
mConfigured = false;
}
}
void WebGPUSwapChain::GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture* texture) {
if (width < 1 || height < 1) {
PANIC_LOG("WebGPUSwapChain::GetCurrentTexture: Invalid width and/or height requested.");
return;
}
if (mConfig.width != width || mConfig.height != height || !mConfigured) {
mConfig.width = width;
mConfig.height = height;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printSurfaceConfiguration(mConfig);
#endif
mSurface.Configure(&mConfig);
mConfigured = true;
return;
}
mSurface.GetCurrentTexture(texture);
}
wgpu::TextureView WebGPUSwapChain::GetNextSurfaceTextureView(uint32_t width, uint32_t height) {
wgpu::SurfaceTexture surfaceTexture;
GetCurrentTexture(width, height, &surfaceTexture);
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::Success) {
return nullptr;
}
// Create a view for this surface texture
wgpu::TextureViewDescriptor textureViewDescriptor;
textureViewDescriptor.nextInChain = nullptr;
textureViewDescriptor.label = "Surface texture view";
textureViewDescriptor.format = surfaceTexture.texture.GetFormat();
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
textureViewDescriptor.baseMipLevel = 0;
textureViewDescriptor.mipLevelCount = 1;
textureViewDescriptor.baseArrayLayer = 0;
textureViewDescriptor.arrayLayerCount = 1;
textureViewDescriptor.aspect = wgpu::TextureAspect::All;
wgpu::TextureView textureView = surfaceTexture.texture.CreateView(&textureViewDescriptor);
return textureView;
}
void WebGPUSwapChain::Present() {
assert_invariant(mSurface);
mSurface.Present();
}
}// namespace filament::backend
@@ -1,46 +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 <backend/Platform.h>
#include <cstdint>
namespace filament::backend {
class WebGPUSwapChain : public Platform::SwapChain {
public:
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter, wgpu::Device& device,
uint64_t flags);
~WebGPUSwapChain();
void GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture*);
wgpu::TextureView GetNextSurfaceTextureView(uint32_t width, uint32_t height);
void Present();
private:
wgpu::Surface mSurface = {};
wgpu::SurfaceConfiguration mConfig = {};
bool mConfigured = false;
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
@@ -144,13 +144,13 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
}
Driver* WebGPUPlatform::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
const Platform::DriverConfig& /*driverConfig*/) noexcept {
if (sharedContext) {
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
"sharedContext was provided, but it will be ignored."
<< utils::io::endl;
}
return WebGPUDriver::create(*this, driverConfig);
return WebGPUDriver::create(*this);
}
WebGPUPlatform::WebGPUPlatform()
@@ -22,8 +22,6 @@
#include <cstdint>
#include <Windows.h>
/**
* Windows OS specific implementation aspects of the WebGPU backend
*/
@@ -43,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;
}
-172
View File
@@ -1,172 +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 "ImageExpectations.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "absl/strings/str_format.h"
#include "utils/Hash.h"
#include <fstream>
#include "backend/PixelBufferDescriptor.h"
#include "private/backend/DriverApi.h"
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
#endif
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
uint32_t expectedPixelHash)
: mWidth(width), mHeight(height), mFileName(std::move(fileName)),
mExpectedPixelHash(expectedPixelHash) {}
int ScreenshotParams::width() const {
return mWidth;
}
int ScreenshotParams::height() const {
return mHeight;
}
uint32_t ScreenshotParams::expectedHash() const {
return mExpectedPixelHash;
}
std::string ScreenshotParams::outputDirectoryPath() const {
return ".";
}
std::string ScreenshotParams::generatedActualFileName() const {
return absl::StrFormat("%s_actual.png", mFileName);
}
std::string ScreenshotParams::generatedActualFilePath() const {
return absl::StrFormat("%s/%s", outputDirectoryPath(), generatedActualFileName());
}
std::string ScreenshotParams::goldenFileName() const {
return absl::StrFormat("%s_golden.png", mFileName);
}
std::string ScreenshotParams::goldenFilePath() const {
return absl::StrFormat("%s/%s", outputDirectoryPath(), goldenFileName());
}
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
filament::backend::DriverApi& api, ScreenshotParams params,
filament::backend::RenderTargetHandle renderTarget)
: mFileName(fileName), mLineNumber(lineNumber), mParams(std::move(params)),
mResult(api, renderTarget, mParams) {}
void ImageExpectation::evaluate() {
// Ensure this is only evaluated once.
if (mEvaluated) {
return;
}
mEvaluated = true;
// Do the actual image comparison inside a scoped trace with the stored file and line.
{
testing::ScopedTrace trace(mFileName, mLineNumber, "");
compareImage();
}
}
void ImageExpectation::compareImage() const {
bool bytesFilled = mResult.bytesFilled();
EXPECT_THAT(bytesFilled, testing::IsTrue())
<< "Render target wasn't copied to the buffer for " << mFileName;
if (bytesFilled) {
uint32_t actualHash = mResult.hash();
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash()));
}
}
ImageExpectations::ImageExpectations(filament::backend::DriverApi& api) : mApi(api) {}
ImageExpectations::~ImageExpectations() {
// Guarantee that all expectations are evaluated when this leaves scope even if the caller
// forgot to manually evaluate them.
evaluate();
}
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
mExpectations.emplace_back(fileName, lineNumber, mApi, std::move(params), renderTarget);
}
void ImageExpectations::evaluate() {
for (auto& expectation: mExpectations) {
expectation.evaluate();
}
mExpectations.clear();
}
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
#ifdef FILAMENT_IOS
bytesFilled_ = true;
bytes_.resize(size);
std::fill(bytes_.begin(), bytes_.end(), 0);
#else
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
mInternal->bytes.resize(size);
auto cb = [](void* buffer, size_t size, void* user) {
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
internal->params.height(),
internal->params.width() * 4, (uint8_t*)buffer);
std::string filePath = internal->params.generatedActualFilePath();
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
filePath);
internal->bytesFilled = true;
};
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
(void*)mInternal.get());
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
std::move(pb));
#endif
}
RenderTargetDump::~RenderTargetDump() {
// If the GPU callback hasn't been made yet then there's a callback elsewhere that has a copy of
// the internal pointer. But there's no guarantee that the callback will be ever made if the GPU
// pipeline wasn't run for some reason. So it is necessary to leak the memory.
// It would be possible to try to coordinate with the callback to have it clean up the memory,
// but if this condition happens there's already an issue with the test case so there's no need.
if (!bytesFilled()) {
mInternal.release();
}
}
uint32_t RenderTargetDump::hash() const {
return utils::hash::murmur3((uint32_t*)mInternal->bytes.data(), mInternal->bytes.size() / 4, 0);
}
bool RenderTargetDump::bytesFilled() const {
return mInternal->bytesFilled;
}
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
-136
View File
@@ -1,136 +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_IMAGE_EXPECTATIONS_H
#define TNT_IMAGE_EXPECTATIONS_H
#include <vector>
#include "gtest/gtest.h"
#include "backend/Handle.h"
#include "backend/DriverApiForward.h"
// Arguments are (RenderTargetHandle renderTarget, ImageExpectations& expectations,
// ScreenshotParams screenshotParams)
#define EXPECT_IMAGE(renderTarget, expectations, screenshotParams) \
do { \
expectations.addExpectation( \
__FILE__, \
__LINE__, \
renderTarget, \
screenshotParams); \
} while (0)
/**
* Stores user-provided configuration values for an image expectation
*/
class ScreenshotParams {
public:
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash);
int width() const;
int height() const;
uint32_t expectedHash() const;
std::string outputDirectoryPath() const;
std::string generatedActualFileName() const;
std::string generatedActualFilePath() const;
std::string goldenFileName() const;
std::string goldenFilePath() const;
private:
int mWidth;
int mHeight;
uint32_t mExpectedPixelHash;
std::string mFileName;
};
/**
* When created adds a command to the GPU pipeline to copy the render target into a buffer and
* stores the result.
* If this object is destroyed before the GPU pipeline is flushed it will leak memory in order to
* avoid the GPU pipeline callback being a use-after-free.
*/
class RenderTargetDump {
public:
RenderTargetDump(filament::backend::DriverApi& api,
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params);
RenderTargetDump(RenderTargetDump&& other) = default;
RenderTargetDump& operator=(RenderTargetDump&& other) = default;
~RenderTargetDump();
/**
* Should only bue used if BytesFilled returns true.
* @return The hash of the stored bytes.
*/
uint32_t hash() const;
/**
* Thread safe as this is backed by an atomic.
* Once this returns true it will never return false.
* @return Whether the bytes have actually been copied from the GPU to the buffer.
*/
bool bytesFilled() const;
private:
struct Internal {
explicit Internal(const ScreenshotParams& params);
ScreenshotParams params;
std::atomic<bool> bytesFilled = false;
std::vector<unsigned char> bytes;
};
// We need a memory location that won't be invalidated to pass to GPU callbacks as they can't
// be canceled during the destructor.
std::unique_ptr<Internal> mInternal;
};
class ImageExpectation {
public:
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
ScreenshotParams params, filament::backend::RenderTargetHandle renderTarget);
void evaluate();
private:
void compareImage() const;
bool mEvaluated = false;
const char* mFileName;
int mLineNumber;
ScreenshotParams mParams;
RenderTargetDump mResult;
};
class ImageExpectations {
public:
explicit ImageExpectations(filament::backend::DriverApi& api);
~ImageExpectations();
/**
* Not meant to be called directly, use EXPECT_IMAGE to get the file name and line number
*/
void addExpectation(const char* fileName, int lineNumber,
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params);
void evaluate();
private:
filament::backend::DriverApi& mApi;
std::vector<ImageExpectation> mExpectations;
};
#endif //TNT_IMAGE_EXPECTATIONS_H
-64
View File
@@ -1,64 +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 "Shader.h"
#include "BackendTest.h"
#include "ShaderGenerator.h"
namespace test {
using namespace filament::backend;
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 };
};
filamat::DescriptorSets descriptors;
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);
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
}
mProgram = cleanup.add(api.createProgram(std::move(prog)));
mDescriptorSetLayout = cleanup.add(
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
}
filament::backend::ProgramHandle Shader::getProgram() const {
return mProgram;
}
filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const {
return mDescriptorSetLayout;
}
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
return mDescriptorSet;
}
} // namespace test
-143
View File
@@ -1,143 +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_SHADER_H
#define TNT_SHADER_H
#include "Lifetimes.h"
namespace test {
// All describing a shader that should be created.
struct ShaderConfig {
std::string vertexShader;
std::string fragmentShader;
std::vector<utils::CString> uniformNames;
};
// All values describing a uniform.
struct ResolvedUniformBindingConfig {
size_t dataSize;
size_t bufferSize;
uint32_t byteOffset;
filament::backend::descriptor_set_t set;
filament::backend::descriptor_binding_t binding;
};
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
// resolve can be called to create a ResolvedBindingConfig with default values
// use in place of nullopt values.
struct UniformBindingConfig {
std::optional<size_t> dataSize;
std::optional<size_t> bufferSize;
std::optional<uint32_t> byteOffset;
std::optional<filament::backend::descriptor_set_t> set;
std::optional<filament::backend::descriptor_binding_t> binding;
template<typename UniformType>
ResolvedUniformBindingConfig resolve();
};
class Shader {
public:
// All graphics resources have their lifetime controlled by the Cleanup and not this object.
// Shader must not outlive either the DriverApi or the Cleanup.
Shader(filament::backend::DriverApi& api, Cleanup& cleanup, ShaderConfig config);
// Uploads a uniform with the default config.
template<typename UniformType>
void uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformType uniforms) const;
// Binds a uniform with the default config.
template<typename UniformType>
void bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer) const;
// Uploads a uniform, with UniformBindingConfig providing the arguments.
template<typename UniformType>
void uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config, UniformType uniforms) const;
// Binds a uniform, with UniformBindingConfig providing the arguments.
template<typename UniformType>
void bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config) const;
filament::backend::ProgramHandle getProgram() const;
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
filament::backend::DescriptorSetHandle getDescriptorSet() const;
protected:
filament::backend::ProgramHandle mProgram;
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
filament::backend::DescriptorSetHandle mDescriptorSet;
};
template<typename UniformType>
ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
auto resolvedDataSize = dataSize.value_or(sizeof(UniformType));
return ResolvedUniformBindingConfig{
.dataSize = resolvedDataSize,
.bufferSize = bufferSize.value_or(resolvedDataSize),
.byteOffset = byteOffset.value_or(0),
.set = set.value_or(1),
.binding = binding.value_or(0)
};
}
template<typename UniformType>
void Shader::uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config, UniformType uniforms) const {
auto resolvedConfig = config.resolve<UniformType>();
UniformType* tmp = new UniformType(uniforms);
auto cb = [](void* buffer, size_t size, void* user) {
UniformType* sp = (UniformType*)buffer;
delete sp;
};
filament::backend::BufferDescriptor bd(tmp, resolvedConfig.dataSize, cb);
api.updateBufferObject(hwBuffer, std::move(bd), resolvedConfig.byteOffset);
}
template<typename UniformType>
void Shader::bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config) const {
auto resolvedConfig = config.resolve<UniformType>();
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
resolvedConfig.bufferSize);
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
}
template<typename UniformType>
void Shader::uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformType uniforms) const {
uploadUniform(api, hwBuffer, UniformBindingConfig{}, uniforms);
}
template<typename UniformType>
void Shader::bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer) const {
bindUniform<UniformType>(api, hwBuffer, UniformBindingConfig{});
}
} // namespace test
#endif //TNT_SHADER_H
+260 -208
View File
@@ -16,9 +16,8 @@
#include "BackendTest.h"
#include "ImageExpectations.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -27,10 +26,8 @@
#include <fstream>
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
#endif
namespace test {
@@ -40,21 +37,6 @@ 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;
@@ -74,12 +56,49 @@ void main() {
fragColor = params.color;
})";
Shader BlitTest::createShader() {
return Shader(getDriverApi(), mCleanup, ShaderConfig{
.vertexShader = triangleVs,
.fragmentShader = triangleFs,
.uniformNames = { "Params" },
});
struct MaterialParams {
float4 color;
float4 scale;
};
struct ScreenshotParams {
int width;
int height;
const char* filename;
uint32_t pixelHashResult;
};
#ifdef FILAMENT_IOS
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {}
#else
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {
using namespace image;
const size_t size = params->width * params->height * 4;
void* buffer = calloc(1, size);
auto cb = [](void* buffer, size_t size, void* user) {
ScreenshotParams* params = (ScreenshotParams*) user;
int w = params->width, h = params->height;
const uint32_t* texels = (uint32_t*) buffer;
params->pixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
LinearImage image(w, h, 4);
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm(params->filename, std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", params->filename);
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
(void*) params);
dapi.readPixels(rt, 0, 0, params->width, params->height, std::move(pb));
}
#endif
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 uint32_t toUintColor(float4 color) {
@@ -97,7 +116,7 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
const int width = baseWidth >> level;
const int height = baseHeight >> level;
const size_t size0 = height * width * 4;
uint8_t* buffer0 = (uint8_t*)calloc(size0, 1);
uint8_t* buffer0 = (uint8_t*) calloc(size0, 1);
PixelBufferDescriptor pb(buffer0, size0, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
const float3 foreground = color;
@@ -121,7 +140,7 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
// | ........
// low addresses
// This is because OpenGL automatically flips image data when uploading into the texture.
uint32_t* texels = (uint32_t*)buffer0;
uint32_t* texels = (uint32_t*) buffer0;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
float2 uv = { (col - width / 2.0f) / width, (row - height / 2.0f) / height };
@@ -149,7 +168,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
const int width = baseWidth >> level;
const int height = baseHeight >> level;
const size_t size0 = height * width * 4 * 6;
uint8_t* buffer0 = (uint8_t*)calloc(size0, 1);
uint8_t* buffer0 = (uint8_t*) calloc(size0, 1);
PixelBufferDescriptor pb(buffer0, size0, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
const float3 foreground = color;
@@ -157,7 +176,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
const float radius = 0.25f;
// Draw a circle on a yellow background.
uint32_t* texels = (uint32_t*)buffer0;
uint32_t* texels = (uint32_t*) buffer0;
for (int face = 0; face < 6; face++) {
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
@@ -175,9 +194,10 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb));
}
TEST_F(BlitTest, ColorMagnify) {
TEST_F(BackendTest, ColorMagnify) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -188,40 +208,37 @@ TEST_F(BlitTest, ColorMagnify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
{}));
dstRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {},
{}));
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
}
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
const int srcLevel = 1;
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTargets[0],
{ 0, 0, kDstTexWidth, kDstTexHeight }, srcRenderTargets[srcLevel],
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
SamplerMagFilter::LINEAR);
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTargets[srcLevel],
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
{
@@ -229,23 +246,29 @@ TEST_F(BlitTest, ColorMagnify) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
{
ImageExpectations expectations(api);
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTargets[0], expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
api.commit(swapChain);
}
flushAndWait();
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// Check if the image matches perfectly to our golden run.
const uint32_t expected = 0x410bdd31;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
}
TEST_F(BlitTest, ColorMinify) {
TEST_F(BackendTest, ColorMinify) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -256,55 +279,57 @@ TEST_F(BlitTest, ColorMinify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
{}));
dstRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {},
{}));
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
}
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
const int srcLevel = 1;
api.blitDEPRECATED(TargetBufferFlags::COLOR0,
dstRenderTargets[0], { 0, 0, kDstTexWidth, kDstTexHeight },
srcRenderTargets[srcLevel],
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
dstRenderTargets[0], {0, 0, kDstTexWidth, kDstTexHeight},
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
SamplerMagFilter::LINEAR);
{
ImageExpectations expectations(api);
EXPECT_IMAGE(dstRenderTargets[0], expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
dumpScreenshot(api, dstRenderTargets[0], &params);
flushAndWait();
}
// Wait for the ReadPixels result to come back.
flushAndWait();
// Check if the image matches perfectly to our golden run.
const uint32_t expected = 0xf3d9c53f;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
}
TEST_F(BlitTest, ColorResolve) {
TEST_F(BackendTest, ColorResolve) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -313,29 +338,49 @@ TEST_F(BlitTest, ColorResolve) {
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
constexpr int kSampleCount = 4;
Shader shader = createShader();
// Create a program.
ProgramHandle program;
{
filamat::DescriptorSets descriptors;
descriptors[1] = { { "Params",
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0 },
{} } };
ShaderGenerator shaderGen(
triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors));
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
program = cleanup.add(api.createProgram(std::move(prog)));
}
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
TrianglePrimitive const triangle(api);
// Create 4-sample texture.
Handle<HwTexture> const srcColorTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight,
1,
TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::COLOR_ATTACHMENT));
// Create 1-sample texture.
Handle<HwTexture> const dstColorTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a 4-sample render target with the 4-sample texture.
Handle<HwRenderTarget> const srcRenderTarget = mCleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> const srcRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
{{ srcColorTexture }}, {}, {}));
// Create a 1-sample render target with the 1-sample texture.
Handle<HwRenderTarget> const dstRenderTarget = mCleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> const dstRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
{{ dstColorTexture }}, {}, {}));
@@ -349,52 +394,58 @@ TEST_F(BlitTest, ColorResolve) {
params.viewport.height = kSrcTexHeight;
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;
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// Draw red triangle into srcRenderTarget.
shader.uploadUniform(api, ubuffer, MaterialParams{
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
uploadUniforms(api, ubuffer, {
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
});
shader.bindUniform<MaterialParams>(api, ubuffer);
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams));
api.bindDescriptorSet(descriptorSet, 1, {});
// FIXME: on Metal this triangle is not drawn. Can't understand why.
{
RenderFrame frame(api);
api.beginRenderPass(srcRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
}
// Resolve the MSAA render target into the single-sample render target.
api.blitDEPRECATED(TargetBufferFlags::COLOR,
dstRenderTarget, { 0, 0, kDstTexWidth, kDstTexHeight },
srcRenderTarget, { 0, 0, kSrcTexWidth, kSrcTexHeight },
dstRenderTarget, {0, 0, kDstTexWidth, kDstTexHeight},
srcRenderTarget, {0, 0, kSrcTexWidth, kSrcTexHeight},
SamplerMagFilter::NEAREST);
{
ImageExpectations expectations(api);
// Grab a screenshot.
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
dumpScreenshot(api, dstRenderTarget, &sparams);
EXPECT_IMAGE(dstRenderTarget, expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
// Wait for the ReadPixels result to come back.
flushAndWait();
flushAndWait();
}
// Check if the image matches perfectly to our golden run.
const uint32_t expected = 0xebfac2ef;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
EXPECT_TRUE(sparams.pixelHashResult == expected);
}
TEST_F(BlitTest, Blit2DTextureArray) {
TEST_F(BackendTest, Blit2DTextureArray) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
api.startCapture(0);
mCleanup.addPostCall([&]() { api.stopCapture(0); });
cleanup.addPostCall([&]() { api.stopCapture(0); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -409,40 +460,33 @@ TEST_F(BlitTest, Blit2DTextureArray) {
constexpr int kDstTexLayer = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth,
kSrcTexHeight, kSrcTexDepth,
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0),
flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create two RenderTargets.
const int level = 0;
Handle<HwRenderTarget> srcRenderTarget = mCleanup.add(
api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0,
{ srcTexture, level, kSrcTexLayer }, {}, {}));
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(
api.createRenderTarget(TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0,
{ dstTexture, level, kDstTexLayer }, {}, {}));
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {}));
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {}));
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
const int srcLevel = 0;
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
{ 0, 0, kDstTexWidth, kDstTexHeight }, srcRenderTarget,
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
SamplerMagFilter::LINEAR);
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
{
@@ -450,24 +494,29 @@ TEST_F(BlitTest, Blit2DTextureArray) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
{
ImageExpectations expectations(api);
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTarget, expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "Blit2DTextureArray",
0x8de7d55b));
api.commit(swapChain);
}
flushAndWait();
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// Check if the image matches perfectly to our golden run.
const uint32_t expected = 0x8de7d55b;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
}
TEST_F(BlitTest, BlitRegion) {
TEST_F(BackendTest, BlitRegion) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -480,48 +529,47 @@ TEST_F(BlitTest, BlitRegion) {
constexpr int kDstLevel = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Blit one-quarter of src level 1 to dst level 0.
Viewport srcRect = {
.left = 0,
.bottom = 0,
.width = (kSrcTexWidth >> kSrcLevel) / 2,
.height = (kSrcTexHeight >> kSrcLevel) / 2,
.left = 0,
.bottom = 0,
.width = (kSrcTexWidth >> kSrcLevel) / 2,
.height = (kSrcTexHeight >> kSrcLevel) / 2,
};
Viewport dstRect = {
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
};
// Create a RenderTarget for each texture's miplevel.
// We purposely set the render target width and height to smaller than the texture, to check
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget =
mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, { srcTexture, kSrcLevel, 0 }, {}, {}));
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {}));
Handle<HwRenderTarget> dstRenderTarget =
mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, { dstTexture, kDstLevel, 0 }, {}, {}));
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {}));
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget,
srcRect,
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
@@ -530,25 +578,33 @@ TEST_F(BlitTest, BlitRegion) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
{
ImageExpectations expectations(api);
{
RenderFrame frame(api);
// TODO: for some reason, this test has very, very slight (as in one pixel) differences
// between OpenGL and Metal. So disable golden checking for now.
// EXPECT_IMAGE(dstRenderTarget, expectations, ScreenshotParams(kDstTexWidth,
// kDstTexHeight, "BlitRegion", 0x74fa34ed));
api.commit(swapChain);
}
flushAndWait();
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
// OpenGL and Metal. So disable golden checking for now.
// Use the compare tool from ImageMagick to see visual differences:
// compare -verbose -metric mae BlitRegion_Metal.png BlitRegion_OpenGL.png difference.png
//
// const uint32_t expected = 0x74fa34ed;
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
// EXPECT_TRUE(params.pixelHashResult == expected);
}
TEST_F(BlitTest, BlitRegionToSwapChain) {
TEST_F(BackendTest, BlitRegionToSwapChain) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -558,15 +614,15 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current.
auto swapChain = mCleanup.add(createSwapChain());
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
// Create a source texture.
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
@@ -574,46 +630,42 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
{}));
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
}
// Blit one-quarter of src level 1 to dst level 0.
const int srcLevel = 1;
Viewport srcRect = {
.left = (kSrcTexWidth >> srcLevel) / 2,
.bottom = (kSrcTexHeight >> srcLevel) / 2,
.width = (kSrcTexWidth >> srcLevel) / 2,
.height = (kSrcTexHeight >> srcLevel) / 2,
.left = (kSrcTexWidth >> srcLevel) / 2,
.bottom = (kSrcTexHeight >> srcLevel) / 2,
.width = (kSrcTexWidth >> srcLevel) / 2,
.height = (kSrcTexHeight >> srcLevel) / 2,
};
Viewport dstRect = {
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
};
{
ImageExpectations expectations(api);
{
{
RenderFrame frame(api);
RenderFrame frame(api);
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
dstRect, srcRenderTargets[srcLevel],
srcRect, SamplerMagFilter::LINEAR);
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
dstRect, srcRenderTargets[srcLevel],
srcRect, SamplerMagFilter::LINEAR);
api.commit(swapChain);
}
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
// TODO: for some reason, this test has been disabled. It needs to be tested on all
// machines.
// EXPECT_IMAGE(dstRenderTarget, expectations,
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
}
flushAndWait();
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test
+114 -70
View File
@@ -16,8 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -80,28 +78,8 @@ struct MaterialParams {
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(MaterialParams),
.bufferSize = sizeof(MaterialParams) + 64,
.byteOffset = 64
};
class BufferUpdatesTest : public BackendTest {
public:
BufferUpdatesTest() : mCleanup(getDriverApi()) {}
protected:
Shader createShader() {
return Shader(getDriverApi(), mCleanup, ShaderConfig{
vertex, fragment, {"Params"}
});
}
Cleanup mCleanup;
};
TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
TEST_F(BackendTest, VertexBufferUpdate) {
const bool largeBuffers = false;
// If updateIndices is true, then even-numbered triangles will have their indices set to
@@ -112,15 +90,31 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// executeCommands().
{
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
Shader shader = createShader();
// Create a program.
filamat::DescriptorSets descriptors;
descriptors[1] = { { "Params",
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } };
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
Program p = shaderGen.getProgram(api);
p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
auto program = api.createProgram(std::move(p));
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// To test large buffers (which exercise a different code path) create an extra large
// buffer. Only the first 3 vertices will be used.
@@ -134,29 +128,38 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
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;
// 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(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// 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 = api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
api.bindDescriptorSet(descriptorSet, 1, {});
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, MaterialParams{
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
});
// Upload uniforms.
{
MaterialParams params {
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
api.updateBufferObject(ubuffer, std::move(bd), 64);
}
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
@@ -199,6 +202,13 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
api.endFrame(0);
api.stopCapture(0);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyBufferObject(ubuffer);
api.destroyRenderTarget(defaultRenderTarget);
}
executeCommands();
@@ -206,38 +216,62 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
// buffer object is partially updated.
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
Shader shader = createShader();
filamat::DescriptorSets descriptors;
descriptors[1] = { { "Params",
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } };
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
Program p = shaderGen.getProgram(api);
p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
auto program = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
// 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(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
api.bindDescriptorSet(descriptorSet, 1, {});
// 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}}, {}, {}));
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto renderTarget = api.createRenderTarget(
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, MaterialParams{
{
MaterialParams params {
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
});
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
api.updateBufferObject(ubuffer, std::move(bd), 64);
}
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
@@ -246,27 +280,29 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512;
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), params);
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
renderTarget, swapChain, program, params);
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
// 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 }
);
{
MaterialParams params {
.color = { 1.0f, 0.0f, 1.0f, 1.0f },
.offset = { 0.5f, 0.5f, 0.0f, 0.0f }
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) ((char*)buffer - offsetof(MaterialParams, color.b));
delete sp;
};
BufferDescriptor bd((char*)tmp + offsetof(MaterialParams, color.b), sizeof(float) * 4, cb);
api.updateBufferObject(ubuffer, std::move(bd), 64 + offsetof(MaterialParams, color.b));
}
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), params);
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
renderTarget, swapChain, program, params);
static const uint32_t expectedHash = 91322442;
readPixelsAndAssertHash(
@@ -276,6 +312,14 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
api.commit(swapChain);
api.endFrame(0);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyBufferObject(ubuffer);
api.destroyRenderTarget(renderTarget);
api.destroyTexture(colorTexture);
// This ensures all driver commands have finished before exiting the test.
api.finish();
+3 -7
View File
@@ -16,8 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
using namespace filament;
using namespace filament::backend;
@@ -25,14 +23,13 @@ namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a SwapChain.
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
// headless) so we obtain a drawable.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
int callbackCountA = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
@@ -82,10 +79,9 @@ TEST_F(BackendTest, FrameScheduledCallback) {
TEST_F(BackendTest, FrameCompletedCallback) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a SwapChain.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
int callbackCountA = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
+27 -18
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -130,13 +129,12 @@ static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
// backend's readPixels does not work correctly with textures that have image data uploaded.
TEST_F(BackendTest, FeedbackLoops) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
@@ -157,10 +155,10 @@ TEST_F(BackendTest, FeedbackLoops) {
{ "test_tex", DescriptorType::SAMPLER, 0 },
{ "Params", DescriptorType::UNIFORM_BUFFER, 1 }
});
program = cleanup.add(api.createProgram(std::move(prog)));
program = api.createProgram(std::move(prog));
}
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
@@ -170,27 +168,27 @@ TEST_F(BackendTest, FeedbackLoops) {
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 1,
DescriptorFlags::NONE, 0
}}}));
}}});
TrianglePrimitive const triangle(getDriverApi());
// Create a texture.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage));
Handle<HwTexture> const texture = api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage);
// Create ubo
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
// Create a RenderTarget for each miplevel.
Handle<HwRenderTarget> renderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
slog.i << "Level " << int(level) << ": " <<
(kTexWidth >> level) << "x" << (kTexHeight >> level) << io::endl;
renderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {}));
renderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {});
}
// Fill the base level of the texture with interesting colors.
@@ -228,13 +226,12 @@ TEST_F(BackendTest, FeedbackLoops) {
params.flags.discardStart = TargetBufferFlags::ALL;
state.rasterState.disableBlending();
for (int targetLevel = 1; targetLevel < kNumLevels; targetLevel++) {
Cleanup passCleanup(api);
const uint32_t sourceLevel = targetLevel - 1;
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
auto textureView = api.createTextureView(texture, sourceLevel, 1);
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -250,6 +247,8 @@ TEST_F(BackendTest, FeedbackLoops) {
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.destroyTexture(textureView);
api.destroyDescriptorSet(descriptorSet);
}
// Upsample passes
@@ -257,13 +256,12 @@ TEST_F(BackendTest, FeedbackLoops) {
state.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
state.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
for (int targetLevel = kNumLevels - 2; targetLevel >= 0; targetLevel--) {
Cleanup passCleanup(api);
const uint32_t sourceLevel = targetLevel + 1;
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
auto textureView = api.createTextureView(texture, sourceLevel, 1);
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -279,6 +277,8 @@ TEST_F(BackendTest, FeedbackLoops) {
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.destroyTexture(textureView);
api.destroyDescriptorSet(descriptorSet);
}
// Read back the render target corresponding to the base level.
@@ -296,6 +296,15 @@ TEST_F(BackendTest, FeedbackLoops) {
executeCommands();
getDriver().purge();
}
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyTexture(texture);
api.destroyBufferObject(ubuffer);
for (auto rt : renderTargets) {
api.destroyRenderTarget(rt);
}
}
const uint32_t expected = 0x70695aa1;
+16 -12
View File
@@ -16,10 +16,9 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
@@ -296,12 +295,10 @@ TEST_F(BackendTest, UpdateImage2D) {
// The test is executed within this block scope to force destructors to run before
// executeCommands().
for (const auto& t : testCases) {
Cleanup cleanup(api);
// 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,
@@ -318,21 +315,21 @@ TEST_F(BackendTest, UpdateImage2D) {
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
ProgramHandle const program = cleanup.add(api.createProgram(std::move(prog)));
ProgramHandle const program = api.createProgram(std::move(prog));
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
// Create a Texture.
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage));
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage);
// Upload some pixel data.
if (t.uploadSubregions) {
@@ -362,6 +359,13 @@ TEST_F(BackendTest, UpdateImage2D) {
api.commit(swapChain);
api.endFrame(0);
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(defaultRenderTarget);
api.destroyTexture(texture);
}
api.finish();
+26 -26
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -61,28 +60,25 @@ using namespace filament;
using namespace filament::backend;
TEST_F(BackendTest, MRT) {
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
auto swapChain = createSwapChain();
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
TrianglePrimitive triangle(api);
TrianglePrimitive triangle(getDriverApi());
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
// Create two Textures.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> textureA = cleanup.add(api.createTexture(
Handle<HwTexture> textureA = getDriverApi().createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -90,8 +86,8 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // depth
usage)); // usage
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
usage); // usage
Handle<HwTexture> textureB = getDriverApi().createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -99,10 +95,10 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // depth
usage)); // usage
usage); // usage
// Create a RenderTarget with two attachments.
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
@@ -112,7 +108,7 @@ TEST_F(BackendTest, MRT) {
0, // layerCount
{{textureA },{textureB }}, // color
{}, // depth
{})); // stencil
{}); // stencil
RenderPassParams params = {};
fullViewport(params);
@@ -128,21 +124,25 @@ TEST_F(BackendTest, MRT) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.startCapture(0);
getDriverApi().startCapture(0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Draw a triangle.
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.flush();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.stopCapture(0);
getDriverApi().stopCapture(0);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
}
executeCommands();
+27 -17
View File
@@ -17,10 +17,9 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
@@ -79,13 +78,12 @@ using namespace filament::backend;
TEST_F(BackendTest, TextureViewLod) {
auto& api = getDriverApi();
api.startCapture(0);
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program that draws only white.
@@ -94,7 +92,7 @@ TEST_F(BackendTest, TextureViewLod) {
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)));
whiteProgram = api.createProgram(std::move(p));
}
// Create a program that samples a texture.
@@ -108,19 +106,19 @@ TEST_F(BackendTest, TextureViewLod) {
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)));
textureProgram = api.createProgram(std::move(p));
}
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = 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));
descriptorSet[0] = api.createDescriptorSet(descriptorSetLayout);
descriptorSet[1] = api.createDescriptorSet(descriptorSetLayout);
// Create a texture that has 4 mip levels. Each level is a different color.
// Level 0: 128x128 (red)
@@ -129,9 +127,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,
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE);
// Create image data.
auto pixelFormat = PixelDataFormat::RGBA;
@@ -165,13 +163,13 @@ TEST_F(BackendTest, TextureViewLod) {
// Level 1: 64x64 (green) <-- base
// Level 2: 32x32 (blue) <--- white triangle rendered
// Level 3: 16x16 (yellow) <-- max
auto texture13 = cleanup.add(api.createTextureView(texture, 1, 3));
auto texture13 = api.createTextureView(texture, 1, 3);
// Render a white triangle into level 2.
// We specify mip level 2, because minMaxLevels has no effect when rendering into a texture.
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 32, 32, 1, 0,
{texture, 2 /* level */, 0 /* layer */}, {}, {}));
{texture, 2 /* level */, 0 /* layer */}, {}, {});
{
RenderPassParams params = {};
fullViewport(params);
@@ -187,7 +185,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
}
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
RenderPassParams params = {};
fullViewport(params);
@@ -219,7 +217,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
// Adjust the base mip to 2.
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
auto texture22 = api.createTextureView(texture, 2, 2);
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
.filterMag = SamplerMagFilter::NEAREST,
@@ -246,6 +244,18 @@ TEST_F(BackendTest, TextureViewLod) {
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
api.destroyDescriptorSet(descriptorSet[0]);
api.destroyDescriptorSet(descriptorSet[1]);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyTexture(texture);
api.destroyTexture(texture13);
api.destroyTexture(texture22);
api.destroyProgram(whiteProgram);
api.destroyProgram(textureProgram);
}
api.finish();
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -71,20 +70,18 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
auto swapChain = createSwapChain();
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
TrianglePrimitive triangle(api);
TrianglePrimitive triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -100,21 +97,25 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.startCapture(0);
getDriverApi().startCapture(0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Render a triangle.
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.flush();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.stopCapture(0);
getDriverApi().stopCapture(0);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
}
executeCommands();
+7 -5
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -81,21 +80,20 @@ TEST_F(BackendTest, PushConstants) {
auto& api = getDriverApi();
api.startCapture(0);
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
ProgramHandle program = api.createProgram(std::move(p));
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
TrianglePrimitive triangle(api);
@@ -152,6 +150,10 @@ TEST_F(BackendTest, PushConstants) {
api.commit(swapChain);
api.endFrame(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
api.stopCapture(0);
+59 -55
View File
@@ -16,10 +16,9 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <utils/Hash.h>
@@ -237,20 +236,17 @@ TEST_F(ReadPixelsTest, ReadPixels) {
TestCase const testCases[] = { t0, t2, t3, t4, t5, t6, t7, t8 };
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// 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)));
Program p = shaderGen.getProgram(getDriverApi());
programFloat = getDriverApi().createProgram(std::move(p));
}
{
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
programUint = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
programUint = getDriverApi().createProgram(std::move(p));
}
@@ -259,35 +255,35 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// Create a platform-specific SwapChain and make it current.
Handle<HwSwapChain> swapChain;
if (t.useDefaultRT) {
swapChain = cleanup.add(createSwapChain());
swapChain = createSwapChain();
} else {
swapChain = cleanup.add(api.createSwapChainHeadless(t.getRenderTargetSize(),
t.getRenderTargetSize(), 0));
swapChain = getDriverApi().createSwapChainHeadless(t.getRenderTargetSize(),
t.getRenderTargetSize(), 0);
}
api.makeCurrent(swapChain, swapChain);
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
Handle<HwTexture> const texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D,
t.mipLevels, t.textureFormat, 1, renderTargetBaseSize, renderTargetBaseSize, 1,
usage));
usage);
Handle<HwRenderTarget> renderTarget;
if (t.useDefaultRT) {
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
renderTarget = cleanup.add(api.createDefaultRenderTarget());
renderTarget = getDriverApi().createDefaultRenderTarget();
} else {
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
renderTarget = cleanup.add(api.createRenderTarget(
renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
t.getRenderTargetSize(), t.samples, 0, {{ texture, uint8_t(t.mipLevel) }}, {},
{}));
{});
}
TrianglePrimitive const triangle(api);
TrianglePrimitive const triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -298,11 +294,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
params.viewport.height = t.getRenderTargetSize();
params.viewport.width = t.getRenderTargetSize();
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Render a white triangle over blue.
api.beginRenderPass(renderTarget, params);
getDriverApi().beginRenderPass(renderTarget, params);
PipelineState state;
state.program = programFloat;
@@ -313,21 +309,21 @@ TEST_F(ReadPixelsTest, ReadPixels) {
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().endRenderPass();
if (t.mipLevel > 0) {
Cleanup localCleanup(api);
// Render red to the first mip level to check that the backend is actually reading the
// correct mip.
RenderPassParams p = params;
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> mipLevelOneRT = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
{{ texture }}, {}, {}));
{{ texture }}, {}, {});
p.clearColor = {1.f, 0.f, 0.f, 1.f};
api.beginRenderPass(mipLevelOneRT, p);
api.endRenderPass();
getDriverApi().beginRenderPass(mipLevelOneRT, p);
getDriverApi().endRenderPass();
getDriverApi().destroyRenderTarget(mipLevelOneRT);
}
// Read pixels.
@@ -351,19 +347,26 @@ TEST_F(ReadPixelsTest, ReadPixels) {
free(buffer);
}, (void*) &t);
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
getDriverApi().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};
api.beginRenderPass(renderTarget, params);
api.endRenderPass();
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().endRenderPass();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(texture);
}
getDriverApi().destroyProgram(programFloat);
getDriverApi().destroyProgram(programUint);
// This ensures all driver commands have finished before exiting the test.
flushAndWait();
}
@@ -372,21 +375,18 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
const size_t renderTargetSize = 2000;
const int iterationCount = 100;
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.makeCurrent(swapChain, swapChain);
auto swapChain = getDriverApi().createSwapChainHeadless(renderTargetSize, renderTargetSize, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().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(
Handle<HwTexture> texture = getDriverApi().createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -394,9 +394,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
renderTargetSize, // width
renderTargetSize, // height
1, // depth
usage)); // usage
usage); // usage
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR,
renderTargetSize, // width
renderTargetSize, // height
@@ -404,9 +404,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
0, // layerCount
{{ texture }}, // color
{}, // depth
{})); // stencil
{}); // stencil
TrianglePrimitive triangle(api);
TrianglePrimitive triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -433,13 +433,13 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
printf("Executing test %d / %d\n", iteration, iterationCount);
}
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Render some content, just so we don't read back uninitialized data.
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
@@ -448,9 +448,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
test->readPixelsFinished = true;
}, this);
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
api.commit(swapChain);
api.endFrame(0);
getDriverApi().readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
flushAndWait();
getDriver().purge();
@@ -460,7 +460,11 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
free(buffer);
api.finish();
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(texture);
getDriverApi().finish();
executeCommands();
}
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -67,11 +66,10 @@ using namespace filament::backend;
// Rendering an external image without setting any data should not crash.
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto& api = getDriverApi();
Cleanup cleanup(api);
TrianglePrimitive triangle(api);
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
@@ -84,22 +82,22 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
// 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({
backend::Handle<HwProgram> program = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = 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(
backend::Handle<HwTexture> texture = api.createTexture(
SamplerType::SAMPLER_EXTERNAL, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -107,7 +105,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
view.width, // width
view.height, // height
1, // depth
usage)); // usage
usage); // usage
RenderPassParams params = {};
fullViewport(params);
@@ -142,6 +140,15 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
api.stopCapture(0);
// Delete our resources.
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyTexture(texture);
// Destroy frame resources.
api.destroyProgram(program);
api.destroyRenderTarget(defaultRenderTarget);
api.finish();
executeCommands();
@@ -149,11 +156,10 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
TEST_F(BackendTest, RenderExternalImage) {
auto& api = getDriverApi();
Cleanup cleanup(api);
TrianglePrimitive triangle(api);
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
@@ -166,18 +172,18 @@ TEST_F(BackendTest, RenderExternalImage) {
// 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)));
auto program = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
// require users to create two Filament textures and have two material parameters
// add a "plane" parameter to setExternalImage
@@ -221,8 +227,8 @@ TEST_F(BackendTest, RenderExternalImage) {
api.setupExternalImage(pixBuffer);
backend::Handle<HwTexture> texture =
cleanup.add(api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer));
api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer);
// We're now free to release the buffer.
CVBufferRelease(pixBuffer);
@@ -263,6 +269,15 @@ TEST_F(BackendTest, RenderExternalImage) {
api.stopCapture(0);
// Delete our resources.
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyTexture(texture);
// Destroy frame resources.
api.destroyProgram(program);
api.destroyRenderTarget(defaultRenderTarget);
api.finish();
executeCommands();
+26 -19
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -56,7 +55,6 @@ TEST_F(BackendTest, ScissorViewportRegion) {
constexpr int kSrcRtHeight = 384;
api.startCapture(0);
Cleanup cleanup(api);
// color texture (mip level 1) 512x512 depth texture (mip level 0) 512x512
// +----------------------------------------+ +------------------------------------------+
@@ -82,21 +80,21 @@ TEST_F(BackendTest, ScissorViewportRegion) {
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
ProgramHandle program = api.createProgram(std::move(p));
// Create source color and depth textures.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> depthTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
Handle<HwTexture> depthTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH16, 1, 512, 512, 1,
TextureUsage::DEPTH_ATTACHMENT));
TextureUsage::DEPTH_ATTACHMENT);
// Render into the bottom-left quarter of the texture.
Viewport srcRect = {
@@ -114,13 +112,13 @@ TEST_F(BackendTest, ScissorViewportRegion) {
// We purposely set the render target width and height to smaller than the texture, to check
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1, 0,
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {}));
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {});
Handle<HwRenderTarget> fullRenderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
Handle<HwRenderTarget> fullRenderTarget = api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1, 0,
{srcTexture, kSrcLevel, 0}, {}, {}));
{srcTexture, kSrcLevel, 0}, {}, {});
TrianglePrimitive triangle(api);
@@ -152,6 +150,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
}
// Wait for the ReadPixels result to come back.
@@ -166,24 +169,23 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
auto& api = getDriverApi();
api.startCapture(0);
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
ProgramHandle program = api.createProgram(std::move(p));
// Create a source color textures.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
// Render into the bottom-left quarter of the texture, checking 3 special cases.
// 1. negative viewport left/bottom
@@ -205,9 +207,9 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
Viewport scissor = {0, 0, (uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max()};
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 512, 512, 1, 0,
{srcTexture, 0, 0}, {}, {}));
{srcTexture, 0, 0}, {}, {});
TrianglePrimitive triangle(api);
@@ -247,6 +249,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
// Wait for the ReadPixels result to come back.
+47 -38
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -64,23 +63,20 @@ namespace test {
class BasicStencilBufferTest : public BackendTest {
public:
Handle<HwSwapChain> mSwapChain;
ProgramHandle mProgram;
Cleanup mCleanup;
BasicStencilBufferTest() : mCleanup(getDriverApi()) {}
Handle<HwSwapChain> swapChain;
ProgramHandle program;
void SetUp() override {
auto& api = getDriverApi();
// Create a platform-specific SwapChain and make it current.
mSwapChain = mCleanup.add(createSwapChain());
api.makeCurrent(mSwapChain, mSwapChain);
swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
mProgram = mCleanup.add(api.createProgram(std::move(p)));
program = api.createProgram(std::move(p));
}
void RunTest(Handle<HwRenderTarget> renderTarget) {
@@ -107,13 +103,13 @@ public:
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = mProgram;
ps.program = program;
ps.rasterState.colorWrite = false;
ps.rasterState.depthWrite = false;
ps.stencilState.stencilWrite = true;
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
api.makeCurrent(mSwapChain, mSwapChain);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, params);
@@ -133,27 +129,29 @@ public:
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.commit(mSwapChain);
api.commit(swapChain);
api.endFrame(0);
}
void TearDown() override {
auto& api = getDriverApi();
api.destroyProgram(program);
api.destroySwapChain(swapChain);
flushAndWait();
}
};
TEST_F(BasicStencilBufferTest, StencilBuffer) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create two textures: a color and a stencil, and an associated RenderTarget.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto stencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto stencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {}, {{stencilTexture}}));
{{colorTexture}}, {}, {{stencilTexture}});
RunTest(renderTarget);
@@ -161,20 +159,23 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
flushAndWait();
getDriver().purge();
api.destroyTexture(colorTexture);
api.destroyTexture(stencilTexture);
api.destroyRenderTarget(renderTarget);
}
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create two textures: a color and a stencil, and an associated RenderTarget.
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));
auto renderTarget = cleanup.add(api.createRenderTarget(
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto depthStencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}});
RunTest(renderTarget);
@@ -182,27 +183,30 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
flushAndWait();
getDriver().purge();
api.destroyTexture(colorTexture);
api.destroyTexture(depthStencilTexture);
api.destroyRenderTarget(renderTarget);
}
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create two textures: a single-sampled color and a MSAA stencil texture.
// We also create two RenderTargets, one for each pass:
// Pass 0: Render a triangle only into the MSAA stencil buffer.
// 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));
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget0 = cleanup.add(api.createRenderTarget(
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE);
auto depthStencilTextureMSAA = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
auto renderTarget0 = getDriverApi().createRenderTarget(
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
auto renderTarget1 = cleanup.add(api.createRenderTarget(
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
auto renderTarget1 = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
api.startCapture(0);
@@ -226,13 +230,13 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = mProgram;
ps.program = program;
ps.rasterState.colorWrite = false;
ps.rasterState.depthWrite = false;
ps.stencilState.stencilWrite = true;
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
api.makeCurrent(mSwapChain, mSwapChain);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget0, params);
@@ -254,7 +258,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.commit(mSwapChain);
api.commit(swapChain);
api.stopCapture(0);
api.endFrame(0);
@@ -262,6 +266,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
flushAndWait();
getDriver().purge();
api.destroyTexture(colorTexture);
api.destroyTexture(depthStencilTextureMSAA);
api.destroyRenderTarget(renderTarget0);
api.destroyRenderTarget(renderTarget1);
}
} // namespace test
-13
View File
@@ -25,7 +25,6 @@
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
@@ -91,21 +90,9 @@ public:
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this BufferObject
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
-7
View File
@@ -20,7 +20,6 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <stddef.h>
@@ -62,17 +61,11 @@ UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, siz
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
public:
UTILS_DEPRECATED
Builder& name(const char* name, size_t len) noexcept {
builderMakeName(mName, name, len);
return static_cast<Builder&>(*this);
}
Builder& name(utils::StaticString const& name) noexcept {
builderMakeName(mName, name.data(), name.size());
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
private:
-13
View File
@@ -26,7 +26,6 @@
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
@@ -96,21 +95,9 @@ public:
* @param name A string to identify this IndexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this IndexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this IndexBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
@@ -21,7 +21,6 @@
#include <filament/Engine.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -83,21 +82,9 @@ public:
* @param name A string to identify this InstanceBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this InstanceBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this InstanceBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
@@ -22,7 +22,6 @@
#include <filament/Engine.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -76,21 +75,9 @@ public:
* @param name A string to identify this MorphTargetBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this MorphTargetBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
@@ -22,7 +22,6 @@
#include <filament/RenderableManager.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -82,21 +81,9 @@ public:
* @param name A string to identify this SkinningBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this SkinningBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this SkinningBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
-13
View File
@@ -23,7 +23,6 @@
#include <backend/CallbackHandler.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mat3.h>
@@ -151,21 +150,9 @@ public:
* @param name A string to identify this Stream
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this Stream for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this Stream
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the Stream object and returns a pointer to it.
*
+1 -14
View File
@@ -26,7 +26,6 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <utility>
@@ -230,20 +229,8 @@ public:
* @param name A string to identify this Texture
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this Texture for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this Texture
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Creates an external texture. The content must be set using setExternalImage().
-13
View File
@@ -26,7 +26,6 @@
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stddef.h>
#include <stdint.h>
@@ -171,21 +170,9 @@ public:
* @param name A string to identify this VertexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this VertexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this VertexBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
+3 -13
View File
@@ -2341,9 +2341,6 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
}
void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
bindPostProcessDescriptorSet(driver);
FEngine const& engine = mEngine;
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
@@ -3039,7 +3036,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg, bool sourceHasLuminance,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc) noexcept {
filament::Viewport const&, FrameGraphTexture::Descriptor const& outDesc) noexcept {
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
@@ -3055,7 +3052,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
.attachments = { .color = { data.output } },
.clearFlags = TargetBufferFlags::DEPTH });
},
[this, vp, sourceHasLuminance](FrameGraphResources const& resources,
[this, sourceHasLuminance](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
@@ -3081,17 +3078,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
});
mi->setParameter("viewport", float4{
float(vp.left) / inputDesc.width,
float(vp.bottom) / inputDesc.height,
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->setParameter("viewportInfo", float4{
1.0f / inputDesc.width,
1.0f / inputDesc.height,
float(inputDesc.width),
float(inputDesc.height)
float(inputDesc.height),
});
mi->commit(driver);
-5
View File
@@ -21,7 +21,6 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
#include <utils/StaticString.h>
namespace filament {
@@ -52,10 +51,6 @@ BufferObject::Builder& BufferObject::Builder::name(const char* name, size_t cons
return BuilderNameMixin::name(name, len);
}
BufferObject::Builder& BufferObject::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
BufferObject* BufferObject::Builder::build(Engine& engine) {
return downcast(engine).createBufferObject(*this);
}
-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;
-5
View File
@@ -21,7 +21,6 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
#include <utils/StaticString.h>
namespace filament {
@@ -52,10 +51,6 @@ IndexBuffer::Builder& IndexBuffer::Builder::name(const char* name, size_t const
return BuilderNameMixin::name(name, len);
}
IndexBuffer::Builder& IndexBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
IndexBuffer* IndexBuffer::Builder::build(Engine& engine) {
return downcast(engine).createIndexBuffer(*this);
}
-6
View File
@@ -21,8 +21,6 @@
#include "FilamentAPI-impl.h"
#include <utils/StaticString.h>
#include <math/mat3.h>
#include <math/vec3.h>
@@ -56,10 +54,6 @@ InstanceBuffer::Builder& InstanceBuffer::Builder::name(const char* name, size_t
return BuilderNameMixin::name(name, len);
}
InstanceBuffer::Builder& InstanceBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount >= 1) << "instanceCount must be >= 1.";
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount <= engine.getMaxAutomaticInstances())
@@ -26,7 +26,6 @@
#include <math/norm.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
namespace filament {
@@ -60,10 +59,6 @@ MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::name(const char* name, s
return BuilderNameMixin::name(name, len);
}
MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
MorphTargetBuffer* MorphTargetBuffer::Builder::build(Engine& engine) {
return downcast(engine).createMorphTargetBuffer(*this);
}
+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,
-5
View File
@@ -26,7 +26,6 @@
#include <math/mat4.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <string.h>
#include <stddef.h>
@@ -65,10 +64,6 @@ SkinningBuffer::Builder& SkinningBuffer::Builder::name(const char* name, size_t
return BuilderNameMixin::name(name, len);
}
SkinningBuffer::Builder& SkinningBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
SkinningBuffer* SkinningBuffer::Builder::build(Engine& engine) {
return downcast(engine).createSkinningBuffer(*this);
}
-5
View File
@@ -24,7 +24,6 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <utils/Panic.h>
#include <filament/Stream.h>
@@ -66,10 +65,6 @@ Stream::Builder& Stream::Builder::name(const char* name, size_t const len) noexc
return BuilderNameMixin::name(name, len);
}
Stream::Builder& Stream::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
Stream* Stream::Builder::build(Engine& engine) {
return downcast(engine).createStream(*this);
}
-6
View File
@@ -41,8 +41,6 @@
#include <utils/algorithm.h>
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
@@ -154,10 +152,6 @@ Texture::Builder& Texture::Builder::name(const char* name, size_t const len) noe
return BuilderNameMixin::name(name, len);
}
Texture::Builder& Texture::Builder::name(StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
Texture* Texture::Builder::build(Engine& engine) {
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
-5
View File
@@ -30,7 +30,6 @@
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -153,10 +152,6 @@ VertexBuffer::Builder& VertexBuffer::Builder::name(const char* name, size_t cons
return BuilderNameMixin::name(name, len);
}
VertexBuffer::Builder& VertexBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
FILAMENT_CHECK_PRECONDITION(mImpl->mVertexCount > 0) << "vertexCount cannot be 0";
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount > 0) << "bufferCount cannot be 0";
+4 -9
View File
@@ -10,11 +10,6 @@ material {
type : float4,
name : viewport,
precision: high
},
{
type : float4,
name : viewportInfo,
precision: high
}
],
variables : [
@@ -27,8 +22,8 @@ material {
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy);
postProcess.normalizedUV = uvToRenderTargetUV(postProcess.normalizedUV);
postProcess.vertex.xy = postProcess.normalizedUV;
}
}
@@ -43,7 +38,7 @@ fragment {
#define gather textureGather
#else
vec4 gather(const mediump sampler2D color, highp vec2 p, const int comp) {
highp ivec2 i = ivec2(p * materialParams.viewportInfo.zw - 0.5);
highp ivec2 i = ivec2(p * materialParams.viewport.zw - 0.5);
vec4 d;
d[0] = texelFetchOffset(color, i, 0, ivec2(0, 1))[comp];
d[1] = texelFetchOffset(color, i, 0, ivec2(1, 1))[comp];
@@ -79,7 +74,7 @@ fragment {
#include "sgsr1_shader_mobile.fs"
void postProcess(inout PostProcessInputs postProcess) {
highp vec4 vp[1] = vec4[1]( materialParams.viewportInfo );
highp vec4 vp[1] = vec4[1]( materialParams.viewport );
postProcess.color = sgsr(materialParams_color, variable_vertex.xy, vp);
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.59.0"
spec.version = "1.58.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.0/filament-v1.59.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.1/filament-v1.58.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 59;
static constexpr size_t MATERIAL_VERSION = 58;
/**
* Supported shading models
+39 -34
View File
@@ -517,13 +517,28 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
MetalArgumentBuffer::destroy(&argBuffer);
}
}
void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t> &spirv) {
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();
uint32_t* data = spirv.data();
std::set<uint32_t> samplerTargetIDs;
struct Descriptor {
uint32_t binding = 0xFFFFFFFF;
bool fixed = false;
};
std::unordered_map<uint32_t, Descriptor> descriptors;
std::set<uint32_t> unidentifiedLoadTargets;
std::vector<uint32_t> bindingTargets;
auto getDesc = [&](uint32_t var) -> Descriptor& {
using itertype = std::unordered_map<uint32_t, Descriptor>::iterator;
itertype itr;
if (itr = descriptors.find(var); itr == descriptors.end()) {
itr = descriptors.insert({var, {}}).first;
}
return itr->second;
};
auto pass = [&](uint32_t targetOp, std::function<void(uint32_t)> f) {
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
@@ -531,35 +546,33 @@ void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t> &spirv)
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
if (targetOp == op) {
f(cursor + 1);
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.
uint32_t samplerTypeId = 0;
pass(spv::Op::OpName, [&](uint32_t pos) {
auto target = data[pos];
char *name = (char *) &data[pos + 1];
char* name = (char*)&data[pos+1];
std::string_view view(name);
if (view.find("_sampler") != std::string_view::npos) {
samplerTargetIDs.insert(target);
unidentifiedLoadTargets.insert(target);
}
});
});
// Write out the offset bindings
// Write out the offsetted 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;
if (unidentifiedLoadTargets.find(targetVar) != unidentifiedLoadTargets.end()) {
data[pos + 2] = data[pos + 2]*2 + 1;
}
else {
data[pos + 2] = data[pos + 2] *2;
}
}
});
@@ -570,9 +583,12 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
//We need to run some opt-passes at all times to transpile to WGSL
auto optimizer = createEmptyOptimizer();
optimizer->RegisterPass(CreateSplitCombinedImageSamplerPass());
//optimizer->RegisterPass(CreateResolveBindingConflictsPass());
optimizeSpirv(optimizer, *spirv);
//After splitting the image samplers, we need to remap the bindings to separate them.
//TODO: This has issues, so use CreateResolveBindingConflictsPass instead. Mapping is less predictable but short term
rebindImageSamplerForWGSL(*spirv);
//Allow non-uniform derivitives due to our nested shaders. See https://github.com/gpuweb/gpuweb/issues/3479
@@ -595,10 +611,8 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
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;
slog.e << "Beginning SpirV-output dump\n\n" << text->str << "\n\nEndSPIRV\n" << io::endl;
spvTextDestroy(text);
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
#endif
}
@@ -608,7 +622,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;
@@ -1020,9 +1034,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 +1044,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,
@@ -1050,8 +1056,8 @@ 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(CreateMergeReturnPass(), MaterialBuilder::TargetApi::METAL | MaterialBuilder::TargetApi::WEBGPU);
RegisterPass(CreateInlineExhaustivePass());
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreatePrivateToLocalPass());
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
@@ -1095,8 +1101,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
View File
@@ -37,7 +37,6 @@ set(DIST_HDRS
${PUBLIC_HDR_DIR}/${TARGET}/PrivateImplementation-impl.h
${PUBLIC_HDR_DIR}/${TARGET}/SingleInstanceComponentManager.h
${PUBLIC_HDR_DIR}/${TARGET}/Slice.h
${PUBLIC_HDR_DIR}/${TARGET}/StaticString.h
${PUBLIC_HDR_DIR}/${TARGET}/StructureOfArrays.h
${PUBLIC_HDR_DIR}/${TARGET}/Systrace.h
${PUBLIC_HDR_DIR}/${TARGET}/sstream.h
+7 -6
View File
@@ -22,8 +22,6 @@
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <string_view>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
@@ -199,13 +197,16 @@ private:
};
int compare(const CString& rhs) const noexcept {
auto const l = std::string_view{data(), size()};
auto const r = std::string_view{rhs.data(), rhs.size()};
return l.compare(r);
size_type const lhs_size = size();
size_type const rhs_size = rhs.size();
if (lhs_size < rhs_size) return -1;
if (lhs_size > rhs_size) return 1;
return strncmp(data(), rhs.data(), size());
}
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) == 0;
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs == rhs);
-110
View File
@@ -1,110 +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_UTILS_STATICSTRING_H
#define TNT_UTILS_STATICSTRING_H
#include <utils/compiler.h>
#include <type_traits>
#include <string_view>
#include <stddef.h>
namespace utils {
/**
* @brief A lightweight string class that stores a pointer to a string literal and its size, without dynamic allocation.
*
* This class is designed to efficiently represent string literals. It does not allocate any memory
* and instead relies on the compiler to manage the memory of the string literals.
*/
class UTILS_PUBLIC StaticString {
public:
using value_type = std::string_view::value_type;
using const_pointer = std::string_view::const_pointer;
using const_reference = std::string_view::const_reference;
using size_type = std::string_view::size_type;
using const_iterator = std::string_view::const_iterator;
// Constructor from string literal
template <size_t M>
constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor)
constexpr StaticString() noexcept = default;
constexpr const_pointer c_str() const noexcept { return mString.data(); }
constexpr const_pointer data() const noexcept { return mString.data(); }
constexpr size_type size() const noexcept { return mString.size(); }
constexpr size_type length() const noexcept { return mString.size(); }
constexpr bool empty() const noexcept { return mString.empty(); }
constexpr const_iterator begin() const noexcept { return mString.begin(); }
constexpr const_iterator end() const noexcept { return mString.end(); }
constexpr const_iterator cbegin() const noexcept { return mString.begin(); }
constexpr const_iterator cend() const noexcept { return mString.end(); }
constexpr const_reference operator[](size_type const pos) const noexcept {
return mString[pos];
}
constexpr const_reference at(size_type pos) const {
return mString[pos];
}
constexpr const_reference front() const noexcept {
return mString.front();
}
constexpr const_reference back() const noexcept {
return mString.back();
}
constexpr int compare(const StaticString& rhs) const noexcept {
return mString.compare(rhs.mString);
}
private:
std::string_view mString;
friend constexpr bool operator==(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString == rhs.mString;
}
friend constexpr bool operator!=(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString != rhs.mString;
}
friend constexpr bool operator<(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString < rhs.mString;
}
friend constexpr bool operator>(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString > rhs.mString;
}
friend constexpr bool operator<=(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString <= rhs.mString;
}
friend constexpr bool operator>=(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString >= rhs.mString;
}
};
} // namespace utils
#endif // TNT_UTILS_STATICSTRING_H
+3 -3
View File
@@ -129,14 +129,14 @@ bool isDoubleSided() {
/**
* Returns the cascade index for this fragment (between 0 and CONFIG_MAX_SHADOW_CASCADES - 1).
*/
int getShadowCascade() {
uint getShadowCascade() {
highp float z = mulMat4x4Float3(getViewFromWorldMatrix(), getWorldPosition()).z;
ivec4 greaterZ = ivec4(greaterThan(frameUniforms.cascadeSplits, vec4(z)));
int cascadeCount = frameUniforms.cascades & 0xF;
uint cascadeCount = frameUniforms.cascades & 0xF;
return clamp(greaterZ.x + greaterZ.y + greaterZ.z + greaterZ.w, 0, cascadeCount - 1);
}
highp vec4 getCascadeLightSpacePosition(int cascade) {
highp vec4 getCascadeLightSpacePosition(uint cascade) {
// For the first cascade, return the interpolated light space position.
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
// the matrix multiply inside computeLightSpacePosition.
+2 -2
View File
@@ -52,12 +52,12 @@ void evaluateDirectionalLight(const MaterialInputs material,
if (light.NoL > 0.0) {
float ssContactShadowOcclusion = 0.0;
int cascade = getShadowCascade();
uint cascade = getShadowCascade();
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
highp vec4 shadowPosition = getShadowPosition(cascade);
visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0);
visibility = shadow(true, sampler0_shadowMap, int(cascade), shadowPosition, 0.0);
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
applyShadowStrength(visibility, material.shadowStrength);
#endif
+1 -1
View File
@@ -41,7 +41,7 @@ vec4 evaluateMaterial(const MaterialInputs material) {
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
#if defined(VARIANT_HAS_SHADOWING)
float visibility = 1.0;
int cascade = getShadowCascade();
uint cascade = getShadowCascade();
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
+1 -1
View File
@@ -485,7 +485,7 @@ float ShadowSample_VSM(const bool ELVSM, const highp sampler2DArray shadowMap,
// get texture coordinate for directional and spot shadow maps
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
highp vec4 getShadowPosition(const int cascade) {
highp vec4 getShadowPosition(const uint cascade) {
return getCascadeLightSpacePosition(cascade);
}
#endif
-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()

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