Compare commits
8 Commits
ebridgewat
...
pf/test-ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8f088140c | ||
|
|
1c72d108fb | ||
|
|
9d18cc35e0 | ||
|
|
2e37ebe70c | ||
|
|
63ef412be1 | ||
|
|
f0996296c1 | ||
|
|
a60d570b3b | ||
|
|
3fdb9f311a |
34
.github/workflows/presubmit.yml
vendored
34
.github/workflows/presubmit.yml
vendored
@@ -19,6 +19,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run build script
|
||||
run: |
|
||||
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
||||
@@ -44,6 +46,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
@@ -60,6 +64,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh presubmit
|
||||
@@ -122,3 +128,31 @@ jobs:
|
||||
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
|
||||
code-correcteness:
|
||||
name: code-correctness
|
||||
runs-on: 'macos-14-xlarge'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install prerequisites
|
||||
run: |
|
||||
pip install pyyaml
|
||||
brew install llvm
|
||||
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
|
||||
- name: Run build script
|
||||
# We need to build before clang-tidy can run analysis
|
||||
run: |
|
||||
# This will build for all three desktop backends on mac
|
||||
./build.sh -p desktop debug gltf_viewer
|
||||
- name: Run test
|
||||
run: |
|
||||
bash test/code-correctness/test.sh
|
||||
|
||||
@@ -142,16 +142,13 @@ abstract class MaterialCompiler extends TaskWithBinary {
|
||||
if (!exclude_vulkan) {
|
||||
matcArgs += ['-a', 'vulkan']
|
||||
}
|
||||
|
||||
def include_webgpu = providers
|
||||
.gradleProperty("com.google.android.filament.include-webgpu")
|
||||
.forUseAtConfigurationTime().present
|
||||
|
||||
if (!include_webgpu) {
|
||||
matcArgs += ['-a', 'webgpu']
|
||||
if (include_webgpu) {
|
||||
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
|
||||
}
|
||||
|
||||
|
||||
def mat_no_opt = providers
|
||||
.gradleProperty("com.google.android.filament.matnopt")
|
||||
.forUseAtConfigurationTime().present
|
||||
|
||||
@@ -177,29 +177,6 @@ endif()
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/VulkanPlatform.h
|
||||
src/vulkan/VulkanDescriptorSetCache.cpp
|
||||
src/vulkan/VulkanDescriptorSetCache.h
|
||||
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
|
||||
src/vulkan/VulkanDescriptorSetLayoutCache.h
|
||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/memory/ResourceManager.cpp
|
||||
src/vulkan/memory/ResourceManager.h
|
||||
src/vulkan/memory/ResourcePointer.h
|
||||
src/vulkan/memory/Resource.cpp
|
||||
src/vulkan/memory/Resource.h
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
src/vulkan/utils/Conversion.cpp
|
||||
src/vulkan/utils/Conversion.h
|
||||
src/vulkan/utils/Definitions.h
|
||||
src/vulkan/utils/Helper.h
|
||||
src/vulkan/utils/Image.h
|
||||
src/vulkan/utils/Image.cpp
|
||||
src/vulkan/utils/Spirv.h
|
||||
src/vulkan/utils/Spirv.cpp
|
||||
src/vulkan/utils/StaticVector.h
|
||||
src/vulkan/VulkanAsyncHandles.h
|
||||
src/vulkan/VulkanBlitter.cpp
|
||||
src/vulkan/VulkanBlitter.h
|
||||
@@ -210,31 +187,56 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanConstants.h
|
||||
src/vulkan/VulkanContext.cpp
|
||||
src/vulkan/VulkanContext.h
|
||||
src/vulkan/VulkanDescriptorSetCache.cpp
|
||||
src/vulkan/VulkanDescriptorSetCache.h
|
||||
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
|
||||
src/vulkan/VulkanDescriptorSetLayoutCache.h
|
||||
src/vulkan/VulkanDriver.cpp
|
||||
src/vulkan/VulkanDriver.h
|
||||
src/vulkan/VulkanDriverFactory.h
|
||||
src/vulkan/VulkanExternalImageManager.cpp
|
||||
src/vulkan/VulkanExternalImageManager.h
|
||||
src/vulkan/VulkanFboCache.cpp
|
||||
src/vulkan/VulkanFboCache.h
|
||||
src/vulkan/VulkanHandles.cpp
|
||||
src/vulkan/VulkanHandles.h
|
||||
src/vulkan/VulkanMemory.h
|
||||
src/vulkan/VulkanMemory.cpp
|
||||
src/vulkan/VulkanMemory.h
|
||||
src/vulkan/VulkanPipelineCache.cpp
|
||||
src/vulkan/VulkanPipelineCache.h
|
||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/VulkanQueryManager.cpp
|
||||
src/vulkan/VulkanQueryManager.h
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanSamplerCache.cpp
|
||||
src/vulkan/VulkanSamplerCache.h
|
||||
src/vulkan/VulkanStagePool.cpp
|
||||
src/vulkan/VulkanStagePool.h
|
||||
src/vulkan/VulkanSwapChain.cpp
|
||||
src/vulkan/VulkanSwapChain.h
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanTexture.cpp
|
||||
src/vulkan/VulkanTexture.h
|
||||
src/vulkan/VulkanYcbcrConversionCache.cpp
|
||||
src/vulkan/VulkanYcbcrConversionCache.h
|
||||
src/vulkan/memory/Resource.cpp
|
||||
src/vulkan/memory/Resource.h
|
||||
src/vulkan/memory/ResourceManager.cpp
|
||||
src/vulkan/memory/ResourceManager.h
|
||||
src/vulkan/memory/ResourcePointer.h
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
src/vulkan/utils/Conversion.cpp
|
||||
src/vulkan/utils/Conversion.h
|
||||
src/vulkan/utils/Definitions.h
|
||||
src/vulkan/utils/Helper.h
|
||||
src/vulkan/utils/Image.cpp
|
||||
src/vulkan/utils/Image.h
|
||||
src/vulkan/utils/Spirv.cpp
|
||||
src/vulkan/utils/Spirv.h
|
||||
src/vulkan/utils/StaticVector.h
|
||||
)
|
||||
if (LINUX OR WIN32)
|
||||
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
|
||||
@@ -257,8 +259,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
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)
|
||||
@@ -508,6 +508,7 @@ if (APPLE OR LINUX)
|
||||
test/ImageExpectations.cpp
|
||||
test/Lifetimes.cpp
|
||||
test/Shader.cpp
|
||||
test/SharedShaders.cpp
|
||||
test/test_FeedbackLoops.cpp
|
||||
test/test_Blit.cpp
|
||||
test/test_MissingRequiredAttributes.cpp
|
||||
|
||||
@@ -149,6 +149,13 @@ public:
|
||||
* - PlatformEGLAndroid
|
||||
*/
|
||||
bool assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired. If true, the
|
||||
* frame is aborted instead of panic. This is only supported for:
|
||||
* - PlatformMetal
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -104,10 +104,6 @@ public:
|
||||
|
||||
// Semaphore to be signaled once the image is available.
|
||||
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
|
||||
|
||||
// A function called right before vkQueueSubmit. After this call, the image must be
|
||||
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
|
||||
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
|
||||
};
|
||||
|
||||
VulkanPlatform();
|
||||
@@ -298,6 +294,16 @@ public:
|
||||
VkQueue getProtectedGraphicsQueue() const noexcept;
|
||||
|
||||
struct ExternalImageMetadata {
|
||||
/**
|
||||
* The Filament texture format.
|
||||
*/
|
||||
TextureFormat filamentFormat;
|
||||
|
||||
/**
|
||||
* The Filament texture usage.
|
||||
*/
|
||||
TextureUsage filamentUsage;
|
||||
|
||||
/**
|
||||
* The width of the external image
|
||||
*/
|
||||
@@ -308,11 +314,6 @@ public:
|
||||
*/
|
||||
uint32_t height;
|
||||
|
||||
/**
|
||||
* The layerCount of the external image
|
||||
*/
|
||||
uint32_t layerCount;
|
||||
|
||||
/**
|
||||
* The layer count of the external image
|
||||
*/
|
||||
@@ -328,11 +329,6 @@ public:
|
||||
*/
|
||||
VkFormat format;
|
||||
|
||||
/**
|
||||
* An external buffer can be protected. This tells you if it is.
|
||||
*/
|
||||
bool isProtected;
|
||||
|
||||
/**
|
||||
* The type of external format (opaque int) if used.
|
||||
*/
|
||||
@@ -352,20 +348,44 @@ public:
|
||||
* Heap information
|
||||
*/
|
||||
uint32_t memoryTypeBits;
|
||||
|
||||
/**
|
||||
* Ycbcr conversion components
|
||||
*/
|
||||
VkComponentMapping ycbcrConversionComponents;
|
||||
|
||||
/**
|
||||
* Ycbcr model
|
||||
*/
|
||||
VkSamplerYcbcrModelConversion ycbcrModel;
|
||||
|
||||
/**
|
||||
* Ycbcr range
|
||||
*/
|
||||
VkSamplerYcbcrRange ycbcrRange;
|
||||
|
||||
/**
|
||||
* Ycbcr x chroma offset
|
||||
*/
|
||||
VkChromaLocation xChromaOffset;
|
||||
|
||||
/**
|
||||
* Ycbcr y chroma offset
|
||||
*/
|
||||
VkChromaLocation yChromaOffset;
|
||||
};
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||
|
||||
|
||||
// Note that the image metadata might change per-frame, hence we need a method for extracting
|
||||
// it.
|
||||
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
||||
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat);
|
||||
|
||||
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle);
|
||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
|
||||
return { VK_NULL_HANDLE, VK_NULL_HANDLE };
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||
@@ -378,20 +398,6 @@ private:
|
||||
// Platform dependent helper methods
|
||||
static ExtensionSet getSwapchainInstanceExtensionsImpl();
|
||||
|
||||
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device);
|
||||
|
||||
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
static VkSampler createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
static VkImageView createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle);
|
||||
|
||||
// Platform dependent helper methods
|
||||
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) noexcept;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace filament::backend {
|
||||
|
||||
class VulkanPlatformAndroid : public VulkanPlatform {
|
||||
public:
|
||||
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
||||
bool sRGB) noexcept;
|
||||
|
||||
struct UTILS_PUBLIC ExternalImageDescAndroid {
|
||||
@@ -39,31 +39,26 @@ public:
|
||||
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
|
||||
ExternalImageHandleRef externalImage) const noexcept;
|
||||
|
||||
virtual ExternalImageMetadata extractExternalImageMetadata(
|
||||
ExternalImageHandleRef image) const override;
|
||||
|
||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
|
||||
|
||||
protected:
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
|
||||
|
||||
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) const noexcept override;
|
||||
|
||||
private:
|
||||
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
|
||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||
bool sRGB = false;
|
||||
unsigned int width; // Texture width
|
||||
unsigned int height; // Texture height
|
||||
TextureFormat format;// Texture format
|
||||
TextureUsage usage; // Texture usage flags
|
||||
|
||||
protected:
|
||||
~ExternalImageVulkanAndroid() override;
|
||||
};
|
||||
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||
|
||||
using ImageData = VulkanPlatform::ImageData;
|
||||
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||
|
||||
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) const noexcept;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -45,6 +45,9 @@ PlatformMetal::~PlatformMetal() noexcept {
|
||||
}
|
||||
|
||||
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
|
||||
? DrawableFailureBehavior::ABORT_FRAME
|
||||
: DrawableFailureBehavior::PANIC;
|
||||
return MetalDriverFactory::create(this, driverConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_VALIDATION)
|
||||
#else
|
||||
#define FVK_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
@@ -70,13 +70,11 @@ struct VulkanRenderPass {
|
||||
// context are stored in VulkanPlatform.
|
||||
struct VulkanContext {
|
||||
public:
|
||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
||||
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
||||
assert_invariant(isProtectedMemorySupported() == true);
|
||||
}
|
||||
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties,
|
||||
uint32_t flags, VkFlags reqs) {
|
||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
||||
if (flags & 1) {
|
||||
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||
if ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -85,6 +83,13 @@ public:
|
||||
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
||||
}
|
||||
|
||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
||||
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
||||
assert_invariant(isProtectedMemorySupported());
|
||||
}
|
||||
return selectMemoryType(mMemoryProperties, flags, reqs);
|
||||
}
|
||||
|
||||
inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const {
|
||||
return mDepthStencilFormats;
|
||||
}
|
||||
@@ -118,7 +123,7 @@ public:
|
||||
}
|
||||
|
||||
inline bool isMultiviewEnabled() const noexcept {
|
||||
return mMultiviewEnabled;
|
||||
return mPhysicalDeviceVk11Features.multiview == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isClipDistanceSupported() const noexcept {
|
||||
@@ -142,6 +147,9 @@ private:
|
||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
};
|
||||
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
};
|
||||
@@ -154,7 +162,6 @@ private:
|
||||
};
|
||||
bool mDebugMarkersSupported = false;
|
||||
bool mDebugUtilsSupported = false;
|
||||
bool mMultiviewEnabled = false;
|
||||
bool mLazilyAllocatedMemorySupported = false;
|
||||
bool mProtectedMemorySupported = false;
|
||||
|
||||
|
||||
@@ -282,18 +282,25 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
fvkutils::DescriptorSetMask curMask = setMask;
|
||||
|
||||
auto& updateSets = mStashedSets;
|
||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
|
||||
|
||||
setMask.forEachSetBit([&](size_t index) {
|
||||
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
|
||||
curMask.unset(index);
|
||||
if (pipelineLayoutIsSame) {
|
||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||
setMask.forEachSetBit([&](size_t index) {
|
||||
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
if (curMask.none() &&
|
||||
mLastBoundInfo.setMask == setMask && mLastBoundInfo.boundSets == updateSets) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (curMask.none() &&
|
||||
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
|
||||
mLastBoundInfo.boundSets == updateSets)) {
|
||||
return;
|
||||
} else {
|
||||
setMask.forEachSetBit([&](size_t index) {
|
||||
if (!updateSets[index]) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
|
||||
@@ -301,7 +308,7 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
auto set = updateSets[index];
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||
1, &set->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||
commands->acquire(set);
|
||||
});
|
||||
|
||||
@@ -329,7 +336,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
||||
}
|
||||
VkWriteDescriptorSet const descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = set->vkSet,
|
||||
.dstSet = set->getVkSet(),
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
@@ -342,10 +349,6 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
||||
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||
VkSampler sampler) noexcept {
|
||||
VkDescriptorImageInfo info{
|
||||
.sampler = sampler,
|
||||
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||
};
|
||||
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
||||
VkImageViewType const expectedType = texture->getViewType();
|
||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||
@@ -355,12 +358,16 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
}
|
||||
info.imageView = texture->getView(range);
|
||||
VkDescriptorImageInfo info{
|
||||
.sampler = sampler,
|
||||
.imageView = texture->getView(range),
|
||||
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet const descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = set->vkSet,
|
||||
.dstSet = set->getVkSet(),
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
@@ -378,10 +385,10 @@ void VulkanDescriptorSetCache::updateInputAttachment(
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
|
||||
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto const vkSet = mDescriptorPool->obtainSet(layout);
|
||||
auto const vkSet = getVkSet(layout);
|
||||
auto const& count = layout->count;
|
||||
auto const vklayout = layout->getVkLayout();
|
||||
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
|
||||
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle,
|
||||
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
||||
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
||||
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
||||
@@ -390,10 +397,20 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
|
||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||
}
|
||||
});
|
||||
set->setVkSet(vkSet);
|
||||
return set;
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::gc() {
|
||||
mStashedSets = {};
|
||||
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
return mDescriptorPool->obtainSet(layout);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::manualRecyle(VulkanDescriptorSetLayout::Count const& count,
|
||||
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
|
||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
using DescriptorSetArray =
|
||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||
~VulkanDescriptorSetCache();
|
||||
@@ -68,14 +70,21 @@ public:
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
// This method is only meant to be used with external samplers (or internally within this
|
||||
// class).
|
||||
VkDescriptorSet getVkSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
// This method is only meant to be used with external samplers.
|
||||
void manualRecyle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
|
||||
VkDescriptorSet vkSet);
|
||||
|
||||
DescriptorSetArray const& getBoundSets() const { return mStashedSets; }
|
||||
|
||||
void gc();
|
||||
|
||||
private:
|
||||
class DescriptorInfinitePool;
|
||||
|
||||
using DescriptorSetArray =
|
||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
VkDevice mDevice;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
@@ -58,6 +60,56 @@ uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType t
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
|
||||
fvkutils::SamplerBitmask const& mask, fvkutils::SamplerBitmask const& external,
|
||||
utils::FixedCapacityVector<VkSampler> const& immutableSamplers) {
|
||||
using Bitmask = fvkutils::SamplerBitmask;
|
||||
uint32_t count = 0;
|
||||
Bitmask alreadySeen;
|
||||
uint8_t immutableIndex = 0;
|
||||
size_t const immutableSamplerCount = immutableSamplers.size();
|
||||
mask.forEachSetBit([&](size_t index) {
|
||||
VkShaderStageFlags stages = 0;
|
||||
uint32_t binding = 0;
|
||||
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
|
||||
binding = (uint32_t) index;
|
||||
stages |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
|
||||
if (mask.test(fragIndex)) {
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
alreadySeen.set(fragIndex);
|
||||
}
|
||||
} else if (!alreadySeen.test(index)) {
|
||||
// We are in fragment stage bits
|
||||
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
|
||||
if (stages) {
|
||||
toBind[count++] = {
|
||||
.binding = binding,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = stages,
|
||||
.pImmutableSamplers = external[index] && immutableSamplerCount > immutableIndex
|
||||
? &immutableSamplers[immutableIndex++]
|
||||
: nullptr,
|
||||
};
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
|
||||
size_t const size = samplers.size();
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
} else if (size == 1) {
|
||||
return (uint64_t) samplers[0];
|
||||
}
|
||||
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
||||
@@ -73,37 +125,44 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
|
||||
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
|
||||
LayoutKey key = {
|
||||
.bitmask = bitmasks,
|
||||
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
|
||||
};
|
||||
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
|
||||
uint32_t count = 0;
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
bitmasks.dynamicUbo);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
|
||||
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, bitmasks.externalSampler,
|
||||
immutableSamplers);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
bitmasks.inputAttachment);
|
||||
|
||||
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
|
||||
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = count,
|
||||
.pBindings = toBind,
|
||||
};
|
||||
VkDescriptorSetLayout vklayout;
|
||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
||||
mVkLayouts[key] = vklayout;
|
||||
return vklayout;
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
|
||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
|
||||
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
|
||||
info);
|
||||
VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
|
||||
auto const& bitmasks = layout->bitmask;
|
||||
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
|
||||
vklayout = itr->second;
|
||||
} else {
|
||||
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
|
||||
uint32_t count = 0;
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
bitmasks.dynamicUbo);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
bitmasks.sampler);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
bitmasks.inputAttachment);
|
||||
|
||||
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
|
||||
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.bindingCount = count,
|
||||
.pBindings = toBind,
|
||||
};
|
||||
|
||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
||||
mVkLayouts[bitmasks] = vklayout;
|
||||
}
|
||||
layout->setVkLayout(vklayout);
|
||||
layout->setVkLayout(getVkLayout(layout->bitmask));
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,11 @@
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanDescriptorSetLayoutCache {
|
||||
@@ -41,21 +40,34 @@ public:
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
// Just a wrapper around getVkLayout()
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
|
||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
|
||||
|
||||
// This method is meant to be used with external samplers
|
||||
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
|
||||
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
|
||||
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
|
||||
struct BitmaskGroupEqual {
|
||||
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
|
||||
struct LayoutKey {
|
||||
// this describes the layout using bitset.
|
||||
VulkanDescriptorSetLayout::Bitmask bitmask = {};
|
||||
// number of immutable samplers can be arbitrary; so we hash them into 64-bit.
|
||||
uint64_t immutableSamplerHash = 0;
|
||||
};
|
||||
static_assert(sizeof(LayoutKey) == 48);
|
||||
|
||||
using LayoutKeyHashFn = utils::hash::MurmurHashFn<LayoutKey>;
|
||||
struct LayoutKeyEqual {
|
||||
bool operator()(LayoutKey const& k1, LayoutKey const& k2) const {
|
||||
return k1.bitmask == k2.bitmask && k1.immutableSamplerHash == k2.immutableSamplerHash;
|
||||
}
|
||||
};
|
||||
|
||||
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
|
||||
mVkLayouts;
|
||||
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -200,9 +200,7 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
||||
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mResourceManager(
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags),
|
||||
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
||||
mPlatform->getDevice())),
|
||||
@@ -221,6 +219,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mQueryManager(mPlatform->getDevice()),
|
||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
||||
&mDescriptorSetLayoutCache),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
|
||||
@@ -313,7 +313,7 @@ void VulkanDriver::terminate() {
|
||||
|
||||
mCurrentSwapChain = {};
|
||||
mDefaultRenderTarget = {};
|
||||
mBoundPipeline = {};
|
||||
mPipelineState = {};
|
||||
|
||||
mQueryManager.terminate();
|
||||
|
||||
@@ -325,6 +325,10 @@ void VulkanDriver::terminate() {
|
||||
|
||||
mCommands.terminate();
|
||||
|
||||
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
|
||||
// descriptorSetLayoutCache
|
||||
mExternalImageManager.terminate();
|
||||
|
||||
mStagePool.terminate();
|
||||
mPipelineCache.terminate();
|
||||
mFramebufferCache.reset();
|
||||
@@ -381,6 +385,10 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
// Do nothing.
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
mExternalImageManager.onBeginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
||||
@@ -421,12 +429,16 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
|
||||
// TODO: YcbcrConversion?
|
||||
VulkanSamplerCache::Params cacheParams = {
|
||||
.sampler = params,
|
||||
};
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||
if (mExternalImageManager.isExternallySampledTexture(texture)) {
|
||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
||||
mAppState.hasBoundExternalImages = true;
|
||||
} else {
|
||||
VulkanSamplerCache::Params cacheParams = {
|
||||
.sampler = params,
|
||||
};
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::flush(int) {
|
||||
@@ -563,41 +575,35 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
if (metadata.isProtected) {
|
||||
usage |= backend::TextureUsage::PROTECTED;
|
||||
}
|
||||
auto const& metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
||||
|
||||
VkImageUsageFlags vkUsage = metadata.usage;
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
assert_invariant(width == metadata.width);
|
||||
assert_invariant(height == metadata.height);
|
||||
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
uint32_t const memoryTypeIndex =
|
||||
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||
<< "failed to find a valid memory type for external image memory.";
|
||||
// In theory the following are reasonable expectations, but in practice it's hard for client's
|
||||
// to match up the dimensions of the texture with that of the AHB.
|
||||
// assert_invariant(width == metadata.width);
|
||||
// assert_invariant(height == metadata.height);
|
||||
// assert_invariant(format == metadata.filamentFormat);
|
||||
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
VkImage vkimg;
|
||||
VkDeviceMemory deviceMemory;
|
||||
std::tie(vkimg, deviceMemory) =
|
||||
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||
std::tie(vkimg, deviceMemory) = mPlatform->createVkImageFromExternal(externalImage);
|
||||
|
||||
VkSamplerYcbcrConversion conversion =
|
||||
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
||||
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
|
||||
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
|
||||
metadata.layerCount, usage, mStagePool);
|
||||
metadata.format, conversion, metadata.samples, metadata.width, metadata.height,
|
||||
metadata.layers, usage, mStagePool);
|
||||
|
||||
if (conversion != VK_NULL_HANDLE) {
|
||||
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
|
||||
}
|
||||
|
||||
// Unlike uploaded textures or swapchains, we need to explicit transition this
|
||||
// texture into the read layout.
|
||||
auto& commands = mCommands.get();
|
||||
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
|
||||
|
||||
texture.inc();
|
||||
}
|
||||
@@ -630,6 +636,8 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
}
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
texture.dec();
|
||||
|
||||
mExternalImageManager.removeExternallySampledTexture(texture);
|
||||
}
|
||||
|
||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
@@ -798,6 +806,11 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||
auto set = mDescriptorSetCache.createSet(dsh, layout);
|
||||
set.inc();
|
||||
|
||||
if (layout->hasExternalSamplers()) {
|
||||
mAppState.hasExternalSamplerLayouts = true;
|
||||
mExternalImageManager.addDescriptorSet(layout, set);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
||||
@@ -917,11 +930,19 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh) {
|
||||
auto layout = resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||
layout.dec();
|
||||
|
||||
if (layout->hasExternalSamplers()) {
|
||||
mExternalImageManager.removeDescriptorSetLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
set.dec();
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
mExternalImageManager.removeDescriptorSet(set);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
|
||||
@@ -1482,7 +1503,6 @@ void VulkanDriver::endRenderPass(int) {
|
||||
// pipeline barrier between framebuffer writes and shader reads.
|
||||
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
||||
|
||||
mRenderPassFboInfo = {};
|
||||
mCurrentRenderPass.renderTarget = {};
|
||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||
|
||||
@@ -1542,10 +1562,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
||||
|
||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
|
||||
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
|
||||
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||
mBoundPipeline.pipelineLayout, stage, index, value);
|
||||
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||
mPipelineState.pipelineLayout, stage, index, value);
|
||||
}
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string) {
|
||||
@@ -1724,6 +1744,27 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
// We need to determine whether to delay bindning until draw().
|
||||
mPipelineState.bindInDraw.first = false;
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto& layouts = pipelineState.pipelineLayout.setLayout;
|
||||
auto haveExternalSamplers = [&](auto hwHandle) {
|
||||
if (!hwHandle) {
|
||||
return false;
|
||||
}
|
||||
auto layout =
|
||||
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, hwHandle);
|
||||
return layout->hasExternalSamplers();
|
||||
};
|
||||
if (std::any_of(layouts.begin(), layouts.end(), haveExternalSamplers)) {
|
||||
mPipelineState.bindInDraw = { true, pipelineState };
|
||||
return;
|
||||
}
|
||||
}
|
||||
bindPipelineImpl(pipelineState);
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto commands = mCurrentRenderPass.commandBuffer;
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
|
||||
@@ -1792,10 +1833,11 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
|
||||
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
|
||||
|
||||
mBoundPipeline = {
|
||||
mPipelineState = {
|
||||
.program = program,
|
||||
.pipelineLayout = pipelineLayout,
|
||||
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
|
||||
.bindInDraw = {false, {}},
|
||||
};
|
||||
|
||||
mPipelineCache.bindLayout(pipelineLayout);
|
||||
@@ -1842,14 +1884,24 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto const& [bindInDraw, pipelineSt] = mPipelineState.bindInDraw;
|
||||
bool const hasUpdated =
|
||||
mExternalImageManager.prepareBindSets(mDescriptorSetCache.getBoundSets());
|
||||
if (bindInDraw || hasUpdated) {
|
||||
bindPipelineImpl(pipelineSt);
|
||||
}
|
||||
mPipelineState.bindInDraw.first = false;
|
||||
}
|
||||
|
||||
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
|
||||
mBoundPipeline.pipelineLayout,
|
||||
mBoundPipeline.descriptorSetMask);
|
||||
mPipelineState.pipelineLayout,
|
||||
mPipelineState.descriptorSetMask);
|
||||
|
||||
// Finally, make the actual draw call. TODO: support subranges
|
||||
const uint32_t firstIndex = indexOffset;
|
||||
const int32_t vertexOffset = 0;
|
||||
const uint32_t firstInstId = 0;
|
||||
uint32_t const firstIndex = indexOffset;
|
||||
constexpr int32_t vertexOffset = 0;
|
||||
constexpr uint32_t firstInstId = 0;
|
||||
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||
#include "vulkan/VulkanExternalImageManager.h"
|
||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
@@ -119,6 +120,7 @@ private:
|
||||
|
||||
private:
|
||||
void collectGarbage();
|
||||
void bindPipelineImpl(PipelineState const& pipelineState);
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
fvkmemory::ResourceManager mResourceManager;
|
||||
@@ -143,21 +145,29 @@ private:
|
||||
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||
VulkanQueryManager mQueryManager;
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
|
||||
// This is necessary for us to write to push constants after binding a pipeline.
|
||||
struct {
|
||||
// For push constant
|
||||
resource_ptr<VulkanProgram> program;
|
||||
// For push commiting dynamic ubos in draw()
|
||||
VkPipelineLayout pipelineLayout;
|
||||
fvkutils::DescriptorSetMask descriptorSetMask;
|
||||
} mBoundPipeline = {};
|
||||
|
||||
// We need to store information about a render pass to enable better barriers at the end of a
|
||||
// renderpass.
|
||||
std::pair<bool, PipelineState> bindInDraw = {false, {}};
|
||||
} mPipelineState = {};
|
||||
|
||||
struct {
|
||||
using AttachmentArray =
|
||||
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
|
||||
AttachmentArray attachments;
|
||||
} mRenderPassFboInfo = {};
|
||||
// This tracks whether the app has seen external samplers bound to a the descriptor set.
|
||||
// This will force bindPipeline to take a slow path.
|
||||
bool hasExternalSamplerLayouts = false;
|
||||
bool hasBoundExternalImages = false;
|
||||
|
||||
bool hasExternalSamplers() const noexcept {
|
||||
return hasExternalSamplerLayouts && hasBoundExternalImages;
|
||||
}
|
||||
} mAppState;
|
||||
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
|
||||
280
filament/backend/src/vulkan/VulkanExternalImageManager.cpp
Normal file
280
filament/backend/src/vulkan/VulkanExternalImageManager.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanExternalImageManager.h"
|
||||
|
||||
#include "VulkanDescriptorSetCache.h"
|
||||
#include "VulkanDescriptorSetLayoutCache.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
void erase(std::vector<T>& v, std::function<bool(T const&)> f) {
|
||||
auto newEnd = std::remove_if(v.begin(), v.end(), f);
|
||||
v.erase(newEnd, v.end());
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
|
||||
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
|
||||
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
|
||||
: mPlatform(platform),
|
||||
mSamplerCache(samplerCache),
|
||||
mYcbcrConversionCache(ycbcrConversionCache),
|
||||
mDescriptorSetCache(setCache),
|
||||
mDescriptorSetLayoutCache(layoutCache) {
|
||||
}
|
||||
|
||||
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
|
||||
|
||||
void VulkanExternalImageManager::terminate() {
|
||||
mSetAndLayouts.clear();
|
||||
mSetBindings.clear();
|
||||
mImages.clear();
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::onBeginFrame() {
|
||||
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
|
||||
image.hasBeenValidated = false;
|
||||
});
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::prepareBindSets(SetArray const& sets) {
|
||||
bool hasUpdated = false;
|
||||
for (auto set: sets) {
|
||||
if (!set) {
|
||||
continue;
|
||||
}
|
||||
if (auto itr = std::find_if(mSetAndLayouts.begin(), mSetAndLayouts.end(),
|
||||
[&](auto const& setAndLayout) { return setAndLayout.first == set; });
|
||||
itr != mSetAndLayouts.end()) {
|
||||
hasUpdated = updateSetAndLayout(itr->first, itr->second) || hasUpdated;
|
||||
}
|
||||
}
|
||||
return hasUpdated;
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::updateSetAndLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto findImage = [&](fvkmemory::resource_ptr<VulkanTexture> texture) -> ImageData* {
|
||||
auto itr = std::find_if(mImages.begin(), mImages.end(), [&](ImageData const& data) {
|
||||
return data.ptr == texture;
|
||||
});
|
||||
assert_invariant(itr != mImages.end());
|
||||
return &(*itr);
|
||||
};
|
||||
|
||||
//std::vector<std::pair<uint8_t, ImageData*>> externalImages;
|
||||
utils::FixedCapacityVector<std::pair<uint8_t, VkSampler>> samplerAndBindings;
|
||||
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
|
||||
|
||||
bool hasImageUpdates = false;
|
||||
for (auto& bindingInfo : mSetBindings) {
|
||||
if (bindingInfo.set != set) {
|
||||
continue;
|
||||
}
|
||||
auto imageData = findImage(bindingInfo.image);
|
||||
hasImageUpdates = updateImage(imageData) || hasImageUpdates;
|
||||
|
||||
auto samplerParams = bindingInfo.samplerParams;
|
||||
// according to spec, these must match chromaFilter
|
||||
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
|
||||
samplerParams.filterMag = SamplerMagFilter::NEAREST;
|
||||
samplerParams.filterMin = SamplerMinFilter::NEAREST;
|
||||
|
||||
auto sampler = mSamplerCache->getSampler({
|
||||
.sampler = samplerParams,
|
||||
.conversion = imageData->conversion,
|
||||
});
|
||||
samplerAndBindings.push_back({ bindingInfo.binding, sampler });
|
||||
}
|
||||
|
||||
// We need to sort by binding number
|
||||
std::sort(samplerAndBindings.begin(), samplerAndBindings.end());
|
||||
|
||||
utils::FixedCapacityVector<VkSampler> outSamplers;
|
||||
outSamplers.reserve(MAX_SAMPLER_COUNT);
|
||||
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
|
||||
[&](auto const& b) { outSamplers.push_back(b.second); });
|
||||
|
||||
VkDescriptorSetLayout const oldLayout = layout->getVkLayout();
|
||||
VkDescriptorSetLayout const newLayout =
|
||||
mDescriptorSetLayoutCache->getVkLayout(layout->bitmask, outSamplers);
|
||||
bool const hasLayoutUpdate = oldLayout != newLayout;
|
||||
layout->setVkLayout(newLayout);
|
||||
|
||||
assert_invariant(
|
||||
(!hasImageUpdates && !hasLayoutUpdate) ||
|
||||
(hasImageUpdates && hasLayoutUpdate));
|
||||
|
||||
if (!hasLayoutUpdate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto foldBitsInHalf = [](auto bitset) {
|
||||
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(bitset) * 4;
|
||||
decltype(bitset) outBitset;
|
||||
bitset.forEachSetBit([&](size_t index) { outBitset.set(index % BITMASK_LOWER_BITS_LEN); });
|
||||
return outBitset;
|
||||
};
|
||||
// We need to build a new descriptor set from the new layout
|
||||
VkDescriptorSet oldSet = set->getVkSet();
|
||||
VkDescriptorSet newSet = mDescriptorSetCache->getVkSet(layout);
|
||||
|
||||
using Bitmask = fvkutils::UniformBufferBitmask;
|
||||
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
|
||||
|
||||
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
|
||||
auto const samplers = layout->bitmask.sampler & (~layout->bitmask.externalSampler);
|
||||
|
||||
// each bitmask denotes a binding index, and separated into two stages - vertex and buffer
|
||||
// We fold the two stages into just the lower half of the bits to denote a combined set of
|
||||
// bindings.
|
||||
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
|
||||
|
||||
// TODO: fix the size for better memory
|
||||
std::vector<VkCopyDescriptorSet> copies;
|
||||
copyBindings.forEachSetBit([&](size_t index) {
|
||||
copies.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
|
||||
.srcSet = oldSet,
|
||||
.srcBinding = (uint32_t) index,
|
||||
.dstSet = newSet,
|
||||
.dstBinding = (uint32_t) index,
|
||||
.descriptorCount = 1,
|
||||
});
|
||||
});
|
||||
vkUpdateDescriptorSets(mPlatform->getDevice(), 0, nullptr, copies.size(), copies.data());
|
||||
|
||||
set->setVkSet(newSet);
|
||||
|
||||
// We need to release the vkset, which is no longer used, back into the pool.
|
||||
mDescriptorSetCache->manualRecyle(layout->count, oldLayout, oldSet);
|
||||
|
||||
// We need to update the external samplers in the set
|
||||
for (auto& bindingInfo: mSetBindings) {
|
||||
if (bindingInfo.set != set) {
|
||||
continue;
|
||||
}
|
||||
mDescriptorSetCache->updateSampler(set, bindingInfo.binding, bindingInfo.image,
|
||||
VK_NULL_HANDLE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata) {
|
||||
// This external image does not require external sampler (YUV conversion).
|
||||
if (metadata.externalFormat == 0) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
VulkanYcbcrConversionCache::Params ycbcrParams = {
|
||||
.conversion = {
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
|
||||
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
|
||||
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
|
||||
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
|
||||
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
|
||||
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
|
||||
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
|
||||
|
||||
// Unclear where to get the chromaFilter, we just assume it's nearest.
|
||||
.chromaFilter = SamplerMagFilter::NEAREST,
|
||||
},
|
||||
.format = metadata.filamentFormat,
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
return mYcbcrConversionCache->getConversion(ycbcrParams);
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::updateImage(ImageData* image) {
|
||||
if (image->hasBeenValidated) {
|
||||
return false;
|
||||
}
|
||||
image->hasBeenValidated = true;
|
||||
|
||||
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
|
||||
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
|
||||
if (vkYcbcr == image->conversion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
image->ptr->setYcbcrConversion(vkYcbcr, metadata.externalFormat != 0);
|
||||
image->conversion = vkYcbcr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::addDescriptorSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
|
||||
mSetAndLayouts.push_back({set, layout});
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeDescriptorSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
|
||||
erase<SetAndLayout>(mSetAndLayouts,
|
||||
[&](auto const& setLayout) { return (setLayout.first == inSet); });
|
||||
erase<SetBindingInfo>(mSetBindings,
|
||||
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeDescriptorSetLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> inLayout) {
|
||||
erase<SetAndLayout>(mSetAndLayouts,
|
||||
[&](auto const& setLayout) { return (setLayout.second == inLayout); });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::bindExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
|
||||
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
|
||||
// Should we do duplicate validation here?
|
||||
mSetBindings.push_back({ bindingPoint, image, set, samplerParams });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::addExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
Platform::ExternalImageHandleRef platformHandleRef) {
|
||||
mImages.push_back({ image, platformHandleRef, false });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image) {
|
||||
erase<SetBindingInfo>(mSetBindings,
|
||||
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
|
||||
erase<ImageData>(mImages, [&](auto const& imageData) { return imageData.ptr == image; });
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::isExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image) const {
|
||||
return std::find_if(mImages.begin(), mImages.end(),
|
||||
[&](auto const& imageData) { return imageData.ptr == image; }) != mImages.end();
|
||||
}
|
||||
|
||||
|
||||
} // namesapce filament::backend
|
||||
115
filament/backend/src/vulkan/VulkanExternalImageManager.h
Normal file
115
filament/backend/src/vulkan/VulkanExternalImageManager.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanYcbcrConversionCache;
|
||||
class VulkanSamplerCache;
|
||||
class VulkanDescriptorSetLayoutCache;
|
||||
class VulkanDescriptorSetCache;
|
||||
|
||||
// Manages the logic of external images and their quirks wrt Vulikan.
|
||||
class VulkanExternalImageManager {
|
||||
public:
|
||||
|
||||
VulkanExternalImageManager(
|
||||
VulkanPlatform* platform,
|
||||
VulkanSamplerCache* samplerCache,
|
||||
VulkanYcbcrConversionCache* ycbcrConversionCache,
|
||||
VulkanDescriptorSetCache* setCache,
|
||||
VulkanDescriptorSetLayoutCache* layoutCache);
|
||||
|
||||
~VulkanExternalImageManager();
|
||||
|
||||
void terminate();
|
||||
|
||||
void onBeginFrame();
|
||||
|
||||
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
// This sets the currently bound layouts objects for the pipeline
|
||||
bool prepareBindSets(SetArray const& layouts);
|
||||
|
||||
void addDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void removeDescriptorSetLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
SamplerParams samplerParams);
|
||||
|
||||
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
Platform::ExternalImageHandleRef platformHandleRef);
|
||||
|
||||
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
|
||||
|
||||
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
|
||||
|
||||
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata);
|
||||
|
||||
private:
|
||||
|
||||
struct ImageData {
|
||||
fvkmemory::resource_ptr<VulkanTexture> ptr;
|
||||
Platform::ExternalImageHandle platformHandle;
|
||||
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
bool updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
bool updateImage(ImageData* imageData);
|
||||
|
||||
VulkanPlatform* mPlatform;
|
||||
VulkanSamplerCache* mSamplerCache;
|
||||
VulkanYcbcrConversionCache* mYcbcrConversionCache;
|
||||
VulkanDescriptorSetCache* mDescriptorSetCache;
|
||||
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
|
||||
|
||||
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
|
||||
|
||||
struct SetBindingInfo {
|
||||
uint8_t binding = 0;
|
||||
fvkmemory::resource_ptr<VulkanTexture> image;
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
|
||||
SamplerParams samplerParams;
|
||||
};
|
||||
|
||||
// Use vectors instead of hash maps because we only expect small number of entries.
|
||||
std::vector<SetAndLayout> mSetAndLayouts;
|
||||
std::vector<SetBindingInfo> mSetBindings;
|
||||
std::vector<ImageData> mImages;
|
||||
};
|
||||
|
||||
} // filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <utils/compiler.h> // UTILS_FALLTHROUGH
|
||||
#include <utils/Panic.h> // ASSERT_POSTCONDITION
|
||||
|
||||
using namespace bluevk;
|
||||
@@ -89,8 +90,9 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
// TODO: properly handle external sampler
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
|
||||
UTILS_FALLTHROUGH;
|
||||
case DescriptorType::SAMPLER: {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
||||
break;
|
||||
|
||||
@@ -66,18 +66,21 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
||||
|
||||
// The bitmask representation of a set layout.
|
||||
struct Bitmask {
|
||||
// TODO: better utiltize the space below and use bitset instead.
|
||||
fvkutils::UniformBufferBitmask ubo; // 8 bytes
|
||||
fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes
|
||||
fvkutils::SamplerBitmask sampler; // 8 bytes
|
||||
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
|
||||
|
||||
// This is a subset of the bitmask.sampler field.
|
||||
fvkutils::SamplerBitmask externalSampler; // 8 bytes
|
||||
|
||||
bool operator==(Bitmask const& right) const {
|
||||
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
|
||||
inputAttachment == right.inputAttachment;
|
||||
inputAttachment == right.inputAttachment &&
|
||||
externalSampler == right.externalSampler;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Bitmask) == 32);
|
||||
static_assert(sizeof(Bitmask) == 40);
|
||||
|
||||
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set
|
||||
// pools.
|
||||
@@ -119,10 +122,16 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
||||
|
||||
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
|
||||
|
||||
// Note that we don't destroy the vklayout. This is done by the layout cache.
|
||||
~VulkanDescriptorSetLayout() = default;
|
||||
|
||||
VkDescriptorSetLayout getVkLayout() const { return mVkLayout; }
|
||||
void setVkLayout(VkDescriptorSetLayout vklayout) { mVkLayout = vklayout; }
|
||||
VkDescriptorSetLayout const& getVkLayout() const noexcept { return mVkLayout; }
|
||||
|
||||
// It is possible to have the layout switch out due to AHardwarebuffer (external image) format
|
||||
// changes.
|
||||
void setVkLayout(VkDescriptorSetLayout vklayout) noexcept { mVkLayout = vklayout; }
|
||||
|
||||
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
|
||||
|
||||
Bitmask const bitmask;
|
||||
Count const count;
|
||||
@@ -137,12 +146,11 @@ public:
|
||||
// can use to repackage the vk handle.
|
||||
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
||||
|
||||
VulkanDescriptorSet(VkDescriptorSet rawSet,
|
||||
VulkanDescriptorSet(
|
||||
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
||||
uint8_t uniqueDynamicUboCount,
|
||||
OnRecycle&& onRecycleFn)
|
||||
: vkSet(rawSet),
|
||||
dynamicUboMask(dynamicUboMask),
|
||||
: dynamicUboMask(dynamicUboMask),
|
||||
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||
|
||||
@@ -152,6 +160,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSet const& getVkSet() const noexcept {
|
||||
return mVkSet;
|
||||
}
|
||||
|
||||
// Note that the only case where you'd set it more than once is with external images/samplers.
|
||||
void setVkSet(VkDescriptorSet vkset) noexcept { mVkSet = vkset; }
|
||||
|
||||
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
|
||||
mOffsets = std::move(offsets);
|
||||
}
|
||||
@@ -163,11 +178,11 @@ public:
|
||||
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
||||
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
||||
|
||||
VkDescriptorSet const vkSet;
|
||||
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
||||
uint8_t const uniqueDynamicUboCount;
|
||||
|
||||
private:
|
||||
VkDescriptorSet mVkSet = VK_NULL_HANDLE;
|
||||
backend::DescriptorSetOffsetArray mOffsets;
|
||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||
OnRecycle mOnRecycleFn;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -33,8 +34,15 @@ VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
|
||||
if (UTILS_LIKELY(iter != mCache.end())) {
|
||||
return iter->second;
|
||||
}
|
||||
VkSamplerYcbcrConversionInfo ycbcrConversion = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.conversion = params.conversion,
|
||||
};
|
||||
|
||||
auto const& samplerParams = params.sampler;
|
||||
VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
VkSamplerCreateInfo samplerInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = params.conversion != VK_NULL_HANDLE ? &ycbcrConversion : VK_NULL_HANDLE,
|
||||
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
|
||||
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
|
||||
@@ -48,7 +56,8 @@ VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
|
||||
.minLod = 0.0f,
|
||||
.maxLod = fvkutils::getMaxLod(samplerParams.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."
|
||||
|
||||
@@ -108,11 +108,6 @@ void VulkanSwapChain::present() {
|
||||
|
||||
mCommands->flush();
|
||||
|
||||
// call the image ready wait function
|
||||
if (mExplicitImageReadyWait != nullptr) {
|
||||
mExplicitImageReadyWait(swapChain);
|
||||
}
|
||||
|
||||
// We only present if it is not headless. No-op for headless.
|
||||
if (!mHeadless) {
|
||||
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
|
||||
@@ -146,7 +141,6 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
VulkanPlatform::ImageSyncData imageSyncData;
|
||||
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
||||
mCurrentSwapIndex = imageSyncData.imageIndex;
|
||||
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
||||
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
||||
|
||||
@@ -49,7 +49,9 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
|
||||
|
||||
void present();
|
||||
|
||||
void acquire(bool& reized);
|
||||
// Acquire a new image from the swapchain. If the image is not available it would wait until it
|
||||
// is.
|
||||
void acquire(bool& resized);
|
||||
|
||||
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
|
||||
uint32_t const imageIndex = mCurrentSwapIndex;
|
||||
@@ -99,7 +101,6 @@ private:
|
||||
VkExtent2D mExtent;
|
||||
uint32_t mLayerCount;
|
||||
uint32_t mCurrentSwapIndex;
|
||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
||||
bool mAcquired;
|
||||
bool mIsFirstRenderPass;
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
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,
|
||||
.format = fvkutils::getVkFormat(params.format),
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
@@ -52,6 +52,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
|
||||
// We could put this in the platform class, but that seems like a bit of an overkill
|
||||
#if defined(__ANDROID__)
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
@@ -59,6 +60,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
};
|
||||
if (params.externalFormat) {
|
||||
conversionInfo.pNext = &externalFormat;
|
||||
conversionInfo.format = VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ class VulkanYcbcrConversionCache {
|
||||
public:
|
||||
struct Params {
|
||||
SamplerYcbcrConversion conversion = {};
|
||||
uint32_t padding = 0;
|
||||
TextureFormat format = {};
|
||||
uint16_t padding = 0;
|
||||
uint64_t externalFormat = 0;
|
||||
};
|
||||
static_assert(sizeof(Params) == 16);
|
||||
|
||||
@@ -132,6 +132,10 @@ public:
|
||||
return id() == other.id() && type() == other.type();
|
||||
}
|
||||
|
||||
inline bool operator!=(resource_ptr<D> const& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
inline explicit operator bool() const {
|
||||
return bool(mRef);
|
||||
}
|
||||
|
||||
@@ -211,6 +211,7 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
|
||||
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
|
||||
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
|
||||
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
|
||||
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||
#endif
|
||||
// MoltenVk is the only non-conformant implementation we're interested in.
|
||||
#if defined(__APPLE__)
|
||||
@@ -326,7 +327,9 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
||||
}
|
||||
|
||||
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
|
||||
VkPhysicalDeviceFeatures2 const& features,
|
||||
VkPhysicalDeviceVulkan11Features const& vk11Features,
|
||||
uint32_t graphicsQueueFamilyIndex,
|
||||
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
|
||||
bool requestImageView2DOn3DImage) {
|
||||
VkDevice device;
|
||||
@@ -359,14 +362,28 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
|
||||
// We could simply enable all supported features, but since that may have performance
|
||||
// consequences let's just enable the features we need.
|
||||
VkPhysicalDeviceFeatures enabledFeatures{
|
||||
VkPhysicalDeviceFeatures enabledFeatures = {
|
||||
.depthClamp = features.features.depthClamp,
|
||||
.samplerAnisotropy = features.features.samplerAnisotropy,
|
||||
.textureCompressionETC2 = features.features.textureCompressionETC2,
|
||||
.textureCompressionBC = features.features.textureCompressionBC,
|
||||
.shaderClipDistance = features.features.shaderClipDistance,
|
||||
};
|
||||
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
||||
|
||||
VkPhysicalDeviceFeatures2 enabledFeatures2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.features = enabledFeatures,
|
||||
};
|
||||
chainStruct(&deviceCreateInfo, &enabledFeatures2);
|
||||
|
||||
VkPhysicalDeviceVulkan11Features enabledVk11Features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
.multiview = vk11Features.multiview,
|
||||
#if defined(__ANDROID__)
|
||||
.samplerYcbcrConversion = vk11Features.samplerYcbcrConversion,
|
||||
#endif
|
||||
};
|
||||
chainStruct(&deviceCreateInfo, &enabledVk11Features);
|
||||
|
||||
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
|
||||
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
|
||||
@@ -383,7 +400,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
|
||||
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
|
||||
.multiview = VK_TRUE,
|
||||
.multiview = vk11Features.multiview,
|
||||
.multiviewGeometryShader = VK_FALSE,
|
||||
.multiviewTessellationShader = VK_FALSE,
|
||||
};
|
||||
@@ -734,6 +751,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
|
||||
};
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPhysicalDeviceVk11Features);
|
||||
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
|
||||
|
||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||
@@ -795,10 +813,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
}
|
||||
|
||||
if (mImpl->mDevice == VK_NULL_HANDLE) {
|
||||
mImpl->mDevice =
|
||||
createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
|
||||
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
||||
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
||||
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice,
|
||||
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features,
|
||||
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
||||
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
||||
}
|
||||
|
||||
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
||||
@@ -826,12 +844,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
if (!mImpl->mSharedContext) {
|
||||
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
|
||||
} else {
|
||||
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
||||
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
|
||||
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
|
||||
context.mMultiviewEnabled = scontext->multiviewSupported;
|
||||
}
|
||||
|
||||
// Check the availability of lazily allocated memory
|
||||
@@ -977,30 +993,6 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
|
||||
return mImpl->mProtectedGraphicsQueue;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
|
||||
ExternalImageHandleRef externalImage) {
|
||||
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
|
||||
usage);
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat) {
|
||||
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
|
||||
viewType, swizzle);
|
||||
}
|
||||
|
||||
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
|
||||
return getSwapchainInstanceExtensionsImpl();
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
#include <backend/platforms/VulkanPlatformAndroid.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
#include "vulkan/VulkanContext.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <private/backend/BackendUtilsAndroid.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include "vulkan/utils/Image.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
@@ -57,7 +57,7 @@ VkFormat transformVkFormat(VkFormat format, bool sRGB) {
|
||||
}
|
||||
|
||||
bool isProtectedFromUsage(uint64_t usage) {
|
||||
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
|
||||
return usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
|
||||
}
|
||||
|
||||
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
||||
@@ -120,7 +120,9 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
||||
usage = 0;
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
|
||||
// We shouldn't be using external samplers as input attachments
|
||||
// usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
|
||||
if (isDepthFormat) {
|
||||
@@ -136,87 +138,38 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
||||
return { format, usage };
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage) {
|
||||
VulkanPlatform::ImageData data;
|
||||
|
||||
// if external format we need to specifiy it in the allocation
|
||||
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
||||
|
||||
const VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
// pass down the format (external means we don't have it VK defined)
|
||||
.externalFormat = metadata.externalFormat,
|
||||
std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
||||
bool sRGB) {
|
||||
auto const format = mapToFilamentFormat(desc.format, sRGB);
|
||||
return {
|
||||
format,
|
||||
mapToFilamentUsage(desc.usage, format),
|
||||
};
|
||||
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
.pNext = useExternalFormat ? &externalFormat : nullptr,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
||||
};
|
||||
|
||||
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
imageInfo.pNext = &externalCreateInfo;
|
||||
imageInfo.format = metadata.format;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent = {
|
||||
metadata.width,
|
||||
metadata.height,
|
||||
1u,
|
||||
};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = metadata.layers;
|
||||
imageInfo.samples = metadata.samples;
|
||||
imageInfo.usage = usage;
|
||||
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Allocate the memory
|
||||
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.buffer = buffer,
|
||||
};
|
||||
VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = &androidHardwareBufferInfo,
|
||||
.image = data.first,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &memoryDedicatedAllocateInfo,
|
||||
.allocationSize = metadata.allocationSize,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
}// namespace
|
||||
|
||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
|
||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
if (aHardwareBuffer) {
|
||||
AHardwareBuffer_release(aHardwareBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
auto bufferImpl = const_cast<AHardwareBuffer*>(buffer);
|
||||
AHardwareBuffer_acquire(bufferImpl);
|
||||
|
||||
AHardwareBuffer_Desc hardwareBufferDescription = {};
|
||||
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
|
||||
|
||||
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
|
||||
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
||||
p->sRGB = sRGB;
|
||||
p->height = hardwareBufferDescription.height;
|
||||
p->width = hardwareBufferDescription.width;
|
||||
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
|
||||
p->format = textureFormat;
|
||||
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
|
||||
return Platform::ExternalImageHandle{ p };
|
||||
}
|
||||
|
||||
@@ -225,23 +178,20 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||
|
||||
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
|
||||
ExternalImageHandleRef externalImage) const noexcept {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
|
||||
auto metadata = extractExternalImageMetadata(externalImage);
|
||||
return {
|
||||
.width = fvkExternalImage->width,
|
||||
.height = fvkExternalImage->height,
|
||||
.format = fvkExternalImage->format,
|
||||
.usage = fvkExternalImage->usage,
|
||||
.width = metadata.width,
|
||||
.height = metadata.height,
|
||||
.format = metadata.filamentFormat,
|
||||
.usage = metadata.filamentUsage,
|
||||
};
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
|
||||
ExternalImageHandleRef externalImage) {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImageMetadata(
|
||||
ExternalImageHandleRef image) const {
|
||||
auto const* fvkExternalImage = static_cast<ExternalImageVulkanAndroid const*>(image.get());
|
||||
|
||||
ExternalImageMetadata metadata;
|
||||
ExternalImageMetadata metadata = {};
|
||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc bufferDesc;
|
||||
@@ -249,16 +199,30 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
|
||||
metadata.width = bufferDesc.width;
|
||||
metadata.height = bufferDesc.height;
|
||||
metadata.layers = bufferDesc.layers;
|
||||
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
|
||||
std::tie(metadata.format, metadata.usage) =
|
||||
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||
}
|
||||
std::tie(metadata.filamentFormat, metadata.filamentUsage) =
|
||||
getFilamentFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||
|
||||
if (isProtectedFromUsage(bufferDesc.usage)) {
|
||||
metadata.filamentUsage |= TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
// TODO: The following seems unnecessary. we should be able to discern directly from the
|
||||
// bufferDesc.
|
||||
if (any(metadata.filamentUsage & TextureUsage::BLIT_SRC)) {
|
||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(metadata.filamentUsage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
}
|
||||
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
|
||||
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
VkAndroidHardwareBufferPropertiesANDROID properties = {
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
|
||||
@@ -268,138 +232,116 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkGetAndroidHardwareBufferProperties failed with error="
|
||||
<< static_cast<int32_t>(result);
|
||||
|
||||
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
|
||||
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
|
||||
<< "mismatched image format( " << metadata.format << ") and queried format("
|
||||
<< bufferPropertiesFormat << ") for external image (AHB)";
|
||||
metadata.externalFormat = formatInfo.externalFormat;
|
||||
|
||||
// Choose either externalFormat > 0 or metadata.format and prefer the latter.
|
||||
if (metadata.externalFormat > 0 && metadata.format != VK_FORMAT_UNDEFINED) {
|
||||
// See VUID-VkImageCreateInfo-pNext-09457
|
||||
metadata.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
metadata.externalFormat = 0;
|
||||
}
|
||||
|
||||
metadata.allocationSize = properties.allocationSize;
|
||||
metadata.memoryTypeBits = properties.memoryTypeBits;
|
||||
|
||||
metadata.ycbcrConversionComponents = formatInfo.samplerYcbcrConversionComponents;
|
||||
metadata.ycbcrModel = formatInfo.suggestedYcbcrModel;
|
||||
metadata.ycbcrRange = formatInfo.suggestedYcbcrRange;
|
||||
metadata.xChromaOffset = formatInfo.suggestedXChromaOffset;
|
||||
metadata.yChromaOffset = formatInfo.suggestedYChromaOffset;
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
|
||||
ExternalImageHandleRef externalImage) const {
|
||||
auto const& metadata = extractExternalImageMetadata(externalImage);
|
||||
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
|
||||
memoryTypeIndex, usage);
|
||||
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
return data;
|
||||
}
|
||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
// if external format we need to specifiy it in the allocation
|
||||
bool const useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
||||
|
||||
VkExternalFormatANDROID const externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.flags = 0,
|
||||
.image = image,
|
||||
.viewType = viewType,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create VkImageView."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(
|
||||
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
|
||||
uint32_t internalFormat) {
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
VkExternalMemoryImageCreateInfo const externalCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
.pNext = useExternalFormat ? &externalFormat : nullptr,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
||||
};
|
||||
|
||||
VkSamplerCreateInfo samplerInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
VkImageCreateInfo const imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &externalCreateInfo,
|
||||
.flags = useExternalFormat ? VK_IMAGE_CREATE_ALIAS_BIT : 0u,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = metadata.format,
|
||||
.extent = {
|
||||
metadata.width,
|
||||
metadata.height,
|
||||
1u,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = metadata.layers,
|
||||
.samples = metadata.samples,
|
||||
.usage = metadata.usage,
|
||||
};
|
||||
VkSampler sampler;
|
||||
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
|
||||
|
||||
VkDevice const device = getDevice();
|
||||
|
||||
VkImage image;
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
return sampler;
|
||||
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Allocate the memory
|
||||
VkImportAndroidHardwareBufferInfoANDROID const androidHardwareBufferInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||
.buffer = buffer,
|
||||
};
|
||||
VkMemoryDedicatedAllocateInfo const memoryDedicatedAllocateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = &androidHardwareBufferInfo,
|
||||
.image = image,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties(getPhysicalDevice(), &memoryProperties);
|
||||
|
||||
|
||||
VkMemoryPropertyFlags const requiredMemoryFlags =
|
||||
any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
|
||||
metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
VkMemoryAllocateInfo const allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &memoryDedicatedAllocateInfo,
|
||||
.allocationSize = metadata.allocationSize,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
VkDeviceMemory memory;
|
||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &memory);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
result = vkBindImageMemory(getDevice(), image, memory, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
return { image, memory };
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
|
||||
@@ -427,20 +369,9 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
|
||||
// Deprecated platform dependent helper methods
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return ExternalImageMetadata{};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return ImageData{};
|
||||
}
|
||||
|
||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
||||
VkInstance instance, uint64_t flags) noexcept {
|
||||
return SurfaceBundle{};
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -63,30 +63,6 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl(
|
||||
return ret;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
||||
VkInstance instance, uint64_t flags) noexcept {
|
||||
VkSurfaceKHR surface;
|
||||
|
||||
@@ -84,30 +84,6 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
||||
VulkanPlatform::ExtensionSet const ret = {
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
|
||||
@@ -668,4 +668,111 @@ VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
|
||||
SamplerYcbcrModelConversion model) {
|
||||
switch (model) {
|
||||
case SamplerYcbcrModelConversion::RGB_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_709:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
|
||||
case SamplerYcbcrModelConversion::YCBCR_601:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
|
||||
case SamplerYcbcrModelConversion::YCBCR_2020:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
|
||||
switch (range) {
|
||||
case SamplerYcbcrRange::ITU_FULL:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
||||
case SamplerYcbcrRange::ITU_NARROW:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc) {
|
||||
switch (loc) {
|
||||
case ChromaLocation::COSITED_EVEN:
|
||||
return VK_CHROMA_LOCATION_COSITED_EVEN;
|
||||
case ChromaLocation::MIDPOINT:
|
||||
return VK_CHROMA_LOCATION_MIDPOINT;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model) {
|
||||
switch (model) {
|
||||
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY:
|
||||
return SamplerYcbcrModelConversion::RGB_IDENTITY;
|
||||
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY:
|
||||
return SamplerYcbcrModelConversion::YCBCR_IDENTITY;
|
||||
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709:
|
||||
return SamplerYcbcrModelConversion::YCBCR_709;
|
||||
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601:
|
||||
return SamplerYcbcrModelConversion::YCBCR_601;
|
||||
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020:
|
||||
return SamplerYcbcrModelConversion::YCBCR_2020;
|
||||
default:
|
||||
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range) {
|
||||
switch (range) {
|
||||
case VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
|
||||
return SamplerYcbcrRange::ITU_FULL;
|
||||
case VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
|
||||
return SamplerYcbcrRange::ITU_NARROW;
|
||||
default:
|
||||
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
ChromaLocation getChromaLocationFilament(VkChromaLocation loc) {
|
||||
switch (loc) {
|
||||
case VK_CHROMA_LOCATION_COSITED_EVEN:
|
||||
return ChromaLocation::COSITED_EVEN;
|
||||
case VK_CHROMA_LOCATION_MIDPOINT:
|
||||
return ChromaLocation::MIDPOINT;
|
||||
default:
|
||||
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex) {
|
||||
switch (c) {
|
||||
case VK_COMPONENT_SWIZZLE_ZERO:
|
||||
return TextureSwizzle::SUBSTITUTE_ZERO;
|
||||
case VK_COMPONENT_SWIZZLE_ONE:
|
||||
return TextureSwizzle::SUBSTITUTE_ONE;
|
||||
case VK_COMPONENT_SWIZZLE_IDENTITY:
|
||||
return (TextureSwizzle) (((uint8_t) TextureSwizzle::CHANNEL_0) + rgbaIndex);
|
||||
case VK_COMPONENT_SWIZZLE_R:
|
||||
return TextureSwizzle::CHANNEL_0;
|
||||
case VK_COMPONENT_SWIZZLE_G:
|
||||
return TextureSwizzle::CHANNEL_1;
|
||||
case VK_COMPONENT_SWIZZLE_B:
|
||||
return TextureSwizzle::CHANNEL_2;
|
||||
case VK_COMPONENT_SWIZZLE_A:
|
||||
return TextureSwizzle::CHANNEL_3;
|
||||
default:
|
||||
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend::fvkutils
|
||||
|
||||
@@ -69,6 +69,12 @@ VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversio
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc);
|
||||
|
||||
// Ycbcr related functions
|
||||
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model);
|
||||
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range);
|
||||
ChromaLocation getChromaLocationFilament(VkChromaLocation loc);
|
||||
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex);
|
||||
|
||||
inline VkImageViewType getViewType(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
|
||||
@@ -348,6 +348,10 @@ using SamplerBitmask = utils::bitset64;
|
||||
// general.
|
||||
using InputAttachmentBitmask = utils::bitset64;
|
||||
|
||||
constexpr uint8_t MAX_DESCRIPTOR_SET_BITMASK_BITS =
|
||||
std::max(std::max(sizeof(UniformBufferBitmask), sizeof(SamplerBitmask)),
|
||||
sizeof(InputAttachmentBitmask)) * 8;
|
||||
|
||||
template<typename Bitmask>
|
||||
static constexpr uint8_t getVertexStageShift() noexcept {
|
||||
// We assume the bottom half of bits are for vertex stages.
|
||||
|
||||
@@ -193,49 +193,6 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
||||
return mostSignificantBit((sampleCount - 1) & mask);
|
||||
}
|
||||
|
||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
|
||||
SamplerYcbcrModelConversion model) {
|
||||
switch (model) {
|
||||
case SamplerYcbcrModelConversion::RGB_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_709:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
|
||||
case SamplerYcbcrModelConversion::YCBCR_601:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
|
||||
case SamplerYcbcrModelConversion::YCBCR_2020:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
|
||||
switch (range) {
|
||||
case SamplerYcbcrRange::ITU_FULL:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
||||
case SamplerYcbcrRange::ITU_NARROW:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc) {
|
||||
switch (loc) {
|
||||
case ChromaLocation::COSITED_EVEN:
|
||||
return VK_CHROMA_LOCATION_COSITED_EVEN;
|
||||
case ChromaLocation::MIDPOINT:
|
||||
return VK_CHROMA_LOCATION_MIDPOINT;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend::fvkutils
|
||||
|
||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
@@ -257,14 +256,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 +276,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) {
|
||||
@@ -353,7 +342,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 {
|
||||
@@ -377,19 +366,19 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
}
|
||||
|
||||
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 +390,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 {
|
||||
@@ -433,7 +422,7 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
||||
assert_invariant(!mSwapChain);
|
||||
mSwapChain = nullptr;
|
||||
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||
mAdapter = mPlatform.requestAdapter(surface);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
@@ -445,28 +434,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
#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."
|
||||
@@ -504,13 +471,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 +498,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 +659,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 +691,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 +703,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 +759,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,
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
@@ -65,11 +63,7 @@ private:
|
||||
// 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
|
||||
*/
|
||||
@@ -99,15 +93,6 @@ private:
|
||||
return mHandleAllocator.allocate<D>();
|
||||
}
|
||||
|
||||
template<typename D, typename B, typename ... ARGS>
|
||||
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
|
||||
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
||||
}
|
||||
template<typename D, typename B>
|
||||
D* handleCast(Handle<B> handle) noexcept {
|
||||
return mHandleAllocator.handle_cast<D*>(handle);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -246,32 +246,4 @@ void WebGPUSwapChain::GetCurrentTexture(uint32_t width, uint32_t height, wgpu::S
|
||||
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
|
||||
|
||||
@@ -32,8 +32,6 @@ public:
|
||||
~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 = {};
|
||||
|
||||
@@ -23,30 +23,44 @@ 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 };
|
||||
};
|
||||
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup(cleanup) {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniforms.size());
|
||||
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||
kLayouts[i] = {
|
||||
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER),
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
|
||||
}
|
||||
|
||||
// This assumes that the uniforms will all be in a single descriptor set at index 1.
|
||||
// If there are shaders with uniforms in other sets then ShaderConfig will need to be expanded
|
||||
// to accommodate that.
|
||||
size_t kDescriptorSetIndex = 1;
|
||||
filamat::DescriptorSets descriptors;
|
||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
||||
descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[i], {}}};
|
||||
descriptors[kDescriptorSetIndex] = filamat::DescriptorSetInfo(config.uniforms.size());
|
||||
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||
descriptors[kDescriptorSetIndex][i] = {
|
||||
config.uniforms[i].name, kLayouts[i], config.uniforms[i].samplerInfo };
|
||||
}
|
||||
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 }});
|
||||
|
||||
Program::DescriptorBindingsInfo bindingsInfo(config.uniforms.size());
|
||||
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||
bindingsInfo[i] = {
|
||||
config.uniforms[i].name,
|
||||
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER), i };
|
||||
}
|
||||
prog.descriptorBindings(1, bindingsInfo);
|
||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||
|
||||
mDescriptorSetLayout = cleanup.add(
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||
}
|
||||
|
||||
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
||||
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
||||
}
|
||||
|
||||
filament::backend::ProgramHandle Shader::getProgram() const {
|
||||
@@ -57,8 +71,4 @@ filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() co
|
||||
return mDescriptorSetLayout;
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
|
||||
return mDescriptorSet;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -18,14 +18,22 @@
|
||||
#define TNT_SHADER_H
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
struct UniformConfig {
|
||||
utils::CString name;
|
||||
// If not specified this will be DescriptorType::UNIFORM_BUFFER
|
||||
std::optional<filament::backend::DescriptorType> type;
|
||||
std::optional<filament::SamplerInterfaceBlock::SamplerInfo> samplerInfo;
|
||||
};
|
||||
|
||||
// All describing a shader that should be created.
|
||||
struct ShaderConfig {
|
||||
std::string vertexShader;
|
||||
std::string fragmentShader;
|
||||
std::vector<utils::CString> uniformNames;
|
||||
std::vector<UniformConfig> uniforms;
|
||||
};
|
||||
|
||||
// All values describing a uniform.
|
||||
@@ -35,6 +43,7 @@ struct ResolvedUniformBindingConfig {
|
||||
uint32_t byteOffset;
|
||||
filament::backend::descriptor_set_t set;
|
||||
filament::backend::descriptor_binding_t binding;
|
||||
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
|
||||
};
|
||||
|
||||
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
|
||||
@@ -46,6 +55,7 @@ struct UniformBindingConfig {
|
||||
std::optional<uint32_t> byteOffset;
|
||||
std::optional<filament::backend::descriptor_set_t> set;
|
||||
std::optional<filament::backend::descriptor_binding_t> binding;
|
||||
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
|
||||
|
||||
template<typename UniformType>
|
||||
ResolvedUniformBindingConfig resolve();
|
||||
@@ -79,12 +89,14 @@ public:
|
||||
|
||||
filament::backend::ProgramHandle getProgram() const;
|
||||
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
||||
filament::backend::DescriptorSetHandle getDescriptorSet() const;
|
||||
|
||||
filament::backend::DescriptorSetHandle createDescriptorSet(
|
||||
filament::backend::DriverApi& api) const;
|
||||
|
||||
protected:
|
||||
Cleanup& mCleanup;
|
||||
filament::backend::ProgramHandle mProgram;
|
||||
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
||||
filament::backend::DescriptorSetHandle mDescriptorSet;
|
||||
};
|
||||
|
||||
template<typename UniformType>
|
||||
@@ -95,7 +107,8 @@ ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
|
||||
.bufferSize = bufferSize.value_or(resolvedDataSize),
|
||||
.byteOffset = byteOffset.value_or(0),
|
||||
.set = set.value_or(1),
|
||||
.binding = binding.value_or(0)
|
||||
.binding = binding.value_or(0),
|
||||
.descriptorSet = descriptorSet
|
||||
};
|
||||
}
|
||||
|
||||
@@ -120,9 +133,16 @@ void Shader::bindUniform(filament::backend::DriverApi& api,
|
||||
UniformBindingConfig config) const {
|
||||
auto resolvedConfig = config.resolve<UniformType>();
|
||||
|
||||
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
|
||||
filament::backend::DescriptorSetHandle descriptorSet;
|
||||
if (resolvedConfig.descriptorSet.has_value()) {
|
||||
descriptorSet = *resolvedConfig.descriptorSet;
|
||||
} else {
|
||||
descriptorSet = createDescriptorSet(api);
|
||||
}
|
||||
|
||||
api.updateDescriptorSetBuffer(descriptorSet, resolvedConfig.binding, hwBuffer, 0,
|
||||
resolvedConfig.bufferSize);
|
||||
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
|
||||
api.bindDescriptorSet(descriptorSet, resolvedConfig.set, {});
|
||||
}
|
||||
|
||||
template<typename UniformType>
|
||||
|
||||
253
filament/backend/test/SharedShaders.cpp
Normal file
253
filament/backend/test/SharedShaders.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SharedShaders.h"
|
||||
|
||||
#include "Shader.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament::backend;
|
||||
|
||||
namespace {
|
||||
|
||||
// A shader stored in pieces so that uniform declarations can be injected.
|
||||
struct ShaderText {
|
||||
std::string mPrefix;
|
||||
std::string mBody;
|
||||
|
||||
std::string withUniform(const std::string& uniformText) const {
|
||||
return absl::StrFormat("%s\n%s\n%s", mPrefix.c_str(), uniformText.c_str(), mBody.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<ShaderText> GetGlslVertexShader(VertexShaderType type) {
|
||||
switch (type) {
|
||||
case VertexShaderType::Noop: {
|
||||
return ShaderText{
|
||||
R"(
|
||||
#version 450 core
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
)", R"(
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
})" };
|
||||
}
|
||||
case VertexShaderType::Simple: {
|
||||
return ShaderText{
|
||||
R"(
|
||||
#version 450 core
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
)", R"(
|
||||
void main() {
|
||||
gl_Position = vec4(
|
||||
mesh_position.xy * (params.scaleMinusOne.xy + 1.0) + params.offset.xy,
|
||||
params.scaleMinusOne.z + 1.0,
|
||||
1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
})" };
|
||||
}
|
||||
case VertexShaderType::Textured: {
|
||||
return ShaderText{
|
||||
R"(
|
||||
#version 450 core
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
layout(location = 0) out vec2 uv;
|
||||
)", R"(
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
uv = (mesh_position.xy * 0.5 + 0.5);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
})" };
|
||||
}
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ShaderText> GetGlslFragmentShader(FragmentShaderType type) {
|
||||
switch (type) {
|
||||
case FragmentShaderType::White: {
|
||||
return ShaderText{
|
||||
R"(
|
||||
#version 450 core
|
||||
precision mediump int; precision highp float;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
)", R"(
|
||||
void main() {
|
||||
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
})" };
|
||||
}
|
||||
case FragmentShaderType::SolidColored: {
|
||||
return ShaderText{
|
||||
R"(
|
||||
#version 450 core
|
||||
precision mediump int; precision highp float;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
)", R"(
|
||||
void main() {
|
||||
fragColor = params.color;
|
||||
})" };
|
||||
}
|
||||
case FragmentShaderType::Textured: {
|
||||
return ShaderText{
|
||||
R"(
|
||||
#version 450 core
|
||||
precision mediump int; precision highp float;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(location = 0) in vec2 uv;
|
||||
)", R"(
|
||||
void main() {
|
||||
fragColor = texture(test_tex, uv);
|
||||
})" };
|
||||
}
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> GetGlslUniform(ShaderUniformType type) {
|
||||
switch (type) {
|
||||
case ShaderUniformType::None: {
|
||||
return "";
|
||||
}
|
||||
case ShaderUniformType::Simple: {
|
||||
return R"(
|
||||
layout(binding = 0, set = 1) uniform Params {
|
||||
highp vec4 color;
|
||||
// Use scaleMinusOne instead of scale so that a 0 initialized value is a good default
|
||||
highp vec4 scaleMinusOne;
|
||||
highp vec4 offset;
|
||||
} params;
|
||||
)";
|
||||
}
|
||||
case ShaderUniformType::SimpleWithPadding: {
|
||||
return R"(
|
||||
layout(binding = 0, set = 1) uniform Params {
|
||||
highp vec4 padding[4]; // offset of 64 bytes
|
||||
|
||||
highp vec4 color;
|
||||
// Use scaleMinusOne instead of scale so that a 0 initialized value is a good default
|
||||
highp vec4 scaleMinusOne;
|
||||
highp vec4 offset;
|
||||
} params;
|
||||
)";
|
||||
}
|
||||
case ShaderUniformType::Sampler: {
|
||||
return R"(
|
||||
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
||||
)";
|
||||
}
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<UniformConfig> GetUniformConfig(ShaderUniformType type) {
|
||||
switch (type) {
|
||||
case ShaderUniformType::None: {
|
||||
return {};
|
||||
}
|
||||
case ShaderUniformType::Simple: {
|
||||
return {{ "Params" }};
|
||||
}
|
||||
case ShaderUniformType::SimpleWithPadding: {
|
||||
return {{ "Params" }};
|
||||
}
|
||||
case ShaderUniformType::Sampler: {
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo{
|
||||
"backend_test", "test_tex", 0,
|
||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
return {{
|
||||
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
}};
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
ShaderLanguage getShaderLanguage(const Backend& backend) {
|
||||
switch (backend) {
|
||||
case Backend::METAL:
|
||||
return ShaderLanguage::MSL;
|
||||
case Backend::WEBGPU:
|
||||
return ShaderLanguage::WGSL;
|
||||
case Backend::VULKAN:
|
||||
case Backend::NOOP:
|
||||
case Backend::OPENGL:
|
||||
default: {
|
||||
return ShaderLanguage::GLSL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Shader SharedShaders::makeShader(filament::backend::DriverApi& api, Cleanup& cleanup,
|
||||
ShaderRequest request) {
|
||||
std::optional<ShaderText> vertex;
|
||||
std::optional<ShaderText> fragment;
|
||||
std::optional<std::string> uniform;
|
||||
if (getShaderLanguage(BackendTest::sBackend) != ShaderLanguage::GLSL) {
|
||||
// TODO: If any shaders need backend/shader language specific shaders rather than transpiled
|
||||
// versions of the GLSL shader, check environment.
|
||||
}
|
||||
vertex = GetGlslVertexShader(request.mVertexType);
|
||||
fragment = GetGlslFragmentShader(request.mFragmentType);
|
||||
uniform = GetGlslUniform(request.mUniformType);
|
||||
if (vertex.has_value() && fragment.has_value() && uniform.has_value()) {
|
||||
return Shader(
|
||||
api, cleanup, ShaderConfig{
|
||||
vertex->withUniform(*uniform), fragment->withUniform(*uniform),
|
||||
GetUniformConfig(request.mUniformType)}
|
||||
);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
std::string SharedShaders::getVertexShaderText(VertexShaderType vertex, ShaderUniformType uniform) {
|
||||
std::optional<ShaderText> vertexText = GetGlslVertexShader(vertex);
|
||||
std::optional<std::string> uniformText = GetGlslUniform(uniform);
|
||||
if (!vertexText.has_value() || !uniformText.has_value()) {
|
||||
abort();
|
||||
}
|
||||
return vertexText->withUniform(*uniformText);
|
||||
}
|
||||
|
||||
std::string SharedShaders::getFragmentShaderText(FragmentShaderType fragment,
|
||||
ShaderUniformType uniform) {
|
||||
std::optional<ShaderText> fragmentText = GetGlslFragmentShader(fragment);
|
||||
std::optional<std::string> uniformText = GetGlslUniform(uniform);
|
||||
if (!fragmentText.has_value() || !uniformText.has_value()) {
|
||||
abort();
|
||||
}
|
||||
return fragmentText->withUniform(*uniformText);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
50
filament/backend/test/SharedShaders.h
Normal file
50
filament/backend/test/SharedShaders.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_SHAREDSHADERS_H
|
||||
#define TNT_SHAREDSHADERS_H
|
||||
|
||||
#include "Shader.h"
|
||||
#include "SharedShadersConstants.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "PlatformRunner.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
enum class ShaderLanguage : uint8_t {
|
||||
GLSL,
|
||||
MSL,
|
||||
WGSL
|
||||
};
|
||||
|
||||
struct ShaderRequest {
|
||||
VertexShaderType mVertexType;
|
||||
FragmentShaderType mFragmentType;
|
||||
ShaderUniformType mUniformType;
|
||||
};
|
||||
|
||||
class SharedShaders {
|
||||
public:
|
||||
static Shader makeShader(filament::backend::DriverApi& api, Cleanup& cleanup,
|
||||
ShaderRequest request);
|
||||
static std::string getVertexShaderText(VertexShaderType vertex, ShaderUniformType uniform);
|
||||
static std::string getFragmentShaderText(FragmentShaderType fragment,
|
||||
ShaderUniformType uniform);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif //TNT_SHAREDSHADERS_H
|
||||
63
filament/backend/test/SharedShadersConstants.h
Normal file
63
filament/backend/test/SharedShadersConstants.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_SHAREDSHADERSCONSTANTS_H
|
||||
#define TNT_SHAREDSHADERSCONSTANTS_H
|
||||
|
||||
#include "math/mathfwd.h"
|
||||
|
||||
enum class ShaderUniformType : uint8_t {
|
||||
None,
|
||||
Simple,
|
||||
SimpleWithPadding,
|
||||
Sampler,
|
||||
};
|
||||
|
||||
struct SimpleMaterialParams {
|
||||
filament::math::float4 color;
|
||||
// 1.0 will be added to this value before use.
|
||||
// The XY values are used to scale position inputs and the Z value is used to set the output
|
||||
// position's Z value.
|
||||
filament::math::float4 scaleMinusOne;
|
||||
// Offset will be applied after scale
|
||||
filament::math::float4 offset;
|
||||
};
|
||||
|
||||
struct SimpleWithPaddingMaterialParams {
|
||||
// The associated uniform structure in the shader will have 64 bytes of padding at the beginning
|
||||
// So users of this struct will need to add 64 bytes to its size and offset all uniform writes.
|
||||
filament::math::float4 color;
|
||||
// 1.0 will be added to this value before use.
|
||||
// The XY values are used to scale position inputs and the Z value is used to set the output
|
||||
// position's Z value.
|
||||
filament::math::float4 scaleMinusOne;
|
||||
// Offset will be applied after scale
|
||||
filament::math::float4 offset;
|
||||
};
|
||||
|
||||
enum class VertexShaderType : uint8_t {
|
||||
Noop,
|
||||
Simple,
|
||||
Textured
|
||||
};
|
||||
|
||||
enum class FragmentShaderType : uint8_t {
|
||||
White,
|
||||
SolidColored,
|
||||
Textured
|
||||
};
|
||||
|
||||
#endif //TNT_SHAREDSHADERSCONSTANTS_H
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "ImageExpectations.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
@@ -40,48 +41,14 @@ using namespace filament::backend;
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
struct MaterialParams {
|
||||
float4 color;
|
||||
float4 scale;
|
||||
};
|
||||
|
||||
class BlitTest : public BackendTest {
|
||||
public:
|
||||
BlitTest() : mCleanup(getDriverApi()) {}
|
||||
|
||||
protected:
|
||||
Shader createShader();
|
||||
|
||||
Cleanup mCleanup;
|
||||
};
|
||||
|
||||
static const char* const triangleVs = R"(#version 450 core
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params;
|
||||
void main() {
|
||||
gl_Position = vec4((mesh_position.xy + 0.5) * params.scale.xy, params.scale.z, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
})";
|
||||
|
||||
static const char* const triangleFs = R"(#version 450 core
|
||||
precision mediump int; precision highp float;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params;
|
||||
void main() {
|
||||
fragColor = params.color;
|
||||
})";
|
||||
|
||||
Shader BlitTest::createShader() {
|
||||
return Shader(getDriverApi(), mCleanup, ShaderConfig{
|
||||
.vertexShader = triangleVs,
|
||||
.fragmentShader = triangleFs,
|
||||
.uniformNames = { "Params" },
|
||||
});
|
||||
}
|
||||
|
||||
static uint32_t toUintColor(float4 color) {
|
||||
color = saturate(color);
|
||||
uint32_t r = color.r * 255.0f;
|
||||
@@ -313,7 +280,11 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
|
||||
constexpr int kSampleCount = 4;
|
||||
|
||||
Shader shader = createShader();
|
||||
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Simple,
|
||||
.mFragmentType = FragmentShaderType::SolidColored,
|
||||
.mUniformType = ShaderUniformType::Simple,
|
||||
});
|
||||
|
||||
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
||||
TrianglePrimitive const triangle(api);
|
||||
@@ -356,14 +327,15 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
|
||||
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
// Draw red triangle into srcRenderTarget.
|
||||
shader.uploadUniform(api, ubuffer, MaterialParams{
|
||||
.color = float4(1, 0, 0, 1),
|
||||
.scale = float4(1, 1, 0.5, 0),
|
||||
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{
|
||||
.color = float4(1, 0, 0, 1),
|
||||
.scaleMinusOne = float4(0, 0, -0.5, 0),
|
||||
.offset = float4(0.5, 0.5, 0, 0),
|
||||
});
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer);
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
|
||||
|
||||
// FIXME: on Metal this triangle is not drawn. Can't understand why.
|
||||
{
|
||||
|
||||
@@ -18,72 +18,18 @@
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
layout(location = 0) out uvec4 indices;
|
||||
|
||||
layout(binding = 0, set = 1) uniform Params {
|
||||
highp vec4 padding[4]; // offset of 64 bytes
|
||||
|
||||
highp vec4 color;
|
||||
highp vec4 offset;
|
||||
} params;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy + params.offset.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragment (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(binding = 0, set = 1) uniform Params {
|
||||
highp vec4 padding[4]; // offset of 64 bytes
|
||||
|
||||
highp vec4 color;
|
||||
highp vec4 offset;
|
||||
} params;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(params.color.rgb, 1.0f);
|
||||
}
|
||||
|
||||
)");
|
||||
|
||||
}
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
// In the shader, these MaterialParams are offset by 64 bytes into the uniform buffer to test buffer
|
||||
// updates with offset.
|
||||
struct MaterialParams {
|
||||
math::float4 color;
|
||||
math::float4 offset;
|
||||
};
|
||||
static_assert(sizeof(MaterialParams) == 8 * sizeof(float));
|
||||
// Uniform config for writing MaterialParams to the shader uniform with 64 bytes of padding.
|
||||
const UniformBindingConfig kBindingConfig = {
|
||||
.dataSize = sizeof(MaterialParams),
|
||||
.bufferSize = sizeof(MaterialParams) + 64,
|
||||
.dataSize = sizeof(SimpleMaterialParams),
|
||||
.bufferSize = sizeof(SimpleMaterialParams) + 64,
|
||||
.byteOffset = 64
|
||||
};
|
||||
|
||||
@@ -93,8 +39,10 @@ public:
|
||||
|
||||
protected:
|
||||
Shader createShader() {
|
||||
return Shader(getDriverApi(), mCleanup, ShaderConfig{
|
||||
vertex, fragment, {"Params"}
|
||||
return SharedShaders::makeShader(getDriverApi(), mCleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Simple,
|
||||
.mFragmentType = FragmentShaderType::SolidColored,
|
||||
.mUniformType = ShaderUniformType::SimpleWithPadding
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,7 +77,7 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
||||
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
@@ -144,17 +92,18 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
// Create a uniform buffer.
|
||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use
|
||||
// a GPU buffer, which is more interesting to test.
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams) + 64,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||
|
||||
api.startCapture(0);
|
||||
|
||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||
// uniform definition.
|
||||
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
|
||||
shader.uploadUniform(api, ubuffer, kBindingConfig, SimpleMaterialParams{
|
||||
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
.scaleMinusOne = { 0.0, 0.0, 0.0, 0.0 },
|
||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
});
|
||||
|
||||
@@ -165,19 +114,21 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
size_t triangleIndex = 0;
|
||||
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
|
||||
const float low = i, high = i + 0.2;
|
||||
const filament::math::float2 v[3] {{low, low}, {high, low}, {low, high}};
|
||||
const filament::math::float2 v[3]{{ low, low },
|
||||
{ high, low },
|
||||
{ low, high }};
|
||||
triangle.updateVertices(v);
|
||||
|
||||
if (updateIndices) {
|
||||
if (triangleIndex % 2 == 0) {
|
||||
// Upload each index separately, to test offsets.
|
||||
const TrianglePrimitive::index_type i[3] {0, 1, 2};
|
||||
const TrianglePrimitive::index_type i[3]{ 0, 1, 2 };
|
||||
triangle.updateIndices(i + 0, 1, 0);
|
||||
triangle.updateIndices(i + 1, 1, 1);
|
||||
triangle.updateIndices(i + 2, 1, 2);
|
||||
} else {
|
||||
// This effectively hides this triangle.
|
||||
const TrianglePrimitive::index_type i[3] {0, 0, 0};
|
||||
const TrianglePrimitive::index_type i[3]{ 0, 0, 0 };
|
||||
triangle.updateIndices(i);
|
||||
}
|
||||
}
|
||||
@@ -220,28 +171,29 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
// Create a uniform buffer.
|
||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
|
||||
// GPU buffer, which is more interesting to test.
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams) + 64,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||
|
||||
// Create a render target.
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {}));
|
||||
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{ colorTexture }}, {}, {}));
|
||||
|
||||
// Upload uniforms for the first triangle.
|
||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||
// uniform definition.
|
||||
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
|
||||
shader.uploadUniform(api, ubuffer, kBindingConfig, SimpleMaterialParams{
|
||||
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
|
||||
.scaleMinusOne = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
});
|
||||
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
||||
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
params.viewport.height = 512;
|
||||
@@ -250,18 +202,18 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
renderTarget, swapChain, shader.getProgram(), params);
|
||||
|
||||
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
|
||||
// color.b, color.a, 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 }
|
||||
);
|
||||
// color.b, color.a, scaleMinusOne, offset.x, and offset.y.
|
||||
const UniformBindingConfig partialBindingConfig = {
|
||||
.dataSize = sizeof(float) * 8,
|
||||
.bufferSize = sizeof(SimpleMaterialParams) + 64,
|
||||
.byteOffset = 64 + offsetof(SimpleMaterialParams, color.b)
|
||||
};
|
||||
shader.uploadUniform(api, ubuffer, partialBindingConfig,
|
||||
std::array<float, 8>{
|
||||
1.0f, 1.0f, // color.b, color.a
|
||||
0.0f, 0.0f, 0.0f, 0.0f, // scale
|
||||
0.5f, 0.5f // offset.x, offset.y
|
||||
});
|
||||
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -96,16 +96,6 @@ struct MaterialParams {
|
||||
float unused;
|
||||
};
|
||||
|
||||
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
|
||||
MaterialParams* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
MaterialParams* sp = (MaterialParams*) buffer;
|
||||
delete sp;
|
||||
};
|
||||
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
|
||||
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
||||
}
|
||||
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
||||
const size_t size = kTexWidth * kTexHeight * 4;
|
||||
void* buffer = calloc(1, size);
|
||||
@@ -140,38 +130,13 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ProgramHandle program;
|
||||
{
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = {
|
||||
{ "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo },
|
||||
{ "Params", { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 1 }, {} }
|
||||
};
|
||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform,
|
||||
std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {
|
||||
{ "test_tex", DescriptorType::SAMPLER, 0 },
|
||||
{ "Params", DescriptorType::UNIFORM_BUFFER, 1 }
|
||||
});
|
||||
program = cleanup.add(api.createProgram(std::move(prog)));
|
||||
}
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
},
|
||||
{
|
||||
DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 1,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
|
||||
Shader shader = Shader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = fullscreenVs,
|
||||
.fragmentShader = fullscreenFs,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}, {"Params"}}
|
||||
});
|
||||
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
|
||||
@@ -218,8 +183,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.program = program;
|
||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
||||
state.program = shader.getProgram();
|
||||
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
@@ -233,20 +198,24 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
|
||||
auto descriptorSet = shader.createDescriptorSet(api);
|
||||
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
||||
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams));
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
UniformBindingConfig uniformBinding{
|
||||
.binding = 1,
|
||||
.descriptorSet = descriptorSet
|
||||
};
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
|
||||
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
@@ -262,20 +231,24 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
|
||||
auto descriptorSet = shader.createDescriptorSet(api);
|
||||
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
||||
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams));
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
UniformBindingConfig uniformBinding{
|
||||
.binding = 1,
|
||||
.descriptorSet = descriptorSet
|
||||
};
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
|
||||
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
@@ -18,16 +18,14 @@
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
|
||||
#include <math/half.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -42,19 +40,6 @@ namespace {
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y *= -1.0f;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragmentTemplate (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
@@ -119,6 +104,15 @@ namespace test {
|
||||
|
||||
template<typename componentType> inline componentType getMaxValue();
|
||||
|
||||
class LoadImageTest : public BackendTest {
|
||||
public:
|
||||
LoadImageTest() {
|
||||
mVertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
|
||||
ShaderUniformType::None);
|
||||
}
|
||||
|
||||
std::string mVertexShader;
|
||||
};
|
||||
|
||||
|
||||
inline std::string stringReplace(const std::string& find, const std::string& replace,
|
||||
@@ -216,7 +210,7 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, UpdateImage2D) {
|
||||
TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
|
||||
// All of these test cases should result in the same rendered image, and thus the same hash.
|
||||
static const uint32_t expectedHash = 3644679986;
|
||||
@@ -306,28 +300,14 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
// Create a program.
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo } };
|
||||
|
||||
std::string const fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(t.textureFormat), fragmentTemplate);
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
|
||||
ProgramHandle const program = cleanup.add(api.createProgram(std::move(prog)));
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
Shader shader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = mVertexShader,
|
||||
.fragmentShader= fragment,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||
});
|
||||
|
||||
// Create a Texture.
|
||||
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
@@ -349,14 +329,15 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
|
||||
}
|
||||
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::NEAREST,
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
||||
defaultRenderTarget, swapChain, program);
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||
defaultRenderTarget, swapChain, shader.getProgram());
|
||||
|
||||
readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash);
|
||||
|
||||
@@ -370,8 +351,9 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
|
||||
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
|
||||
@@ -379,36 +361,23 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
TextureFormat const textureFormat = TextureFormat::SRGB8_A8;
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = createSwapChain();
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
|
||||
// Create a program.
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo } };
|
||||
|
||||
std::string const fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
Shader shader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
||||
"text_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
}}});
|
||||
|
||||
// Create a texture.
|
||||
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
|
||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
||||
|
||||
// Create image data.
|
||||
size_t components; int bpp;
|
||||
@@ -436,6 +405,7 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
@@ -443,8 +413,8 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
||||
defaultRenderTarget, swapChain, program);
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||
defaultRenderTarget, swapChain, shader.getProgram());
|
||||
|
||||
static const uint32_t expectedHash = 359858623;
|
||||
readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash);
|
||||
@@ -453,21 +423,14 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
api.finish();
|
||||
api.stopCapture();
|
||||
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
|
||||
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
||||
@@ -475,38 +438,27 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
TextureFormat textureFormat = TextureFormat::RGBA32F;
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = createSwapChain();
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
|
||||
// Create a program.
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo } };
|
||||
std::string const fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
Shader shader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = mVertexShader,
|
||||
.fragmentShader = fragment,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||
});
|
||||
|
||||
// Create a texture with 3 mip levels.
|
||||
// Base level: 1024
|
||||
// Level 1: 512 <-- upload data and sample from this level
|
||||
// Level 2: 256
|
||||
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, 3,
|
||||
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE);
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 3,
|
||||
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
||||
|
||||
// Create image data.
|
||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
|
||||
@@ -515,6 +467,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
@@ -522,8 +475,8 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
||||
defaultRenderTarget, swapChain, program);
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||
defaultRenderTarget, swapChain, shader.getProgram());
|
||||
|
||||
static const uint32_t expectedHash = 3644679986;
|
||||
readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash);
|
||||
@@ -532,21 +485,14 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
api.finish();
|
||||
api.stopCapture();
|
||||
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, UpdateImage3D) {
|
||||
TEST_F(LoadImageTest, UpdateImage3D) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
|
||||
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
||||
@@ -556,35 +502,24 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = createSwapChain();
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
|
||||
// Create a program.
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo } };
|
||||
std::string fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{ "test_tex", DescriptorType::SAMPLER, 0 }});
|
||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
Shader shader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = mVertexShader,
|
||||
.fragmentShader = fragment,
|
||||
.uniforms = {{"test_tex", DescriptorType::SAMPLER, samplerInfo}}
|
||||
});
|
||||
|
||||
// Create a texture.
|
||||
Handle<HwTexture> texture = api.createTexture(samplerType, 1,
|
||||
textureFormat, 1, 512, 512, 4, usage);
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(samplerType, 1,
|
||||
textureFormat, 1, 512, 512, 4, usage));
|
||||
|
||||
// Create image data for all 4 layers.
|
||||
size_t components; int bpp;
|
||||
@@ -606,6 +541,7 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
@@ -613,8 +549,8 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
||||
defaultRenderTarget, swapChain, program);
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||
defaultRenderTarget, swapChain, shader.getProgram());
|
||||
|
||||
static const uint32_t expectedHash = 3644679986;
|
||||
readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash);
|
||||
@@ -623,17 +559,9 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
api.finish();
|
||||
api.stopCapture();
|
||||
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
namespace {
|
||||
@@ -26,21 +27,6 @@ namespace {
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
layout(location = 0) out uvec4 indices;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragment (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
@@ -71,10 +57,12 @@ TEST_F(BackendTest, MRT) {
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.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)));
|
||||
Shader shader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
|
||||
ShaderUniformType::None),
|
||||
.fragmentShader = fragment,
|
||||
.uniforms = {}
|
||||
});
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
@@ -122,7 +110,7 @@ TEST_F(BackendTest, MRT) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.program = program;
|
||||
state.program = shader.getProgram();
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -34,18 +35,7 @@ namespace {
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
layout(location = 0) out vec2 uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
uv = (mesh_position.xy * 0.5 + 0.5);
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragment (R"(#version 450 core
|
||||
std::string fragmentTexturedLod (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(location = 0) in vec2 uv;
|
||||
@@ -57,18 +47,6 @@ void main() {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string whiteFragment (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0, set = 1) uniform sampler2D backend_test_sib_tex;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(1.0);
|
||||
}
|
||||
)");
|
||||
|
||||
}
|
||||
|
||||
namespace test {
|
||||
@@ -88,39 +66,25 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program that draws only white.
|
||||
Handle<HwProgram> whiteProgram;
|
||||
{
|
||||
ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
|
||||
whiteProgram = cleanup.add(api.createProgram(std::move(p)));
|
||||
}
|
||||
Shader whiteShader = SharedShaders::makeShader(api, cleanup, ShaderRequest {
|
||||
.mVertexType = VertexShaderType::Textured,
|
||||
.mFragmentType = FragmentShaderType::White,
|
||||
.mUniformType = ShaderUniformType::Sampler
|
||||
});
|
||||
|
||||
// Create a program that samples a texture.
|
||||
Handle<HwProgram> textureProgram;
|
||||
{
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "backend_test", "sib_tex", 0,
|
||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "backend_test_sib_tex",
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, samplerInfo } };
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
|
||||
textureProgram = cleanup.add(api.createProgram(std::move(p)));
|
||||
}
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
|
||||
DescriptorSetHandle descriptorSet[2];
|
||||
descriptorSet[0] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
descriptorSet[1] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
std::string vertexShader = SharedShaders::getVertexShaderText(
|
||||
VertexShaderType::Textured, ShaderUniformType::Sampler);
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo {
|
||||
"backend_test", "sib_tex", 0,
|
||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
Shader texturedShader(api, cleanup, ShaderConfig {
|
||||
.vertexShader = vertexShader,
|
||||
.fragmentShader = fragmentTexturedLod,
|
||||
.uniforms = {{
|
||||
"backend_test_sib_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
}}
|
||||
});
|
||||
|
||||
// Create a texture that has 4 mip levels. Each level is a different color.
|
||||
// Level 0: 128x128 (red)
|
||||
@@ -129,9 +93,10 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
// Level 3: 16x16 (yellow)
|
||||
const size_t kTextureSize = 128;
|
||||
const size_t kMipLevels = 4;
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
|
||||
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kMipLevels, TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT
|
||||
| TextureUsage::UPLOADABLE));
|
||||
|
||||
// Create image data.
|
||||
auto pixelFormat = PixelDataFormat::RGBA;
|
||||
@@ -179,7 +144,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
PipelineState ps = {};
|
||||
ps.program = whiteProgram;
|
||||
ps.program = whiteShader.getProgram();
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.depthWrite = false;
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
@@ -187,7 +152,8 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget =
|
||||
cleanup.add(api.createDefaultRenderTarget(0));
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -197,18 +163,19 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.program = textureProgram;
|
||||
state.pipelineLayout.setLayout = { descriptorSetLayout };
|
||||
state.program = texturedShader.getProgram();
|
||||
state.pipelineLayout.setLayout = { texturedShader.getDescriptorSetLayout() };
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = SamplerCompareFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
api.updateDescriptorSetTexture(descriptorSet[0], 0, texture13, {
|
||||
DescriptorSetHandle descriptorSet13 = texturedShader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet13, 0, texture13, {
|
||||
.filterMag = SamplerMagFilter::NEAREST,
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
|
||||
api.bindDescriptorSet(descriptorSet[0], 0, {});
|
||||
api.bindDescriptorSet(descriptorSet13, 0, {});
|
||||
|
||||
// Render a triangle to the screen, sampling from mip level 1.
|
||||
// Because the min level is 1, the result color should be the white triangle drawn in the
|
||||
@@ -221,11 +188,12 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
// Adjust the base mip to 2.
|
||||
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
|
||||
|
||||
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
|
||||
DescriptorSetHandle descriptorSet22 = texturedShader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet22, 0, texture22, {
|
||||
.filterMag = SamplerMagFilter::NEAREST,
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
|
||||
api.bindDescriptorSet(descriptorSet[1], 0, {});
|
||||
api.bindDescriptorSet(descriptorSet22, 0, {});
|
||||
|
||||
// Render a second, smaller, triangle, again sampling from mip level 1.
|
||||
// This triangle should be yellow striped.
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
namespace {
|
||||
@@ -26,7 +27,7 @@ namespace {
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
std::string vertex(R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
@@ -46,16 +47,6 @@ void main() {
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragment (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(1.0);
|
||||
}
|
||||
|
||||
)");
|
||||
|
||||
}
|
||||
|
||||
namespace test {
|
||||
@@ -78,9 +69,11 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
api.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)));
|
||||
Shader shader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = vertex,
|
||||
.fragmentShader = SharedShaders::getFragmentShaderText(FragmentShaderType::White,
|
||||
ShaderUniformType::None),
|
||||
});
|
||||
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
|
||||
@@ -89,12 +82,12 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
||||
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.program = program;
|
||||
state.program = shader.getProgram();
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
@@ -30,6 +31,7 @@ using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
#ifndef FILAMENT_IOS
|
||||
|
||||
#include <imageio/ImageEncoder.h>
|
||||
#include <image/ColorTransform.h>
|
||||
|
||||
@@ -42,20 +44,7 @@ namespace {
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragmentFloat (R"(#version 450 core
|
||||
std::string fragmentFloat(R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
@@ -65,7 +54,7 @@ void main() {
|
||||
|
||||
)");
|
||||
|
||||
std::string fragmentUint (R"(#version 450 core
|
||||
std::string fragmentUint(R"(#version 450 core
|
||||
|
||||
layout(location = 0) out uvec4 fragColor;
|
||||
|
||||
@@ -108,7 +97,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
size_t samples = 1;
|
||||
|
||||
// The size of the actual render target, taking mip level into account;
|
||||
size_t getRenderTargetSize () const {
|
||||
size_t getRenderTargetSize() const {
|
||||
return std::max(size_t(1), renderTargetBaseSize >> mipLevel);
|
||||
}
|
||||
|
||||
@@ -138,11 +127,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
}
|
||||
|
||||
void exportScreenshot(void* pixelData) const {
|
||||
#ifndef FILAMENT_IOS
|
||||
#ifndef FILAMENT_IOS
|
||||
const size_t width = readRect.width, height = readRect.height;
|
||||
LinearImage image(width, height, 4);
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
|
||||
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*) pixelData);
|
||||
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
|
||||
}
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
|
||||
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
|
||||
@@ -151,13 +140,13 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
||||
png.c_str());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void exportRawBytes(void* pixelData) const {
|
||||
std::string out = std::string(testName) + ".raw";
|
||||
std::ofstream outputStream(out.c_str(), std::ios::binary | std::ios::trunc);
|
||||
outputStream.write((char*) pixelData, getBufferSizeBytes());
|
||||
outputStream.write((char*)pixelData, getBufferSizeBytes());
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
@@ -240,22 +229,20 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
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)));
|
||||
}
|
||||
{
|
||||
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
programUint = cleanup.add(api.createProgram(std::move(p)));
|
||||
}
|
||||
std::string vertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
|
||||
ShaderUniformType::None);
|
||||
Shader floatShader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = vertexShader,
|
||||
.fragmentShader = fragmentFloat,
|
||||
.uniforms = {}
|
||||
});
|
||||
Shader uintShader(api, cleanup, ShaderConfig{
|
||||
.vertexShader = vertexShader,
|
||||
.fragmentShader = fragmentUint,
|
||||
.uniforms = {}
|
||||
});
|
||||
|
||||
|
||||
for (const auto& t : testCases)
|
||||
{
|
||||
for (const auto& t: testCases) {
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
Handle<HwSwapChain> swapChain;
|
||||
if (t.useDefaultRT) {
|
||||
@@ -292,7 +279,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
||||
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
params.viewport.height = t.getRenderTargetSize();
|
||||
@@ -305,9 +292,9 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
|
||||
PipelineState state;
|
||||
state.program = programFloat;
|
||||
state.program = floatShader.getProgram();
|
||||
if (isUnsignedIntFormat(t.textureFormat)) {
|
||||
state.program = programUint;
|
||||
state.program = uintShader.getProgram();
|
||||
}
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
@@ -325,7 +312,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
|
||||
{{ texture }}, {}, {}));
|
||||
p.clearColor = {1.f, 0.f, 0.f, 1.f};
|
||||
p.clearColor = { 1.f, 0.f, 0.f, 1.f };
|
||||
api.beginRenderPass(mipLevelOneRT, p);
|
||||
api.endRenderPass();
|
||||
}
|
||||
@@ -335,28 +322,28 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
|
||||
PixelBufferDescriptor descriptor(buffer, t.getBufferSizeBytes(), t.format, t.type,
|
||||
t.alignment, t.left, t.top, t.getPixelBufferStride(), [](void* buffer, size_t size,
|
||||
void* user) {
|
||||
const auto* test = (const TestCase*) user;
|
||||
void* user) {
|
||||
const auto* test = (const TestCase*)user;
|
||||
assert_invariant(test);
|
||||
|
||||
test->exportScreenshot(buffer);
|
||||
//test->exportRawBytes(buffer);
|
||||
|
||||
// Hash the contents of the buffer and check that they match.
|
||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
||||
uint32_t hash = utils::hash::murmur3((const uint32_t*)buffer, size / 4, 0);
|
||||
|
||||
ASSERT_EQ(test->hash, hash) << test->testName <<
|
||||
" failed: hashes do not match." << std::endl;
|
||||
" failed: hashes do not match." << std::endl;
|
||||
|
||||
free(buffer);
|
||||
}, (void*) &t);
|
||||
}, (void*)&t);
|
||||
|
||||
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
|
||||
t.readRect.height, std::move(descriptor));
|
||||
|
||||
// Now render red over what was just rendered. This ensures that readPixels captures the
|
||||
// state of rendering between render passes.
|
||||
params.clearColor = {1.f, 0.f, 0.f, 1.f};
|
||||
params.clearColor = { 1.f, 0.f, 0.f, 1.f };
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.endRenderPass();
|
||||
|
||||
@@ -376,25 +363,27 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
|
||||
auto swapChain = cleanup.add(
|
||||
api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
||||
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Noop,
|
||||
.mFragmentType = FragmentShaderType::White,
|
||||
.mUniformType = ShaderUniformType::None
|
||||
});
|
||||
|
||||
// Create a Texture and RenderTarget to render into.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
1, // samples
|
||||
renderTargetSize, // width
|
||||
renderTargetSize, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
SamplerType::SAMPLER_2D, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
1, // samples
|
||||
renderTargetSize, // width
|
||||
renderTargetSize, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR,
|
||||
@@ -411,7 +400,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
||||
params.clearColor = { 0.f, 0.f, 1.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
params.viewport.height = renderTargetSize;
|
||||
@@ -420,7 +409,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
void* buffer = calloc(1, renderTargetSize * renderTargetSize * 4);
|
||||
|
||||
PipelineState state;
|
||||
state.program = program;
|
||||
state.program = shader.getProgram();
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
@@ -444,11 +433,12 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
|
||||
[](void* buffer, size_t size, void* user) {
|
||||
ReadPixelsTest* test = (ReadPixelsTest*) user;
|
||||
ReadPixelsTest* test = (ReadPixelsTest*)user;
|
||||
test->readPixelsFinished = true;
|
||||
}, this);
|
||||
|
||||
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
|
||||
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize,
|
||||
std::move(descriptor));
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -28,42 +29,19 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
layout(location = 0) out vec2 uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
uv = (mesh_position.xy * 0.5 + 0.5);
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragment (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
||||
|
||||
void main() {
|
||||
fragColor = texture(test_tex, uv);
|
||||
}
|
||||
)");
|
||||
|
||||
}
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
Shader createShader(DriverApi& api, Cleanup& cleanup, Backend backend) {
|
||||
return SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Textured,
|
||||
.mFragmentType = FragmentShaderType::Textured,
|
||||
.mUniformType = ShaderUniformType::Sampler
|
||||
});
|
||||
}
|
||||
|
||||
// Rendering an external image without setting any data should not crash.
|
||||
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
auto& api = getDriverApi();
|
||||
@@ -73,57 +51,41 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo } };
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Shader shader = createShader(api, cleanup, sBackend);
|
||||
|
||||
// Create a program that samples a texture.
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
backend::Handle<HwProgram> program = cleanup.add(api.createProgram(std::move(p)));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(
|
||||
api.createDefaultRenderTarget(0));
|
||||
|
||||
// Create a texture that will be backed by an external image.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
const NativeView& view = getNativeView();
|
||||
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_EXTERNAL, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
1, // samples
|
||||
view.width, // width
|
||||
view.height, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
SamplerType::SAMPLER_EXTERNAL, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
1, // samples
|
||||
view.width, // width
|
||||
view.height, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
||||
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.program = program;
|
||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
||||
state.program = shader.getProgram();
|
||||
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
|
||||
api.startCapture(0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
@@ -155,29 +117,11 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 },
|
||||
samplerInfo } };
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Shader shader = createShader(api, cleanup, sBackend);
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
|
||||
// Create a program that samples a texture.
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(
|
||||
api.createDefaultRenderTarget(0));
|
||||
|
||||
// require users to create two Filament textures and have two material parameters
|
||||
// add a "plane" parameter to setExternalImage
|
||||
@@ -198,10 +142,12 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
values[1] = values[0];
|
||||
values[2] = values[0];
|
||||
values[3] = values[0];
|
||||
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**) keys, (const void**) values, 4, nullptr, nullptr);
|
||||
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys,
|
||||
(const void**)values, 4, nullptr, nullptr);
|
||||
CVPixelBufferRef pixBuffer = nullptr;
|
||||
CVReturn status =
|
||||
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options, &pixBuffer);
|
||||
CVPixelBufferCreate(kCFAllocatorDefault, 1024, 1024, kCVPixelFormatType_32BGRA, options,
|
||||
&pixBuffer);
|
||||
assert(status == kCVReturnSuccess);
|
||||
|
||||
// Fill image with checker-pattern.
|
||||
@@ -210,7 +156,7 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
const uint32_t black = 0xFF000000;
|
||||
CVReturn lockStatus = CVPixelBufferLockBaseAddress(pixBuffer, 0);
|
||||
assert(lockStatus == kCVReturnSuccess);
|
||||
uint32_t* pix = (uint32_t*) CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0);
|
||||
uint32_t* pix = (uint32_t*)CVPixelBufferGetBaseAddressOfPlane(pixBuffer, 0);
|
||||
assert(pix);
|
||||
for (size_t r = 0; r < 1024; r++) {
|
||||
for (size_t c = 0; c < 1024; c++) {
|
||||
@@ -230,13 +176,13 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 1.f, 0.f, 1.f};
|
||||
params.clearColor = { 0.f, 1.f, 0.f, 1.f };
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.program = program;
|
||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
||||
state.program = shader.getProgram();
|
||||
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
@@ -247,7 +193,6 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
// Render a triangle.
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
@@ -27,23 +28,6 @@ namespace test {
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
static const char* const triangleVs = R"(#version 450 core
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
})";
|
||||
|
||||
static const char* const triangleFs = R"(#version 450 core
|
||||
precision mediump int; precision highp float;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = vec4(1.0f);
|
||||
})";
|
||||
|
||||
TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
@@ -85,10 +69,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
auto swapChain = cleanup.add(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)));
|
||||
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Noop,
|
||||
.mFragmentType = FragmentShaderType::White,
|
||||
.mUniformType = ShaderUniformType::None,
|
||||
});
|
||||
|
||||
// Create source color and depth textures.
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
|
||||
@@ -133,7 +118,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState ps = {};
|
||||
ps.program = program;
|
||||
ps.program = shader.getProgram();
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.depthWrite = false;
|
||||
|
||||
@@ -175,10 +160,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
auto swapChain = cleanup.add(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)));
|
||||
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Noop,
|
||||
.mFragmentType = FragmentShaderType::White,
|
||||
.mUniformType = ShaderUniformType::None,
|
||||
});
|
||||
|
||||
// Create a source color textures.
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
@@ -220,7 +206,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState ps = {};
|
||||
ps.program = program;
|
||||
ps.program = shader.getProgram();
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.depthWrite = false;
|
||||
|
||||
|
||||
@@ -17,43 +17,13 @@
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string vertex (R"(#version 450 core
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
}
|
||||
)");
|
||||
|
||||
std::string fragment (R"(#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(1.0);
|
||||
}
|
||||
|
||||
)");
|
||||
|
||||
}
|
||||
|
||||
namespace test {
|
||||
|
||||
// 1. Clear the stencil buffer to all zeroes.
|
||||
@@ -64,7 +34,7 @@ namespace test {
|
||||
class BasicStencilBufferTest : public BackendTest {
|
||||
public:
|
||||
|
||||
Handle<HwSwapChain> mSwapChain;
|
||||
Handle <HwSwapChain> mSwapChain;
|
||||
ProgramHandle mProgram;
|
||||
Cleanup mCleanup;
|
||||
|
||||
@@ -77,21 +47,23 @@ public:
|
||||
mSwapChain = mCleanup.add(createSwapChain());
|
||||
api.makeCurrent(mSwapChain, mSwapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
mProgram = mCleanup.add(api.createProgram(std::move(p)));
|
||||
Shader shader = SharedShaders::makeShader(api, mCleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Noop,
|
||||
.mFragmentType = FragmentShaderType::White,
|
||||
.mUniformType = ShaderUniformType::None
|
||||
});
|
||||
mProgram = shader.getProgram();
|
||||
}
|
||||
|
||||
void RunTest(Handle<HwRenderTarget> renderTarget) {
|
||||
void RunTest(Handle <HwRenderTarget> renderTarget) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
// We'll be using a triangle as geometry.
|
||||
TrianglePrimitive smallTriangle(api);
|
||||
static filament::math::float2 vertices[3] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ -0.5, 0.5 }
|
||||
{ 0.5, -0.5 },
|
||||
{ -0.5, 0.5 }
|
||||
};
|
||||
smallTriangle.updateVertices(vertices);
|
||||
TrianglePrimitive triangle(api);
|
||||
@@ -100,7 +72,7 @@ public:
|
||||
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL;
|
||||
params.viewport = {0, 0, 512, 512};
|
||||
params.viewport = { 0, 0, 512, 512 };
|
||||
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
params.clearStencil = 0u;
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
@@ -153,7 +125,7 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||
{{colorTexture}}, {}, {{stencilTexture}}));
|
||||
{{ colorTexture }}, {}, {{ stencilTexture }}));
|
||||
|
||||
RunTest(renderTarget);
|
||||
|
||||
@@ -171,10 +143,11 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1,
|
||||
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
|
||||
{{ colorTexture }}, { depthStencilTexture }, {{ depthStencilTexture }}));
|
||||
|
||||
RunTest(renderTarget);
|
||||
|
||||
@@ -194,15 +167,17 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
|
||||
// Performs an auto-resolve on the color.
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1,
|
||||
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1,
|
||||
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||
auto renderTarget0 = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
|
||||
{{}}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||
auto renderTarget1 = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
|
||||
{{ colorTexture }}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||
|
||||
api.startCapture(0);
|
||||
|
||||
@@ -210,8 +185,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
TrianglePrimitive smallTriangle(api);
|
||||
static filament::math::float2 vertices[3] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ -0.5, 0.5 }
|
||||
{ 0.5, -0.5 },
|
||||
{ -0.5, 0.5 }
|
||||
};
|
||||
smallTriangle.updateVertices(vertices);
|
||||
TrianglePrimitive triangle(api);
|
||||
@@ -220,7 +195,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::STENCIL;
|
||||
params.viewport = {0, 0, 512, 512};
|
||||
params.viewport = { 0, 0, 512, 512 };
|
||||
params.clearStencil = 0u;
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
@@ -317,6 +317,15 @@ public:
|
||||
*/
|
||||
size_t metalUploadBufferSizeBytes = 512 * 1024;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired.
|
||||
*
|
||||
* Each frame rendered requires a CAMetalDrawable texture, which is
|
||||
* presented on-screen at the completion of each frame. These are
|
||||
* limited and provided round-robin style by the system.
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
|
||||
@@ -138,6 +138,7 @@ Engine* FEngine::create(Builder const& builder) {
|
||||
.forceGLES2Context = instance->getConfig().forceGLES2Context,
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
|
||||
@@ -733,6 +734,7 @@ int FEngine::loop() {
|
||||
.forceGLES2Context = mConfig.forceGLES2Context,
|
||||
.stereoscopicType = mConfig.stereoscopicType,
|
||||
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
|
||||
|
||||
109
test/code-correctness/src/run.py
Normal file
109
test/code-correctness/src/run.py
Normal file
@@ -0,0 +1,109 @@
|
||||
# Copyright (C) 2025 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import glob
|
||||
import yaml
|
||||
import hashlib
|
||||
import concurrent.futures
|
||||
import re
|
||||
|
||||
from utils import execute, ArgParseImpl
|
||||
|
||||
def get_line(file_name, offset):
|
||||
with open(f'{file_name}', 'rb') as file:
|
||||
bytes = file.read()[0:offset]
|
||||
f_str = bytes.decode('utf-8')
|
||||
return len(f_str.split('\n'))
|
||||
return -1
|
||||
|
||||
def get_func_name(msg):
|
||||
pattern = r"\'(.+)\'"
|
||||
res = re.findall(pattern, msg)
|
||||
if len(res) > 0:
|
||||
return res[0].replace("'", '')
|
||||
return msg
|
||||
|
||||
def run_tidy(files):
|
||||
files_str = ' '.join(files)
|
||||
hid = hashlib.md5(files_str.encode('utf-8')).hexdigest()
|
||||
_, _ = execute(f'clang-tidy --export-fixes=/tmp/{hid}.yaml --quiet --checks=-*,bugprone-exception-escape {files_str}')
|
||||
results = []
|
||||
with open(f'/tmp/{hid}.yaml', 'r') as file:
|
||||
data = yaml.safe_load(file)
|
||||
for d in data['Diagnostics']:
|
||||
if d['DiagnosticName'] != 'bugprone-exception-escape':
|
||||
continue
|
||||
msg = d['DiagnosticMessage']
|
||||
fpath = msg['FilePath']
|
||||
offset = msg['FileOffset']
|
||||
line_num = get_line(fpath, offset)
|
||||
results.append((msg['FilePath'].replace(f'{os.getcwd()}/', ''), line_num, get_func_name(msg['Message'])))
|
||||
return results
|
||||
|
||||
def exception_escape_test():
|
||||
files = glob.glob('filament/**/*.mm', recursive=True) + \
|
||||
glob.glob('filament/**/*.cpp', recursive=True) + \
|
||||
glob.glob('filament/**/*.h', recursive=True)
|
||||
|
||||
num_workers = 5 # Number of threads to spawn
|
||||
part_len = len(files) // num_workers
|
||||
workloads = []
|
||||
for i in range(num_workers):
|
||||
next = min(len(files), part_len)
|
||||
workloads.append(files[0:next])
|
||||
files = files[next:]
|
||||
|
||||
all_results = []
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor:
|
||||
future_to_worker_id = {executor.submit(run_tidy, workloads[i]): i for i in range(num_workers)}
|
||||
|
||||
for future in concurrent.futures.as_completed(future_to_worker_id):
|
||||
worker_id = future_to_worker_id[future]
|
||||
try:
|
||||
all_results.extend(future.result())
|
||||
except Exception as exc:
|
||||
print(f"Main: Worker {worker_id} generated an exception: {exc}")
|
||||
test_name = 'code-correctness::exception-escape'
|
||||
failure_str_lines = []
|
||||
if len(all_results) > 0:
|
||||
failure_str_lines.append(f'Number of failures: {len(all_results)}')
|
||||
for fname, line_num, msg in all_results:
|
||||
failure_str_lines.append(f'{fname}({line_num}): {msg}()')
|
||||
return (len(all_results) == 0, failure_str_lines)
|
||||
|
||||
TESTS = [
|
||||
(exception_escape_test,
|
||||
'exception-escape',
|
||||
'\'an exception may be thrown in a function which should not throw exceptions\'')
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
has_failures = False
|
||||
for test_func, test_name, test_desc in TESTS:
|
||||
result, res_strs = test_func()
|
||||
ss = ' ' * 4
|
||||
if result:
|
||||
print(f'[{test_name}] PASSED')
|
||||
else:
|
||||
has_failures = True
|
||||
print(f'[{test_name}] FAILED')
|
||||
print(f'{ss}Description: \'{test_desc}\'')
|
||||
for s in res_strs:
|
||||
print(f'{ss}{s}')
|
||||
if has_failures:
|
||||
# TODO: Enable this when we've fixed all the exception-escape errors
|
||||
#exit(1)
|
||||
pass
|
||||
|
||||
68
test/code-correctness/src/utils.py
Normal file
68
test/code-correctness/src/utils.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# Copyright (C) 2024 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def execute(cmd,
|
||||
cwd=None,
|
||||
capture_output=True,
|
||||
stdin=None,
|
||||
env=None,
|
||||
raise_errors=False):
|
||||
in_env = os.environ
|
||||
in_env.update(env if env else {})
|
||||
home = os.environ['HOME']
|
||||
if f'{home}/bin' not in in_env['PATH']:
|
||||
in_env['PATH'] = in_env['PATH'] + f':{home}/bin'
|
||||
|
||||
stdout = subprocess.PIPE if capture_output else sys.stdout
|
||||
stderr = subprocess.PIPE if capture_output else sys.stdout
|
||||
output = ''
|
||||
err_output = ''
|
||||
return_code = -1
|
||||
kwargs = {
|
||||
'cwd': cwd,
|
||||
'env': in_env,
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
'stdin': stdin,
|
||||
'universal_newlines': True
|
||||
}
|
||||
if capture_output:
|
||||
process = subprocess.Popen(cmd.split(' '), **kwargs)
|
||||
output, err_output = process.communicate()
|
||||
return_code = process.returncode
|
||||
else:
|
||||
return_code = subprocess.call(cmd.split(' '), **kwargs)
|
||||
|
||||
if return_code:
|
||||
# Error
|
||||
if raise_errors:
|
||||
raise subprocess.CalledProcessError(return_code, cmd)
|
||||
if output:
|
||||
if type(output) != str:
|
||||
try:
|
||||
output = output.decode('utf-8').strip()
|
||||
except UnicodeDecodeError as e:
|
||||
print('cannot decode ', output, file=sys.stderr)
|
||||
return return_code, (output if return_code == 0 else err_output)
|
||||
|
||||
class ArgParseImpl(argparse.ArgumentParser):
|
||||
def error(self, message):
|
||||
sys.stderr.write('error: %s\n' % message)
|
||||
self.print_help()
|
||||
sys.exit(1)
|
||||
28
test/code-correctness/test.sh
Normal file
28
test/code-correctness/test.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2025 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#!/usr/bin/bash
|
||||
|
||||
CODE_CORRECTNESS_TEST_DIR="$(pwd)/test/code-correctness"
|
||||
|
||||
# Check if the clang-tidy command exists and is executable
|
||||
if ! command -v clang-tidy > /dev/null 2>&1; then
|
||||
# If command -v fails (returns a non-zero exit status), clang-tidy is not found
|
||||
echo "Error: clang-tidy command not found." >&2
|
||||
echo "Please install clang-tidy or ensure it is in your system's PATH." >&2
|
||||
exit 1 # Exit the script with an error code (conventionally non-zero for failure)
|
||||
fi
|
||||
|
||||
set -e && set -x && \
|
||||
python3 ${CODE_CORRECTNESS_TEST_DIR}/src/run.py
|
||||
15
third_party/dawn/tnt/CMakeLists.txt
vendored
15
third_party/dawn/tnt/CMakeLists.txt
vendored
@@ -33,22 +33,36 @@ if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
||||
tint_lang_core
|
||||
tint_lang_core_constant
|
||||
tint_lang_core_intrinsic
|
||||
tint_lang_core_ir
|
||||
tint_lang_core_ir_transform
|
||||
tint_lang_core_ir_type
|
||||
tint_lang_core_type
|
||||
tint_lang_spirv
|
||||
tint_lang_spirv_intrinsic
|
||||
tint_lang_spirv_ir
|
||||
tint_lang_spirv_reader
|
||||
tint_lang_spirv_reader_ast_lower
|
||||
tint_lang_spirv_reader_ast_parser
|
||||
tint_lang_spirv_reader_common
|
||||
tint_lang_spirv_reader_lower
|
||||
tint_lang_spirv_reader_parser
|
||||
tint_lang_spirv_type
|
||||
tint_lang_spirv_validate
|
||||
tint_lang_spirv_writer
|
||||
tint_lang_wgsl
|
||||
tint_lang_wgsl_ast
|
||||
tint_lang_wgsl_ast_transform
|
||||
tint_lang_wgsl_common
|
||||
tint_lang_wgsl_features
|
||||
tint_lang_wgsl_intrinsic
|
||||
tint_lang_wgsl_ir
|
||||
tint_lang_wgsl_program
|
||||
tint_lang_wgsl_resolver
|
||||
tint_lang_wgsl_sem
|
||||
tint_lang_wgsl_writer
|
||||
tint_lang_wgsl_writer_ast_printer
|
||||
tint_lang_wgsl_writer_ir_to_program
|
||||
tint_lang_wgsl_writer_raise
|
||||
tint_results
|
||||
tint_utils
|
||||
tint_utils_diagnostic
|
||||
@@ -58,6 +72,7 @@ if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
||||
tint_utils_strconv
|
||||
tint_utils_symbol
|
||||
tint_utils_text
|
||||
tint_utils_text_generator
|
||||
)
|
||||
|
||||
# Use the following no-op definitions to introduce a library and its dependency.
|
||||
|
||||
Reference in New Issue
Block a user