Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f366ac3a35 | ||
|
|
f78770c7fb | ||
|
|
feca632486 | ||
|
|
68652347b3 | ||
|
|
585c10d9ba | ||
|
|
7e7a481320 | ||
|
|
b9a6cc5462 | ||
|
|
755912e676 |
@@ -12,7 +12,7 @@ jobs:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-32core'
|
||||
runs-on: ubuntu-22.04-32core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
validate-docs:
|
||||
name: validate-docs
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
runs-on: ubuntu-22.04-4core
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
runs-on: 'ubuntu-24.04-8core'
|
||||
runs-on: ubuntu-22.04-8core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
|
||||
|
||||
steps:
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
|
||||
|
||||
steps:
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -334,9 +334,6 @@ if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (FILAMENT_USE_EXTERNAL_GLES3)
|
||||
|
||||
@@ -8,4 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- Fix build/compile errors when upgrading to MacOS 15.4
|
||||
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.59.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.58.1'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.59.0'
|
||||
pod 'Filament', '~> 1.58.1'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,13 +7,6 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.59.1
|
||||
|
||||
|
||||
## v1.59.0
|
||||
|
||||
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
|
||||
|
||||
## v1.58.2
|
||||
|
||||
- engine: Generate 1D instead of 3D LUTs for color grading whenever possible.
|
||||
|
||||
@@ -143,15 +143,6 @@ abstract class MaterialCompiler extends TaskWithBinary {
|
||||
matcArgs += ['-a', 'vulkan']
|
||||
}
|
||||
|
||||
def include_webgpu = providers
|
||||
.gradleProperty("com.google.android.filament.include-webgpu")
|
||||
.forUseAtConfigurationTime().present
|
||||
|
||||
if (!include_webgpu) {
|
||||
matcArgs += ['-a', 'webgpu']
|
||||
}
|
||||
|
||||
|
||||
def mat_no_opt = providers
|
||||
.gradleProperty("com.google.android.filament.matnopt")
|
||||
.forUseAtConfigurationTime().present
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.59.0
|
||||
VERSION_NAME=1.58.1
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -233,8 +233,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanTexture.cpp
|
||||
src/vulkan/VulkanTexture.h
|
||||
src/vulkan/VulkanYcbcrConversionCache.cpp
|
||||
src/vulkan/VulkanYcbcrConversionCache.h
|
||||
)
|
||||
if (LINUX OR WIN32)
|
||||
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
|
||||
@@ -255,10 +253,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUConstants.h
|
||||
src/webgpu/WebGPUDriver.cpp
|
||||
src/webgpu/WebGPUDriver.h
|
||||
src/webgpu/WebGPUSwapChain.cpp
|
||||
src/webgpu/WebGPUSwapChain.h
|
||||
src/webgpu/WebGPUHandles.cpp
|
||||
src/webgpu/WebGPUHandles.h
|
||||
)
|
||||
if (WIN32)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||
@@ -308,11 +302,6 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
# add this subproject to the Filament folder
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Filament)
|
||||
|
||||
# we need to export the headers properly for backend.lib on windows
|
||||
if (WIN32 AND FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE "WGPU_IMPLEMENTATION")
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Expose a header-only target to minimize dependencies.
|
||||
# ==================================================================================================
|
||||
@@ -505,9 +494,7 @@ if (APPLE OR LINUX)
|
||||
test/ShaderGenerator.cpp
|
||||
test/TrianglePrimitive.cpp
|
||||
test/Arguments.cpp
|
||||
test/ImageExpectations.cpp
|
||||
test/Lifetimes.cpp
|
||||
test/Shader.cpp
|
||||
test/test_FeedbackLoops.cpp
|
||||
test/test_Blit.cpp
|
||||
test/test_MissingRequiredAttributes.cpp
|
||||
@@ -523,7 +510,6 @@ if (APPLE OR LINUX)
|
||||
test/test_Handles.cpp
|
||||
)
|
||||
set(BACKEND_TEST_LIBS
|
||||
absl::str_format
|
||||
backend
|
||||
getopt
|
||||
gtest
|
||||
@@ -539,7 +525,7 @@ if (APPLE AND NOT IOS)
|
||||
list(APPEND BACKEND_TEST_SRC
|
||||
test/test_RenderExternalImage.cpp)
|
||||
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
|
||||
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
|
||||
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
|
||||
|
||||
set(BACKEND_TEST_DEPS
|
||||
OSDependent
|
||||
|
||||
@@ -361,7 +361,8 @@ public:
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat);
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
|
||||
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
|
||||
@@ -40,11 +40,9 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
// TODO WebGPU examine right size of handles
|
||||
#define HandleAllocatorWGPU HandleAllocator<64, 160, 552> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -211,8 +211,4 @@ template class HandleAllocatorVK;
|
||||
template class HandleAllocatorMTL;
|
||||
#endif
|
||||
|
||||
#if defined (FILAMENT_SUPPORTS_WEBGPU)
|
||||
template class HandleAllocatorWGPU;
|
||||
#endif
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -1177,17 +1177,9 @@ size_t MetalDriver::getMaxUniformBufferSize() {
|
||||
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxTextureSize(SamplerType type) {
|
||||
if (type == SamplerType::SAMPLER_3D) {
|
||||
return 2048;
|
||||
}
|
||||
if (mContext->highestSupportedGpuFamily.apple >= 3 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 2) {
|
||||
return 16384;
|
||||
} else if (mContext->highestSupportedGpuFamily.apple >= 2) {
|
||||
return 8192;
|
||||
}
|
||||
return 4096;
|
||||
size_t MetalDriver::getMaxTextureSize(SamplerType) {
|
||||
// TODO: return the actual size instead of hardcoding the minspec
|
||||
return 2048;
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxArrayTextureLayers() {
|
||||
|
||||
@@ -241,9 +241,7 @@ size_t NoopDriver::getMaxUniformBufferSize() {
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
|
||||
// NoopDriver is being actively used for other purposes. This needs to be resolved before we
|
||||
// can change it to 2048. b/406832484
|
||||
return 16384u;
|
||||
return 4096u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxArrayTextureLayers() {
|
||||
|
||||
@@ -566,11 +566,7 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
} else if (strstr(renderer, "AMD") ||
|
||||
strstr(renderer, "ATI")) {
|
||||
// AMD/ATI GPU
|
||||
} else if (strstr(renderer, "Mozilla")) {
|
||||
bugs->disable_invalidate_framebuffer = true;
|
||||
}
|
||||
|
||||
if (strstr(vendor, "Mesa")) {
|
||||
} else if (strstr(vendor, "Mesa")) {
|
||||
// Seen on
|
||||
// [Mesa],
|
||||
// [llvmpipe (LLVM 17.0.6, 256 bits)],
|
||||
@@ -578,6 +574,8 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
// [4.50]
|
||||
// not known which version are affected
|
||||
bugs->rebind_buffer_after_deletion = true;
|
||||
} else if (strstr(renderer, "Mozilla")) {
|
||||
bugs->disable_invalidate_framebuffer = true;
|
||||
}
|
||||
} else {
|
||||
// When running under ANGLE, it's a different set of workaround that we need.
|
||||
@@ -589,19 +587,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
}
|
||||
}
|
||||
|
||||
if (strstr(vendor, "Mozilla")) {
|
||||
// Seen on
|
||||
// [Mozilla],
|
||||
// [GeForce GTX 980, or similar]
|
||||
// or [ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar]
|
||||
// or anything else,
|
||||
// [OpenGL ES 3.0 (WebGL 2.0)],
|
||||
// [OpenGL ES GLSL ES 3.00 (WebGL GLSL ES 3.00)]
|
||||
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
|
||||
// ANGLE or not. (b/376125497)
|
||||
bugs->rebind_buffer_after_deletion = true;
|
||||
}
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x
|
||||
if (UTILS_UNLIKELY(major == 2)) {
|
||||
|
||||
@@ -320,8 +320,8 @@ public:
|
||||
// a glFinish. So we must delay the destruction until we know the GPU is finished.
|
||||
bool delay_fbo_destruction;
|
||||
|
||||
// Mesa and Mozilla(web) sometimes clear the generic buffer binding when *another* buffer
|
||||
// is destroyed, if that other buffer is bound on an *indexed* buffer binding.
|
||||
// Mesa sometimes clears the generic buffer binding when *another* buffer is destroyed,
|
||||
// if that other buffer is bound on an *indexed* buffer binding.
|
||||
bool rebind_buffer_after_deletion;
|
||||
|
||||
// Force feature level 0. Typically used for low end ES3 devices with significant driver
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include "VulkanBlitter.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanBuffer;
|
||||
class VulkanFboCache;
|
||||
class VulkanPipelineCache;
|
||||
class VulkanSamplerCache;
|
||||
|
||||
struct VulkanProgram;
|
||||
|
||||
class VulkanBlitter {
|
||||
public:
|
||||
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
|
||||
|
||||
@@ -56,9 +56,9 @@ VkImageView VulkanAttachment::getImageView() {
|
||||
assert_invariant(texture);
|
||||
VkImageSubresourceRange range = getSubresourceRange();
|
||||
if (range.layerCount > 1) {
|
||||
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY);
|
||||
return texture->getMultiviewAttachmentView(range);
|
||||
}
|
||||
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D);
|
||||
return texture->getAttachmentView(range);
|
||||
}
|
||||
|
||||
bool VulkanAttachment::isDepth() const {
|
||||
@@ -68,11 +68,11 @@ bool VulkanAttachment::isDepth() const {
|
||||
VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
assert_invariant(texture);
|
||||
return {
|
||||
.aspectMask = texture->getImageAspect(),
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
.aspectMask = texture->getImageAspect(),
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
|
||||
@@ -29,6 +30,8 @@
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator)
|
||||
VK_DEFINE_HANDLE(VmaPool)
|
||||
|
||||
|
||||
@@ -260,6 +260,7 @@ VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
|
||||
|
||||
void VulkanDescriptorSetCache::terminate() noexcept{
|
||||
mDescriptorPool.reset();
|
||||
clearHistory();
|
||||
}
|
||||
|
||||
// bind() is not really binding the set but just stashing until we have all the info
|
||||
@@ -329,6 +330,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
||||
}
|
||||
VkWriteDescriptorSet const descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = set->vkSet,
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
@@ -344,19 +346,20 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
|
||||
VkSampler sampler) noexcept {
|
||||
VkDescriptorImageInfo info{
|
||||
.sampler = sampler,
|
||||
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||
};
|
||||
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
||||
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
|
||||
VkImageViewType const expectedType = texture->getViewType();
|
||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
|
||||
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
|
||||
// an attachment, then the range for this view has exactly one level and one layer.
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
// an attachment, then the sampler for this texture has the same view properties as a
|
||||
// view for an attachment. Therefore, we can use getAttachmentView to get a
|
||||
// corresponding VkImageView.
|
||||
info.imageView = texture->getAttachmentView(range);
|
||||
} else {
|
||||
info.imageView = texture->getViewForType(range, expectedType);
|
||||
}
|
||||
info.imageView = texture->getView(range);
|
||||
|
||||
info.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout());
|
||||
VkWriteDescriptorSet const descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
@@ -392,7 +395,7 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
|
||||
});
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::gc() {
|
||||
void VulkanDescriptorSetCache::clearHistory() {
|
||||
mStashedSets = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void gc();
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
class DescriptorInfinitePool;
|
||||
|
||||
@@ -25,12 +25,10 @@
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/VulkanSamplerCache.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
@@ -214,7 +212,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mPipelineCache(mPlatform->getDevice()),
|
||||
mStagePool(mAllocator, &mCommands),
|
||||
mFramebufferCache(mPlatform->getDevice()),
|
||||
mYcbcrConversionCache(mPlatform->getDevice()),
|
||||
mSamplerCache(mPlatform->getDevice()),
|
||||
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
@@ -366,7 +363,7 @@ void VulkanDriver::collectGarbage() {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
// Command buffers need to be submitted and completed before other resources can be gc'd.
|
||||
mCommands.gc();
|
||||
mDescriptorSetCache.gc();
|
||||
mDescriptorSetCache.clearHistory();
|
||||
mStagePool.gc();
|
||||
mFramebufferCache.gc();
|
||||
mPipelineCache.gc();
|
||||
@@ -421,11 +418,7 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
|
||||
// TODO: YcbcrConversion?
|
||||
VulkanSamplerCache::Params cacheParams = {
|
||||
.sampler = params,
|
||||
};
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(params);
|
||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||
}
|
||||
|
||||
@@ -563,7 +556,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
if (metadata.isProtected) {
|
||||
usage |= backend::TextureUsage::PROTECTED;
|
||||
}
|
||||
@@ -573,7 +566,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||
if (any(usage & (TextureUsage::BLIT_DST & TextureUsage::UPLOADABLE))) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
@@ -589,15 +582,13 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||
<< "failed to find a valid memory type for external image memory.";
|
||||
|
||||
VkImage vkimg;
|
||||
VkDeviceMemory deviceMemory;
|
||||
std::tie(vkimg, deviceMemory) =
|
||||
const auto& data =
|
||||
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
||||
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
|
||||
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
|
||||
metadata.layerCount, usage, mStagePool);
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
||||
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
|
||||
metadata.samples, metadata.width, metadata.height, metadata.layerCount, usage,
|
||||
mStagePool);
|
||||
|
||||
texture.inc();
|
||||
}
|
||||
@@ -606,14 +597,13 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
|
||||
// not supported in this backend
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
}
|
||||
|
||||
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
@@ -621,7 +611,6 @@ void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {
|
||||
// not supported in this backend
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
@@ -1145,28 +1134,24 @@ math::float2 VulkanDriver::getClipSpaceParams() {
|
||||
}
|
||||
|
||||
uint8_t VulkanDriver::getMaxDrawBuffers() {
|
||||
return mContext.getPhysicalDeviceLimits().maxColorAttachments;
|
||||
return MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; // TODO: query real value
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxUniformBufferSize() {
|
||||
return mContext.getPhysicalDeviceLimits().maxUniformBufferRange;
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
// TODO: devices that return less than 32768 should be rejected. This represents only 3%
|
||||
// of android devices.
|
||||
return 32768;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxTextureSize(SamplerType type) {
|
||||
switch (type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimension2D;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimension3D;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimensionCube;
|
||||
default:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimension1D;
|
||||
}
|
||||
size_t VulkanDriver::getMaxTextureSize(SamplerType) {
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
return 4096;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxArrayTextureLayers() {
|
||||
return mContext.getPhysicalDeviceLimits().maxImageArrayLayers;
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
return 256;
|
||||
}
|
||||
|
||||
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanQueryManager.h"
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||
@@ -136,7 +135,6 @@ private:
|
||||
VulkanPipelineCache mPipelineCache;
|
||||
VulkanStagePool mStagePool;
|
||||
VulkanFboCache mFramebufferCache;
|
||||
VulkanYcbcrConversionCache mYcbcrConversionCache;
|
||||
VulkanSamplerCache mSamplerCache;
|
||||
VulkanBlitter mBlitter;
|
||||
VulkanReadPixels mReadPixels;
|
||||
|
||||
@@ -18,18 +18,24 @@
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include "backend/Program.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <list>
|
||||
#include <tsl/robin_map.h>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanSamplerCache.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/VulkanSamplerCache.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
@@ -28,40 +26,41 @@ namespace filament::backend {
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
|
||||
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
auto iter = mCache.find(params);
|
||||
if (UTILS_LIKELY(iter != mCache.end())) {
|
||||
return iter->second;
|
||||
}
|
||||
auto const& samplerParams = params.sampler;
|
||||
VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
|
||||
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(samplerParams.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(samplerParams.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(samplerParams.wrapR),
|
||||
.anisotropyEnable = samplerParams.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float) (1u << samplerParams.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(samplerParams.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(samplerParams.compareFunc),
|
||||
VkSamplerCreateInfo samplerInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = fvkutils::getMaxLod(samplerParams.filterMin),
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE };
|
||||
.unnormalizedCoordinates = VK_FALSE
|
||||
};
|
||||
VkSampler sampler;
|
||||
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
mCache.insert({ params, sampler });
|
||||
mCache.insert({params, sampler});
|
||||
return sampler;
|
||||
}
|
||||
|
||||
void VulkanSamplerCache::terminate() noexcept {
|
||||
for (auto pair: mCache) {
|
||||
for (auto pair : mCache) {
|
||||
vkDestroySampler(mDevice, pair.second, VKALLOC);
|
||||
}
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include "VulkanContext.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -29,28 +26,12 @@ namespace filament::backend {
|
||||
// Simple manager for VkSampler objects.
|
||||
class VulkanSamplerCache {
|
||||
public:
|
||||
struct Params {
|
||||
SamplerParams sampler = {};
|
||||
uint32_t padding = 0;
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Params) == 16);
|
||||
|
||||
explicit VulkanSamplerCache(VkDevice device);
|
||||
VkSampler getSampler(Params params) noexcept;
|
||||
VkSampler getSampler(SamplerParams params) noexcept;
|
||||
void terminate() noexcept;
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
|
||||
struct SamplerEqualTo {
|
||||
bool operator()(Params lhs, Params rhs) const noexcept {
|
||||
SamplerParams::EqualTo equal;
|
||||
return equal(lhs.sampler, rhs.sampler) && lhs.conversion == rhs.conversion;
|
||||
}
|
||||
};
|
||||
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
|
||||
tsl::robin_map<SamplerParams, VkSampler, SamplerParams::Hasher, SamplerParams::EqualTo> mCache;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -32,7 +32,6 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
|
||||
VkExtent2D extent)
|
||||
: mPlatform(platform),
|
||||
mContext(context),
|
||||
mResourceManager(resourceManager),
|
||||
mCommands(commands),
|
||||
mAllocator(allocator),
|
||||
@@ -78,16 +77,16 @@ void VulkanSwapChain::update() {
|
||||
}
|
||||
for (auto const color: bundle.colors) {
|
||||
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
|
||||
mContext, device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
|
||||
bundle.colorFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
|
||||
bundle.extent.height, bundle.layerCount, colorUsage, mStagePool);
|
||||
device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
|
||||
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, colorUsage,
|
||||
mStagePool);
|
||||
mColors.push_back(colorTexture);
|
||||
}
|
||||
|
||||
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, mContext, device,
|
||||
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
|
||||
bundle.depthFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
|
||||
bundle.extent.height, bundle.layerCount, depthUsage, mStagePool);
|
||||
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device,
|
||||
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
|
||||
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, depthUsage,
|
||||
mStagePool);
|
||||
|
||||
mExtent = bundle.extent;
|
||||
mLayerCount = bundle.layerCount;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
@@ -29,6 +28,7 @@
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -83,7 +83,6 @@ private:
|
||||
void update();
|
||||
|
||||
VulkanPlatform* mPlatform;
|
||||
VulkanContext const& mContext;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
VulkanCommands* mCommands;
|
||||
VmaAllocator mAllocator;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
@@ -55,6 +54,7 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A,
|
||||
};
|
||||
|
||||
auto const compose = [](VkComponentMapping const& prev,
|
||||
VkComponentMapping const& next) -> VkComponentMapping {
|
||||
VkComponentSwizzle vals[4] = { next.r, next.g, next.b, next.a };
|
||||
@@ -151,152 +151,35 @@ uint8_t getLayerCountFromDepth(uint32_t const depth) {
|
||||
return getLayerCount(getSamplerTypeFromDepth(depth), depth);
|
||||
}
|
||||
|
||||
VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
|
||||
VkPhysicalDevice physicalDevice, VkFormat vkFormat, TextureUsage tusage) {
|
||||
VkImageUsageFlags usage = {};
|
||||
if (any(tusage & TextureUsage::BLIT_SRC)) {
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
if (any(tusage & TextureUsage::BLIT_DST)) {
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
// Determine if we can use the transient usage flag combined with lazily allocated memory.
|
||||
const bool useTransientAttachment =
|
||||
// Lazily allocated memory is available.
|
||||
context.isLazilyAllocatedMemorySupported() &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(tusage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
|
||||
const VkImageUsageFlags transientFlag =
|
||||
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
|
||||
|
||||
if (any(tusage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
if (physicalDevice != VK_NULL_HANDLE) {
|
||||
// Validate that the format is actually sampleable.
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (any(tusage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
|
||||
if (any(tusage & TextureUsage::SUBPASS_INPUT)) {
|
||||
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
}
|
||||
if (any(tusage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
}
|
||||
if (any(tusage & TextureUsage::UPLOADABLE)) {
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
if (any(tusage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
|
||||
// Depth resolves uses a custom shader and therefore needs to be sampleable.
|
||||
if (samples > 1) {
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands,
|
||||
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory,
|
||||
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
|
||||
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat, VkImageUsageFlags usage,
|
||||
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
|
||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
|
||||
bool isProtected)
|
||||
: mStagePool(stagePool),
|
||||
mCommands(commands),
|
||||
mAllocator(allocator),
|
||||
mDevice(device),
|
||||
mTextureImage(image),
|
||||
mTextureImageMemory(deviceMemory),
|
||||
mVkFormat(format),
|
||||
: mVkFormat(format),
|
||||
mViewType(viewType),
|
||||
mFullViewRange{ fvkutils::getImageAspect(format), 0, levels, 0, layerCount },
|
||||
mYcbcr{ ycbcrConversion, isExternalFormat },
|
||||
mDefaultLayout(getDefaultLayoutImpl(usage)),
|
||||
mUsage(usage),
|
||||
mIsProtected(isProtected) {}
|
||||
mFullViewRange{fvkutils::getImageAspect(format), 0, levels, 0, layerCount},
|
||||
mDefaultLayout(defaultLayout),
|
||||
mIsProtected(isProtected),
|
||||
mStagePool(stagePool),
|
||||
mDevice(device),
|
||||
mAllocator(allocator),
|
||||
mCommands(commands),
|
||||
mIsTransientAttachment(false) {}
|
||||
|
||||
VulkanTextureState::~VulkanTextureState() {
|
||||
if (mTextureImageMemory != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
|
||||
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
|
||||
}
|
||||
clearCachedImageViews();
|
||||
}
|
||||
|
||||
void VulkanTextureState::clearCachedImageViews() noexcept {
|
||||
for (auto entry: mCachedImageViews) {
|
||||
vkDestroyImageView(mDevice, entry.second, VKALLOC);
|
||||
}
|
||||
mCachedImageViews.clear();
|
||||
}
|
||||
|
||||
VkImageView VulkanTextureState::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle) {
|
||||
ImageViewKey const key{ range, viewType, swizzle };
|
||||
if (auto iter = mCachedImageViews.find(key); iter != mCachedImageViews.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversionInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.conversion = mYcbcr.conversion,
|
||||
};
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = mYcbcr.conversion != VK_NULL_HANDLE ? &conversionInfo : nullptr,
|
||||
.flags = 0,
|
||||
.image = mTextureImage,
|
||||
.viewType = viewType,
|
||||
.format = mYcbcr.isExternalFormat ? VK_FORMAT_UNDEFINED : mVkFormat,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
vkCreateImageView(mDevice, &viewInfo, VKALLOC, &imageView);
|
||||
mCachedImageViews.emplace(key, imageView);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
// Constructor for internally passed VkImage - including swapchain images and external images.
|
||||
VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
|
||||
// Constructor for internally passed VkImage
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage tusage,
|
||||
VulkanStagePool& stagePool)
|
||||
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth,
|
||||
TextureFormat::UNUSED, tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
|
||||
commands, allocator, device, image, memory, format,
|
||||
fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
||||
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion,
|
||||
/*isExternalFormat=*/false,
|
||||
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
|
||||
any(usage & TextureUsage::PROTECTED))) {
|
||||
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width,
|
||||
uint32_t height, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
|
||||
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, TextureFormat::UNUSED,
|
||||
tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
||||
allocator, commands, stagePool, format, fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
||||
/*mipLevels=*/1, getLayerCountFromDepth(depth), getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
|
||||
mState->mTextureImage = image;
|
||||
mState->mTextureImageMemory = memory;
|
||||
mPrimaryViewRange = mState->mFullViewRange;
|
||||
}
|
||||
|
||||
@@ -306,19 +189,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
|
||||
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
|
||||
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage) {
|
||||
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
||||
allocator, commands, stagePool, fvkutils::getVkFormat(tformat),
|
||||
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
|
||||
VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
|
||||
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
|
||||
VkFormat const vkFormat = fvkutils::getVkFormat(tformat);
|
||||
bool const isProtected = any(tusage & TextureUsage::PROTECTED);
|
||||
VkImageCreateInfo imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
|
||||
.format = vkFormat,
|
||||
.format = mState->mVkFormat,
|
||||
.extent = {w, h, depth},
|
||||
.mipLevels = levels,
|
||||
.arrayLayers = 1,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = getUsage(context, samples, physicalDevice, vkFormat, tusage),
|
||||
.usage = 0,
|
||||
};
|
||||
if (target == SamplerType::SAMPLER_3D && any(tusage & TextureUsage::ALL_ATTACHMENTS)) {
|
||||
if (context.isImageView2DOn3DImageSupported()) {
|
||||
@@ -348,17 +233,79 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
imageInfo.arrayLayers = depth * 6;
|
||||
imageInfo.extent.depth = 1;
|
||||
}
|
||||
if (isProtected) {
|
||||
if (any(usage & TextureUsage::PROTECTED)) {
|
||||
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::BLIT_DST)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
// Determine if we can use the transient usage flag combined with lazily allocated memory.
|
||||
const bool useTransientAttachment =
|
||||
// Lazily allocated memory is available.
|
||||
context.isLazilyAllocatedMemorySupported() &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(usage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
|
||||
mState->mIsTransientAttachment = useTransientAttachment;
|
||||
|
||||
const VkImageUsageFlags transientFlag =
|
||||
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
|
||||
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
// Validate that the format is actually sampleable.
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, mState->mVkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
|
||||
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
}
|
||||
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
}
|
||||
if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
|
||||
// Depth resolves uses a custom shader and therefore needs to be sampleable.
|
||||
if (samples > 1) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Constrain the sample count according to the sample count masks in VkPhysicalDeviceProperties.
|
||||
// Note that VulkanRenderTarget holds a single MSAA count, so we play it safe if this is used as
|
||||
// any kind of attachment (color or depth).
|
||||
auto const& limits = context.getPhysicalDeviceLimits();
|
||||
const auto& limits = context.getPhysicalDeviceLimits();
|
||||
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
samples = fvkutils::reduceSampleCount(samples,
|
||||
fvkutils::isVkDepthFormat(vkFormat)
|
||||
fvkutils::isVkDepthFormat(mState->mVkFormat)
|
||||
? limits.sampledImageDepthSampleCounts
|
||||
: limits.sampledImageColorSampleCounts);
|
||||
}
|
||||
@@ -372,36 +319,33 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
this->samples = samples;
|
||||
imageInfo.samples = (VkSampleCountFlagBits) samples;
|
||||
|
||||
VkImage textureImage;
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &textureImage);
|
||||
VkResult result = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
|
||||
if (result != VK_SUCCESS || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
|
||||
FVK_LOGD << "vkCreateImage: "
|
||||
<< "image = " << textureImage << ", "
|
||||
<< "image = " << mState->mTextureImage << ", "
|
||||
<< "result = " << result << ", "
|
||||
<< "handle = " << utils::io::hex << textureImage << utils::io::dec << ", "
|
||||
<< "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
|
||||
<< "extent = " << w << "x" << h << "x"<< depth << ", "
|
||||
<< "mipLevels = " << int(levels) << ", "
|
||||
<< "TextureUsage = " << static_cast<int>(tusage) << ", "
|
||||
<< "TextureUsage = " << static_cast<int>(usage) << ", "
|
||||
<< "usage = " << imageInfo.usage << ", "
|
||||
<< "samples = " << imageInfo.samples << ", "
|
||||
<< "type = " << imageInfo.imageType << ", "
|
||||
<< "flags = " << imageInfo.flags << ", "
|
||||
<< "target = " << static_cast<int>(target) <<", "
|
||||
<< "format = " << vkFormat << utils::io::endl;
|
||||
<< "format = " << mState->mVkFormat << utils::io::endl;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Allocate memory for the VkImage and bind it.
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device, textureImage, &memReqs);
|
||||
VkMemoryRequirements memReqs = {};
|
||||
vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
|
||||
|
||||
bool const useTransientAttachment = imageInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||
|
||||
VkFlags const requiredMemoryFlags =
|
||||
const VkFlags requiredMemoryFlags =
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
|
||||
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
@@ -413,22 +357,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
VkDeviceMemory textureImageMemory;
|
||||
result = vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory);
|
||||
result = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
result = vkBindImageMemory(device, textureImage, textureImageMemory, 0);
|
||||
result = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
|
||||
0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
mState = fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
|
||||
commands, allocator, device, textureImage, textureImageMemory, vkFormat,
|
||||
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
|
||||
VK_NULL_HANDLE /* ycbcrConversion */, false /*isExternalFormat*/, imageInfo.usage,
|
||||
isProtected);
|
||||
|
||||
// Spec out the "primary" VkImageView that shaders use to sample from the image.
|
||||
mPrimaryViewRange = mState->mFullViewRange;
|
||||
|
||||
// Go ahead and create the primary image view.
|
||||
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
|
||||
|
||||
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
||||
}
|
||||
|
||||
// Constructor for creating a texture view
|
||||
@@ -437,8 +380,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
|
||||
uint8_t levelCount)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
mState(src->mState) {
|
||||
src->format, src->usage) {
|
||||
mState = src->mState;
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
|
||||
mPrimaryViewRange.levelCount = levelCount;
|
||||
@@ -449,10 +392,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
mState(src->mState),
|
||||
mPrimaryViewRange(src->mPrimaryViewRange),
|
||||
mSwizzle(composeSwizzle(src->mSwizzle, swizzle)) {}
|
||||
src->format, src->usage) {
|
||||
mState = src->mState;
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
|
||||
}
|
||||
|
||||
VulkanTextureState::~VulkanTextureState() {
|
||||
if (mTextureImageMemory != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
|
||||
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
|
||||
}
|
||||
for (auto entry: mCachedImageViews) {
|
||||
vkDestroyImageView(mDevice, entry.second, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
|
||||
@@ -559,20 +513,20 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
|
||||
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
|
||||
|
||||
// TODO: support blit-based format conversion for 3D images and cubemaps.
|
||||
constexpr int layer = 0;
|
||||
const int layer = 0;
|
||||
|
||||
VkOffset3D const rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
||||
const VkOffset3D rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
||||
|
||||
VkImageAspectFlags const aspect = getImageAspect();
|
||||
const VkImageAspectFlags aspect = getImageAspect();
|
||||
|
||||
VkImageBlit const blitRegions[1] = {{
|
||||
const VkImageBlit blitRegions[1] = {{
|
||||
.srcSubresource = { aspect, 0, 0, 1 },
|
||||
.srcOffsets = { rect[0], rect[1] },
|
||||
.dstSubresource = { aspect, uint32_t(miplevel), layer, 1 },
|
||||
.dstOffsets = { rect[0], rect[1] }
|
||||
}};
|
||||
|
||||
VkImageSubresourceRange const range = { aspect, miplevel, 1, layer, 1 };
|
||||
const VkImageSubresourceRange range = { aspect, miplevel, 1, layer, 1 };
|
||||
|
||||
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
|
||||
VulkanLayout const oldLayout = getLayout(layer, miplevel);
|
||||
@@ -588,21 +542,41 @@ VulkanLayout VulkanTexture::getDefaultLayout() const {
|
||||
return mState->mDefaultLayout;
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
assert_invariant(mState->mYcbcr.conversion == VK_NULL_HANDLE &&
|
||||
"We are not yet supporting external image as attachments.");
|
||||
if (type == VK_IMAGE_VIEW_TYPE_2D) {
|
||||
VkImageSubresourceRange copy = range;
|
||||
copy.levelCount = 1;
|
||||
copy.layerCount = 1;
|
||||
return mState->getImageView(copy, type, {});
|
||||
} else {
|
||||
return mState->getImageView(range, type, {});
|
||||
}
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getView(VkImageSubresourceRange const& range) {
|
||||
return mState->getImageView(range, mState->mViewType, mSwizzle);
|
||||
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
return getImageView(range, type, mSwizzle);
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle) {
|
||||
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
|
||||
auto iter = mState->mCachedImageViews.find(key);
|
||||
if (iter != mState->mCachedImageViews.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = mState->mTextureImage,
|
||||
.viewType = viewType,
|
||||
.format = mState->mVkFormat,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
|
||||
mState->mCachedImageViews.emplace(key, imageView);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkImageAspectFlags VulkanTexture::getImageAspect() const {
|
||||
@@ -764,21 +738,6 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternalFormat) {
|
||||
// Note that this comparison is valid because we only ever create VkSamplerYcbcrConversion from
|
||||
// a cache. So for each set of parameters, there is exactly one conversion (similar to
|
||||
// samplers).
|
||||
VulkanTextureState::Ycbcr ycbcr = {
|
||||
.conversion = conversion,
|
||||
.isExternalFormat = isExternalFormat,
|
||||
};
|
||||
|
||||
if (mState->mYcbcr != ycbcr) {
|
||||
mState->mYcbcr = ycbcr;
|
||||
mState->clearCachedImageViews();
|
||||
}
|
||||
}
|
||||
|
||||
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
|
||||
assert_invariant(level <= 0xffff && layer <= 0xffff);
|
||||
const uint32_t key = (layer << 16) | level;
|
||||
|
||||
@@ -19,10 +19,7 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanBuffer.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
@@ -34,14 +31,10 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanTexture;
|
||||
|
||||
struct VulkanTextureState : public fvkmemory::Resource {
|
||||
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
|
||||
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
|
||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
|
||||
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat,
|
||||
VkImageUsageFlags usage, bool isProtected);
|
||||
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
||||
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
|
||||
|
||||
~VulkanTextureState();
|
||||
|
||||
@@ -64,50 +57,29 @@ struct VulkanTextureState : public fvkmemory::Resource {
|
||||
// No implicit padding allowed due to it being a hash key.
|
||||
static_assert(sizeof(ImageViewKey) == 40);
|
||||
|
||||
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle);
|
||||
private:
|
||||
void clearCachedImageViews() noexcept;
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanCommands* const mCommands;
|
||||
VmaAllocator const mAllocator;
|
||||
VkDevice const mDevice;
|
||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
||||
|
||||
// The texture with the sidecar owns the sidecar.
|
||||
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
|
||||
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkImage const mTextureImage;
|
||||
VkDeviceMemory const mTextureImageMemory;
|
||||
VkFormat const mVkFormat;
|
||||
VkImageViewType const mViewType;
|
||||
VkImageSubresourceRange const mFullViewRange;
|
||||
VkImage mTextureImage = VK_NULL_HANDLE;
|
||||
VulkanLayout mDefaultLayout;
|
||||
|
||||
// Note that this parameter is not constant due to the fact that AHB can force a change in the
|
||||
// conversion matrix per-frame.
|
||||
struct Ycbcr {
|
||||
VkSamplerYcbcrConversion conversion;
|
||||
bool isExternalFormat;
|
||||
|
||||
bool operator==(Ycbcr const& other) const {
|
||||
return conversion == other.conversion && isExternalFormat == other.isExternalFormat;
|
||||
}
|
||||
|
||||
bool operator!=(Ycbcr const& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
} mYcbcr;
|
||||
|
||||
VulkanLayout const mDefaultLayout;
|
||||
VkImageUsageFlags const mUsage;
|
||||
bool const mIsProtected;
|
||||
bool mIsProtected = false;
|
||||
|
||||
// Track the image layout of each subresource using a sparse range map.
|
||||
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
|
||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
||||
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
|
||||
|
||||
friend struct VulkanTexture;
|
||||
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
|
||||
VulkanStagePool& mStagePool;
|
||||
VkDevice mDevice;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanCommands* mCommands;
|
||||
bool mIsTransientAttachment;
|
||||
};
|
||||
|
||||
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
@@ -119,10 +91,10 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
VulkanStagePool& stagePool);
|
||||
|
||||
// Specialized constructor for internally created textures (e.g. from a swap chain)
|
||||
VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
|
||||
// The texture will never destroy the given VkImage, but it does manages its subresources.
|
||||
VulkanTexture(VkDevice device, VmaAllocator allocator,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage tusage, VulkanStagePool& stagePool);
|
||||
|
||||
// Constructor for creating a texture view for wrt specific mip range
|
||||
@@ -141,6 +113,11 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel);
|
||||
|
||||
// Returns the primary image view, which is used for shader sampling.
|
||||
VkImageView getPrimaryImageView() {
|
||||
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
|
||||
}
|
||||
|
||||
VkImageViewType getViewType() const {
|
||||
return mState->mViewType;
|
||||
}
|
||||
@@ -155,11 +132,20 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
// time of the call.
|
||||
VulkanLayout getDefaultLayout() const;
|
||||
|
||||
// Gets or creates a cached VkImageView for a subresource that can be used as a render
|
||||
// target attachment. Unlike the primary image view, this always the identity swizzle.
|
||||
VkImageView getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type);
|
||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
||||
// target attachment. Unlike the primary image view, this always has type VK_IMAGE_VIEW_TYPE_2D
|
||||
// and the identity swizzle.
|
||||
VkImageView getAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
VkImageView getView(VkImageSubresourceRange const& range);
|
||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
||||
// target attachment when rendering with multiview.
|
||||
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
// This is a workaround for the first few frames where we're waiting for the texture to actually
|
||||
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
|
||||
// imageView needs to be of the right type. Hence, we provide an option to indicate the
|
||||
// view type. Swizzle option does not matter in this case.
|
||||
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
|
||||
|
||||
VkFormat getVkFormat() const {
|
||||
return mState->mVkFormat;
|
||||
@@ -179,7 +165,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
}
|
||||
|
||||
bool isTransientAttachment() const {
|
||||
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||
return mState->mIsTransientAttachment;
|
||||
}
|
||||
|
||||
bool getIsProtected() const {
|
||||
@@ -206,11 +192,6 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
// manually (outside of calls to transitionLayout).
|
||||
void setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout);
|
||||
|
||||
// This is used in the case of external images and external samplers. AHB might update the
|
||||
// conversion per-frame. This implies that we need to invalidate the view cache when that
|
||||
// happens.
|
||||
void setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternal);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
void print() const;
|
||||
#endif
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <vulkan/vulkan_android.h> // for VkExternalFormatANDROID
|
||||
#endif
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
VulkanYcbcrConversionCache::Params params) noexcept {
|
||||
auto iter = mCache.find(params);
|
||||
if (UTILS_LIKELY(iter != mCache.end())) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
auto const& chroma = params.conversion;
|
||||
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.externalFormat = params.externalFormat,
|
||||
};
|
||||
if (params.externalFormat) {
|
||||
conversionInfo.pNext = &externalFormat;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkSamplerYcbcrConversion conversion;
|
||||
VkResult result =
|
||||
vkCreateSamplerYcbcrConversion(mDevice, &conversionInfo, nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
mCache.insert({ params, conversion });
|
||||
return conversion;
|
||||
}
|
||||
|
||||
void VulkanYcbcrConversionCache::terminate() noexcept {
|
||||
for (auto& [param, conv]: mCache) {
|
||||
vkDestroySamplerYcbcrConversion(mDevice, conv, VKALLOC);
|
||||
}
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Simple manager for VkSamplerYcbcrConversion objects.
|
||||
class VulkanYcbcrConversionCache {
|
||||
public:
|
||||
struct Params {
|
||||
SamplerYcbcrConversion conversion = {};
|
||||
uint32_t padding = 0;
|
||||
uint64_t externalFormat = 0;
|
||||
};
|
||||
static_assert(sizeof(Params) == 16);
|
||||
|
||||
explicit VulkanYcbcrConversionCache(VkDevice device);
|
||||
VkSamplerYcbcrConversion getConversion(Params params) noexcept;
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
|
||||
struct ConversionEqualTo {
|
||||
bool operator()(Params lhs, Params rhs) const noexcept {
|
||||
SamplerYcbcrConversion::EqualTo equal;
|
||||
return equal(lhs.conversion, rhs.conversion) &&
|
||||
lhs.externalFormat == rhs.externalFormat;
|
||||
}
|
||||
};
|
||||
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
@@ -295,8 +295,9 @@ VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
@@ -321,6 +322,7 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, Sampler
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
@@ -348,6 +350,7 @@ VkSampler VulkanPlatform::createExternalSamplerImpl(
|
||||
uint32_t internalFormat) {
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSWAPCHAIN_IMPL_H
|
||||
|
||||
#include "vulkan/VulkanContext.h"
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
@@ -36,7 +35,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -118,6 +116,7 @@ void printLimits(wgpu::Limits const& limits) {
|
||||
printLimit("maxBufferSize", limits.maxBufferSize);
|
||||
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
||||
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
||||
printLimit("maxInterStageShaderComponents", limits.maxInterStageShaderComponents);
|
||||
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
||||
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
||||
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
||||
@@ -159,6 +158,8 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
|
||||
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
|
||||
FWGPU_LOGI << " compatibility mode: " << bool(adapterInfo.compatibilityMode)
|
||||
<< utils::io::endl;
|
||||
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
|
||||
}
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
@@ -174,17 +175,55 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::Limits supportedLimits{};
|
||||
wgpu::SupportedLimits supportedLimits{};
|
||||
if (!adapter.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits);
|
||||
printLimits(supportedLimits.limits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||
std::stringstream usages_stream{};
|
||||
usages_stream << capabilities.usages;
|
||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usages: " << usages_stream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||
[](wgpu::TextureFormat const format) {
|
||||
std::stringstream format_stream{};
|
||||
format_stream << format;
|
||||
FWGPU_LOGI << " " << format_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
|
||||
std::for_each(capabilities.presentModes,
|
||||
capabilities.presentModes + capabilities.presentModeCount,
|
||||
[](wgpu::PresentMode const presentMode) {
|
||||
std::stringstream present_mode_stream{};
|
||||
present_mode_stream << presentMode;
|
||||
FWGPU_LOGI << " " << present_mode_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
|
||||
std::for_each(capabilities.alphaModes,
|
||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||
std::stringstream alpha_mode_stream{};
|
||||
alpha_mode_stream << alphaMode;
|
||||
FWGPU_LOGI << " " << alpha_mode_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printDeviceDetails(wgpu::Device const& device) {
|
||||
@@ -201,31 +240,24 @@ void printDeviceDetails(wgpu::Device const& device) {
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::Limits supportedLimits{};
|
||||
wgpu::SupportedLimits supportedLimits{};
|
||||
if (!device.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits);
|
||||
printLimits(supportedLimits.limits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace
|
||||
|
||||
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||
Platform::DriverConfig validConfig {driverConfig};
|
||||
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
|
||||
return new WebGPUDriver(platform, validConfig);
|
||||
Driver* WebGPUDriver::create(WebGPUPlatform& platform) noexcept {
|
||||
return new WebGPUDriver(platform);
|
||||
}
|
||||
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags) {
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform) noexcept
|
||||
: mPlatform(platform) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printInstanceDetails(mPlatform.getInstance());
|
||||
#endif
|
||||
@@ -257,14 +289,10 @@ void WebGPUDriver::terminate() {
|
||||
}
|
||||
|
||||
void WebGPUDriver::tick(int) {
|
||||
mDevice.Tick();
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor{};
|
||||
commandEncoderDescriptor.nextInChain = nullptr;
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
}
|
||||
|
||||
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
@@ -281,12 +309,6 @@ void WebGPUDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::endFrame(uint32_t frameId) {
|
||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
||||
mQueue.Submit(1, &mCommandBuffer);
|
||||
mCommandEncoder = nullptr;
|
||||
mCommandBuffer = nullptr;
|
||||
mTextureView = nullptr;
|
||||
mSwapChain->Present();
|
||||
}
|
||||
|
||||
void WebGPUDriver::flush(int) {
|
||||
@@ -320,13 +342,6 @@ void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
||||
mSwapChain = nullptr;
|
||||
// TODO: use webgpu handle allocator from
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// if (sch) {
|
||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
||||
// destruct(sch, hwSwapChain);
|
||||
// }
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
|
||||
@@ -342,9 +357,6 @@ void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// return allocAndConstructHandle<HwSwapChain>();
|
||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
@@ -353,7 +365,7 @@ Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
@@ -373,23 +385,23 @@ Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
return allocHandle<HwIndexBuffer>();
|
||||
return Handle<HwIndexBuffer>((Handle<HwIndexBuffer>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
return allocHandle<WGPUBufferObject>();
|
||||
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||
return allocHandle<WGPURenderTarget>();
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
||||
return allocHandle<WGPUVertexBuffer>();
|
||||
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||
@@ -401,15 +413,15 @@ Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
return allocHandle<WGPUVertexBufferInfo>();
|
||||
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return allocHandle<WGPUTexture>();
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
return allocHandle<WGPURenderTarget>();
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
||||
@@ -430,43 +442,25 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
||||
assert_invariant(!mSwapChain);
|
||||
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||
mAdapter = mPlatform.requestAdapter(surface);
|
||||
mSurface= mPlatform.createSurface(nativeWindow, flags);
|
||||
mAdapter = mPlatform.requestAdapter(mSurface);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(mAdapter);
|
||||
wgpu::SurfaceCapabilities surfaceCapabilities{};
|
||||
if (!mSurface.GetCapabilities(mAdapter, &surfaceCapabilities)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||
} else {
|
||||
printSurfaceCapabilitiesDetails(surfaceCapabilities);
|
||||
}
|
||||
#endif
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(mDevice);
|
||||
#endif
|
||||
mQueue = mDevice.GetQueue();
|
||||
mSwapChain = std::make_unique<WebGPUSwapChain>(std::move(surface), mAdapter, mDevice, flags);
|
||||
// TODO configure the surface (maybe before or after creating the swapchain?
|
||||
// how do we get the surface extent?)
|
||||
// TODO actually create the swapchain
|
||||
// auto onQueueWorkDone = [](wgpu::QueueWorkDoneStatus status, void* /* pUserData */) {
|
||||
// FWGPU_LOGW << "Queued work finished with status: " << status << std::endl;
|
||||
// };
|
||||
// mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus status, void* pUserdata) {
|
||||
// FWGPU_LOGW << "Queued work finished with status:\n";
|
||||
// }, nullptr /* pUserData */);
|
||||
void* userDataPtr = nullptr;
|
||||
mQueue.OnSubmittedWorkDone(
|
||||
wgpu::CallbackMode::AllowProcessEvents,
|
||||
[](wgpu::QueueWorkDoneStatus status, void* pUserData) {
|
||||
FWGPU_LOGW << "Queued work finished with status: " << static_cast<int>(status) << "\n";
|
||||
if (pUserData == nullptr) {
|
||||
// Expected case
|
||||
} else {
|
||||
FWGPU_LOGW << "Unexpected non-null pUserData received.\n";
|
||||
}
|
||||
},
|
||||
userDataPtr /* pUserData */
|
||||
);
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
@@ -479,9 +473,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
||||
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
||||
#endif
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// hwSwapChain->swapChain = mSwapChain.get();
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
@@ -504,13 +495,7 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {
|
||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
||||
WGPUTexture const* src = handleCast<WGPUTexture>(srch);
|
||||
(void) src;
|
||||
// textures.insert(
|
||||
// constructHandle<WGPUTexture>(th, src, baseLevel, levelCount));
|
||||
}
|
||||
uint8_t baseLevel, uint8_t levelCount) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
@@ -537,11 +522,7 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
|
||||
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||
assert_invariant(!mDefaultRenderTarget);
|
||||
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
|
||||
assert_invariant(mDefaultRenderTarget);
|
||||
}
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
|
||||
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||
@@ -702,11 +683,6 @@ void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
|
||||
|
||||
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
Handle<HwBufferObject> boh) {
|
||||
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
|
||||
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
|
||||
assert_invariant(index < vertexBuffer->mBuffers.size());
|
||||
vertexBuffer->setBuffer(bufferObject, index);
|
||||
|
||||
}
|
||||
|
||||
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
||||
@@ -739,33 +715,9 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
||||
mTextureView = mSwapChain->GetNextSurfaceTextureView(params.viewport.width, params.viewport.height);
|
||||
wgpu::RenderPassColorAttachment renderPassColorAttachment = {};
|
||||
renderPassColorAttachment.view = mTextureView;
|
||||
renderPassColorAttachment.resolveTarget = nullptr;
|
||||
renderPassColorAttachment.loadOp = wgpu::LoadOp::Clear;
|
||||
renderPassColorAttachment.storeOp = wgpu::StoreOp::Store;
|
||||
renderPassColorAttachment.clearValue = wgpu::Color{1, 0 , 0 , 1};
|
||||
renderPassColorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||
|
||||
wgpu::RenderPassDescriptor renderPassDescriptor = {};
|
||||
renderPassDescriptor.nextInChain = nullptr;
|
||||
renderPassDescriptor.colorAttachmentCount = 1;
|
||||
renderPassDescriptor.colorAttachments = &renderPassColorAttachment;
|
||||
renderPassDescriptor.depthStencilAttachment = nullptr;
|
||||
renderPassDescriptor.timestampWrites = nullptr;
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
mRenderPassEncoder.SetViewport((float)params.viewport.left, (float)params.viewport.bottom,
|
||||
(float) params.viewport.width, (float) params.viewport.height, params.depthRange.near, params.depthRange.far);
|
||||
// (float) 1024/*params.viewport.width*/, (float) 640/*params.viewport.height*/, params.depthRange.near, params.depthRange.far);
|
||||
}
|
||||
|
||||
void WebGPUDriver::endRenderPass(int) {
|
||||
// FWGPU_LOGW << __FUNCTION__<< "\n";
|
||||
mRenderPassEncoder.End();
|
||||
mRenderPassEncoder = nullptr;
|
||||
}
|
||||
|
||||
void WebGPUDriver::nextSubpass(int) {
|
||||
@@ -775,12 +727,6 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
}
|
||||
|
||||
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||
wgpu::CommandBufferDescriptor commandBufferDescriptor = {};
|
||||
commandBufferDescriptor.nextInChain = nullptr;
|
||||
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
|
||||
|
||||
// mQueue.Submit(1, &mCommandBuffer);
|
||||
// mCommandBuffer = nullptr;
|
||||
}
|
||||
|
||||
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
@@ -837,8 +783,6 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
|
||||
@@ -17,27 +17,18 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
#include "webgpu/WebGPUSwapChain.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "private/backend/Driver.h"
|
||||
#include "private/backend/HandleAllocator.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
|
||||
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -49,30 +40,26 @@ public:
|
||||
~WebGPUDriver() noexcept override;
|
||||
|
||||
[[nodiscard]] Dispatcher getDispatcher() const noexcept final;
|
||||
[[nodiscard]] static Driver* create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
[[nodiscard]] static Driver* create(WebGPUPlatform& platform) noexcept;
|
||||
|
||||
private:
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
|
||||
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
||||
// handled in the WebGPUPlatform
|
||||
WebGPUPlatform& mPlatform;
|
||||
wgpu::Surface mSurface = nullptr;
|
||||
wgpu::Adapter mAdapter = nullptr;
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Queue mQueue = nullptr;
|
||||
// TODO consider moving to handle allocator when ready
|
||||
std::unique_ptr<WebGPUSwapChain> mSwapChain = nullptr;
|
||||
uint64_t mNextFakeHandle = 1;
|
||||
wgpu::CommandEncoder mCommandEncoder = nullptr;
|
||||
wgpu::TextureView mTextureView = nullptr;
|
||||
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
friend class ConcreteDispatcher;
|
||||
|
||||
@@ -87,29 +74,8 @@ private:
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl);
|
||||
|
||||
#include "private/backend/DriverAPI.inc"
|
||||
|
||||
/*
|
||||
* Memory management
|
||||
*/
|
||||
|
||||
HandleAllocatorWGPU mHandleAllocator;
|
||||
|
||||
template<typename D>
|
||||
Handle<D> allocHandle() {
|
||||
return mHandleAllocator.allocate<D>();
|
||||
}
|
||||
|
||||
template<typename D, typename B, typename ... ARGS>
|
||||
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
|
||||
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
||||
}
|
||||
template<typename D, typename B>
|
||||
D* handleCast(Handle<B> handle) noexcept {
|
||||
return mHandleAllocator.handle_cast<D*>(handle);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// Created by Idris Idris Shah on 3/21/25.
|
||||
//
|
||||
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WGPUVertexBuffer::WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount,
|
||||
Handle<WGPUVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vextexCount),
|
||||
vbih(vbih),
|
||||
mBuffers(MAX_VERTEX_BUFFER_COUNT) {}
|
||||
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
|
||||
|
||||
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
|
||||
: HwBufferObject(byteCount),
|
||||
mBindingType(bindingType) {}
|
||||
}// namespace filament::backend
|
||||
@@ -1,109 +0,0 @@
|
||||
//
|
||||
// Created by Idris Idris Shah on 3/21/25.
|
||||
//
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct WGPUBufferObject;
|
||||
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
||||
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
attributes(attributes) {}
|
||||
AttributeArray attributes;
|
||||
};
|
||||
|
||||
struct WGPUVertexBuffer : public HwVertexBuffer {
|
||||
WGPUVertexBuffer(uint32_t vextexCount, uint32_t bufferCount, Handle<WGPUVertexBufferInfo> vbih);
|
||||
void setBuffer(WGPUBufferObject* bufferObject, uint32_t index);
|
||||
|
||||
Handle<WGPUVertexBufferInfo> vbih;
|
||||
utils::FixedCapacityVector<wgpu::Buffer> mBuffers;
|
||||
};
|
||||
|
||||
struct WGPUIndexBuffer : public HwIndexBuffer {
|
||||
WGPUIndexBuffer(BufferUsage usage, uint8_t elementSize, uint32_t indexCount);
|
||||
|
||||
wgpu::Buffer buffer;
|
||||
};
|
||||
|
||||
struct WGPUBufferObject : HwBufferObject {
|
||||
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
|
||||
|
||||
wgpu::Buffer mBuffer;
|
||||
const BufferObjectBinding mBindingType;
|
||||
};
|
||||
|
||||
class WGPUTexture : public HwTexture {
|
||||
public:
|
||||
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||
|
||||
// constructors for creating texture views
|
||||
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
wgpu::Texture texture = nullptr;
|
||||
};
|
||||
|
||||
class WGPURenderTarget : public HwRenderTarget {
|
||||
public:
|
||||
class Attachment {
|
||||
public:
|
||||
friend class WGPURenderTarget;
|
||||
|
||||
Attachment() = default;
|
||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||
: level(level),
|
||||
layer(layer),
|
||||
texture(gpuTexture->texture),
|
||||
mWGPUTexture(gpuTexture) {}
|
||||
|
||||
uint8_t level = 0;
|
||||
uint16_t layer = 0;
|
||||
|
||||
private:
|
||||
wgpu::Texture texture = nullptr;
|
||||
WGPUTexture* mWGPUTexture = nullptr;
|
||||
};
|
||||
|
||||
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
||||
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
|
||||
WGPURenderTarget()
|
||||
: HwRenderTarget(0, 0),
|
||||
defaultRenderTarget(true) {}
|
||||
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
|
||||
const RenderPassParams& params);
|
||||
|
||||
math::uint2 getAttachmentSize() noexcept;
|
||||
|
||||
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
|
||||
uint8_t getSamples() const { return samples; }
|
||||
|
||||
Attachment getDrawColorAttachment(size_t index);
|
||||
Attachment getReadColorAttachment(size_t index);
|
||||
|
||||
private:
|
||||
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
|
||||
bool defaultRenderTarget = false;
|
||||
uint8_t samples = 1;
|
||||
|
||||
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
math::uint2 attachmentSize = {};
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "webgpu/WebGPUSwapChain.h"
|
||||
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||
std::stringstream usagesStream{};
|
||||
usagesStream << capabilities.usages;
|
||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usages: " << usagesStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||
[](wgpu::TextureFormat const format) {
|
||||
std::stringstream formatStream{};
|
||||
formatStream << format;
|
||||
FWGPU_LOGI << " " << formatStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
|
||||
std::for_each(capabilities.presentModes,
|
||||
capabilities.presentModes + capabilities.presentModeCount,
|
||||
[](wgpu::PresentMode const presentMode) {
|
||||
std::stringstream presentModeStream{};
|
||||
presentModeStream << presentMode;
|
||||
FWGPU_LOGI << " " << presentModeStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
|
||||
std::for_each(capabilities.alphaModes,
|
||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||
std::stringstream alphaModeStream{};
|
||||
alphaModeStream << alphaMode;
|
||||
FWGPU_LOGI << " " << alphaModeStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config) {
|
||||
std::stringstream formatStream{};
|
||||
formatStream << config.format;
|
||||
std::stringstream usageStream{};
|
||||
usageStream << config.usage;
|
||||
std::stringstream alphaModeStream{};
|
||||
alphaModeStream << config.alphaMode;
|
||||
std::stringstream presentModeStream{};
|
||||
presentModeStream << config.presentMode;
|
||||
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
|
||||
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
|
||||
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
|
||||
[](wgpu::TextureFormat const viewFormat) {
|
||||
std::stringstream viewFormatStream{};
|
||||
viewFormatStream << viewFormat;
|
||||
FWGPU_LOGI << " " << viewFormatStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha mode: " << alphaModeStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
|
||||
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
|
||||
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||
const std::array expectedColorFormats =
|
||||
useSRGBColorSpace ?
|
||||
std::array{
|
||||
wgpu::TextureFormat::RGBA8UnormSrgb,
|
||||
wgpu::TextureFormat::BGRA8UnormSrgb }
|
||||
: std::array{
|
||||
wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureFormat::BGRA8Unorm };
|
||||
auto const firstFoundColorFormat = std::find_first_of(expectedColorFormats.begin(),
|
||||
expectedColorFormats.end(), availableFormats, availableFormats + availableFormatsCount);
|
||||
FILAMENT_CHECK_POSTCONDITION(firstFoundColorFormat != expectedColorFormats.end())
|
||||
<< "Cannot find a suitable WebGPU swapchain "
|
||||
<< (useSRGBColorSpace ? "sRGB" : "non-standard (e.g. linear) RGB") << " color format";
|
||||
return *firstFoundColorFormat;
|
||||
}
|
||||
|
||||
wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||
wgpu::PresentMode const* availablePresentModes) {
|
||||
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
|
||||
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
|
||||
const wgpu::PresentMode desiredPresentMode = wgpu::PresentMode::Fifo;
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
std::any_of(availablePresentModes, availablePresentModes + availablePresentModesCount,
|
||||
[](const wgpu::PresentMode availablePresentMode) {
|
||||
return availablePresentMode == desiredPresentMode;
|
||||
}))
|
||||
<< "Cannot find a suitable WebGPU swapchain present mode";
|
||||
return desiredPresentMode;
|
||||
}
|
||||
|
||||
wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
wgpu::CompositeAlphaMode const* availableAlphaModes) {
|
||||
bool autoAvailable = false;
|
||||
bool inheritAvailable = false;
|
||||
bool opaqueAvailable = false;
|
||||
bool premultipliedAvailable = false;
|
||||
bool unpremultipliedAvailable = false;
|
||||
std::for_each(availableAlphaModes, availableAlphaModes + availableAlphaModesCount,
|
||||
[&](const wgpu::CompositeAlphaMode alphMode) {
|
||||
switch (alphMode) {
|
||||
// in practice, the surface capabilities would not list Auto,
|
||||
// but for completeness and defensive programming we can leverage it
|
||||
// if it explicitly comes back as available/supported
|
||||
case wgpu::CompositeAlphaMode::Auto:
|
||||
autoAvailable = true;
|
||||
break;
|
||||
case wgpu::CompositeAlphaMode::Opaque:
|
||||
opaqueAvailable = true;
|
||||
break;
|
||||
case wgpu::CompositeAlphaMode::Premultiplied:
|
||||
premultipliedAvailable = true;
|
||||
break;
|
||||
case wgpu::CompositeAlphaMode::Unpremultiplied:
|
||||
unpremultipliedAvailable = true;
|
||||
break;
|
||||
case wgpu::CompositeAlphaMode::Inherit:
|
||||
inheritAvailable = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (autoAvailable || inheritAvailable || opaqueAvailable) {
|
||||
return wgpu::CompositeAlphaMode::Auto;
|
||||
} else if (premultipliedAvailable) {
|
||||
// In practice, we do not expect this to possibly happen, as opaque should be supported,
|
||||
// which we select first. However, if the underlying system actually does not support
|
||||
// opaque we allow it to be tested, but warn that this may not likely work as they expect
|
||||
// (untested territory).
|
||||
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
|
||||
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
|
||||
"historically used these. Premultiplied alpha composite mode for "
|
||||
"transparency is being selected as a fallback, but may not work as expected."
|
||||
<< utils::io::endl;
|
||||
return wgpu::CompositeAlphaMode::Premultiplied;
|
||||
} else {
|
||||
FILAMENT_CHECK_POSTCONDITION(unpremultipliedAvailable)
|
||||
<< "No available composite alpha modes? Unknown/unhandled composite alpha mode?";
|
||||
// Again, we don't expect this in practice, but allow if for the same reason as premultiplied.
|
||||
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
|
||||
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
|
||||
"historically used these. Unpremultiplied alpha composite mode for "
|
||||
"transparency is being selected as a fallback "
|
||||
"(premulitipled is not available either), but may not work as expected."
|
||||
<< utils::io::endl;
|
||||
return wgpu::CompositeAlphaMode::Unpremultiplied;
|
||||
}
|
||||
}
|
||||
|
||||
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device& device,
|
||||
wgpu::SurfaceCapabilities const& capabilities, bool useSRGBColorSpace) {
|
||||
config.device = device;
|
||||
config.usage = wgpu::TextureUsage::RenderAttachment;
|
||||
config.format =
|
||||
selectColorFormat(capabilities.formatCount, capabilities.formats, useSRGBColorSpace);
|
||||
config.presentMode =
|
||||
selectPresentMode(capabilities.presentModeCount, capabilities.presentModes);
|
||||
config.alphaMode = selectAlphaMode(capabilities.alphaModeCount, capabilities.alphaModes);
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter,
|
||||
wgpu::Device& device, uint64_t flags)
|
||||
: mSurface(surface) {
|
||||
wgpu::SurfaceCapabilities capabilities = {};
|
||||
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||
} else {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printSurfaceCapabilitiesDetails(capabilities);
|
||||
#endif
|
||||
}
|
||||
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
||||
initConfig(mConfig, device, capabilities, useSRGBColorSpace);
|
||||
}
|
||||
|
||||
WebGPUSwapChain::~WebGPUSwapChain() {
|
||||
if (mConfigured) {
|
||||
mSurface.Unconfigure();
|
||||
mConfigured = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture* texture) {
|
||||
if (width < 1 || height < 1) {
|
||||
PANIC_LOG("WebGPUSwapChain::GetCurrentTexture: Invalid width and/or height requested.");
|
||||
return;
|
||||
}
|
||||
if (mConfig.width != width || mConfig.height != height || !mConfigured) {
|
||||
mConfig.width = width;
|
||||
mConfig.height = height;
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printSurfaceConfiguration(mConfig);
|
||||
#endif
|
||||
mSurface.Configure(&mConfig);
|
||||
mConfigured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mSurface.GetCurrentTexture(texture);
|
||||
}
|
||||
|
||||
wgpu::TextureView WebGPUSwapChain::GetNextSurfaceTextureView(uint32_t width, uint32_t height) {
|
||||
wgpu::SurfaceTexture surfaceTexture;
|
||||
GetCurrentTexture(width, height, &surfaceTexture);
|
||||
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::Success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a view for this surface texture
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor;
|
||||
textureViewDescriptor.nextInChain = nullptr;
|
||||
textureViewDescriptor.label = "Surface texture view";
|
||||
textureViewDescriptor.format = surfaceTexture.texture.GetFormat();
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
textureViewDescriptor.baseMipLevel = 0;
|
||||
textureViewDescriptor.mipLevelCount = 1;
|
||||
textureViewDescriptor.baseArrayLayer = 0;
|
||||
textureViewDescriptor.arrayLayerCount = 1;
|
||||
textureViewDescriptor.aspect = wgpu::TextureAspect::All;
|
||||
wgpu::TextureView textureView = surfaceTexture.texture.CreateView(&textureViewDescriptor);
|
||||
|
||||
return textureView;
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::Present() {
|
||||
assert_invariant(mSurface);
|
||||
mSurface.Present();
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUSwapChain : public Platform::SwapChain {
|
||||
public:
|
||||
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter, wgpu::Device& device,
|
||||
uint64_t flags);
|
||||
~WebGPUSwapChain();
|
||||
|
||||
void GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture*);
|
||||
wgpu::TextureView GetNextSurfaceTextureView(uint32_t width, uint32_t height);
|
||||
void Present();
|
||||
|
||||
private:
|
||||
wgpu::Surface mSurface = {};
|
||||
wgpu::SurfaceConfiguration mConfig = {};
|
||||
bool mConfigured = false;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||
@@ -144,13 +144,13 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
}
|
||||
|
||||
Driver* WebGPUPlatform::createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
const Platform::DriverConfig& /*driverConfig*/) noexcept {
|
||||
if (sharedContext) {
|
||||
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
|
||||
"sharedContext was provided, but it will be ignored."
|
||||
<< utils::io::endl;
|
||||
}
|
||||
return WebGPUDriver::create(*this, driverConfig);
|
||||
return WebGPUDriver::create(*this);
|
||||
}
|
||||
|
||||
WebGPUPlatform::WebGPUPlatform()
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
/**
|
||||
* Windows OS specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
@@ -43,12 +41,12 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
|
||||
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
|
||||
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
|
||||
surfaceSourceWin.hwnd = nativeWindow;
|
||||
const wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceWin,
|
||||
.label = "windows_surface"
|
||||
};
|
||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface.Get() != nullptr) << "Unable to create Windows-backed surface.";
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Windows-backed surface.";
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ImageExpectations.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "utils/Hash.h"
|
||||
#include <fstream>
|
||||
|
||||
#include "backend/PixelBufferDescriptor.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#ifndef FILAMENT_IOS
|
||||
|
||||
#include <imageio/ImageEncoder.h>
|
||||
#include <image/ColorTransform.h>
|
||||
|
||||
#endif
|
||||
|
||||
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
||||
uint32_t expectedPixelHash)
|
||||
: mWidth(width), mHeight(height), mFileName(std::move(fileName)),
|
||||
mExpectedPixelHash(expectedPixelHash) {}
|
||||
|
||||
int ScreenshotParams::width() const {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
int ScreenshotParams::height() const {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
uint32_t ScreenshotParams::expectedHash() const {
|
||||
return mExpectedPixelHash;
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::outputDirectoryPath() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::generatedActualFileName() const {
|
||||
return absl::StrFormat("%s_actual.png", mFileName);
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::generatedActualFilePath() const {
|
||||
return absl::StrFormat("%s/%s", outputDirectoryPath(), generatedActualFileName());
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::goldenFileName() const {
|
||||
return absl::StrFormat("%s_golden.png", mFileName);
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::goldenFilePath() const {
|
||||
return absl::StrFormat("%s/%s", outputDirectoryPath(), goldenFileName());
|
||||
}
|
||||
|
||||
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
||||
filament::backend::DriverApi& api, ScreenshotParams params,
|
||||
filament::backend::RenderTargetHandle renderTarget)
|
||||
: mFileName(fileName), mLineNumber(lineNumber), mParams(std::move(params)),
|
||||
mResult(api, renderTarget, mParams) {}
|
||||
|
||||
void ImageExpectation::evaluate() {
|
||||
// Ensure this is only evaluated once.
|
||||
if (mEvaluated) {
|
||||
return;
|
||||
}
|
||||
mEvaluated = true;
|
||||
|
||||
// Do the actual image comparison inside a scoped trace with the stored file and line.
|
||||
{
|
||||
testing::ScopedTrace trace(mFileName, mLineNumber, "");
|
||||
compareImage();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageExpectation::compareImage() const {
|
||||
bool bytesFilled = mResult.bytesFilled();
|
||||
EXPECT_THAT(bytesFilled, testing::IsTrue())
|
||||
<< "Render target wasn't copied to the buffer for " << mFileName;
|
||||
if (bytesFilled) {
|
||||
uint32_t actualHash = mResult.hash();
|
||||
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash()));
|
||||
}
|
||||
}
|
||||
|
||||
ImageExpectations::ImageExpectations(filament::backend::DriverApi& api) : mApi(api) {}
|
||||
|
||||
ImageExpectations::~ImageExpectations() {
|
||||
// Guarantee that all expectations are evaluated when this leaves scope even if the caller
|
||||
// forgot to manually evaluate them.
|
||||
evaluate();
|
||||
}
|
||||
|
||||
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
|
||||
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
|
||||
mExpectations.emplace_back(fileName, lineNumber, mApi, std::move(params), renderTarget);
|
||||
}
|
||||
|
||||
void ImageExpectations::evaluate() {
|
||||
for (auto& expectation: mExpectations) {
|
||||
expectation.evaluate();
|
||||
}
|
||||
mExpectations.clear();
|
||||
}
|
||||
|
||||
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
|
||||
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
|
||||
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
|
||||
#ifdef FILAMENT_IOS
|
||||
bytesFilled_ = true;
|
||||
bytes_.resize(size);
|
||||
std::fill(bytes_.begin(), bytes_.end(), 0);
|
||||
#else
|
||||
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
|
||||
mInternal->bytes.resize(size);
|
||||
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
||||
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
|
||||
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
|
||||
internal->params.height(),
|
||||
internal->params.width() * 4, (uint8_t*)buffer);
|
||||
std::string filePath = internal->params.generatedActualFilePath();
|
||||
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
|
||||
filePath);
|
||||
internal->bytesFilled = true;
|
||||
};
|
||||
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
|
||||
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
|
||||
(void*)mInternal.get());
|
||||
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
|
||||
std::move(pb));
|
||||
#endif
|
||||
}
|
||||
|
||||
RenderTargetDump::~RenderTargetDump() {
|
||||
// If the GPU callback hasn't been made yet then there's a callback elsewhere that has a copy of
|
||||
// the internal pointer. But there's no guarantee that the callback will be ever made if the GPU
|
||||
// pipeline wasn't run for some reason. So it is necessary to leak the memory.
|
||||
// It would be possible to try to coordinate with the callback to have it clean up the memory,
|
||||
// but if this condition happens there's already an issue with the test case so there's no need.
|
||||
if (!bytesFilled()) {
|
||||
mInternal.release();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RenderTargetDump::hash() const {
|
||||
return utils::hash::murmur3((uint32_t*)mInternal->bytes.data(), mInternal->bytes.size() / 4, 0);
|
||||
}
|
||||
|
||||
bool RenderTargetDump::bytesFilled() const {
|
||||
return mInternal->bytesFilled;
|
||||
}
|
||||
|
||||
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_IMAGE_EXPECTATIONS_H
|
||||
#define TNT_IMAGE_EXPECTATIONS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "backend/Handle.h"
|
||||
#include "backend/DriverApiForward.h"
|
||||
|
||||
// Arguments are (RenderTargetHandle renderTarget, ImageExpectations& expectations,
|
||||
// ScreenshotParams screenshotParams)
|
||||
#define EXPECT_IMAGE(renderTarget, expectations, screenshotParams) \
|
||||
do { \
|
||||
expectations.addExpectation( \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
renderTarget, \
|
||||
screenshotParams); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Stores user-provided configuration values for an image expectation
|
||||
*/
|
||||
class ScreenshotParams {
|
||||
public:
|
||||
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash);
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
uint32_t expectedHash() const;
|
||||
|
||||
std::string outputDirectoryPath() const;
|
||||
std::string generatedActualFileName() const;
|
||||
std::string generatedActualFilePath() const;
|
||||
std::string goldenFileName() const;
|
||||
std::string goldenFilePath() const;
|
||||
|
||||
private:
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
uint32_t mExpectedPixelHash;
|
||||
std::string mFileName;
|
||||
};
|
||||
|
||||
/**
|
||||
* When created adds a command to the GPU pipeline to copy the render target into a buffer and
|
||||
* stores the result.
|
||||
* If this object is destroyed before the GPU pipeline is flushed it will leak memory in order to
|
||||
* avoid the GPU pipeline callback being a use-after-free.
|
||||
*/
|
||||
class RenderTargetDump {
|
||||
public:
|
||||
RenderTargetDump(filament::backend::DriverApi& api,
|
||||
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params);
|
||||
RenderTargetDump(RenderTargetDump&& other) = default;
|
||||
RenderTargetDump& operator=(RenderTargetDump&& other) = default;
|
||||
~RenderTargetDump();
|
||||
|
||||
/**
|
||||
* Should only bue used if BytesFilled returns true.
|
||||
* @return The hash of the stored bytes.
|
||||
*/
|
||||
uint32_t hash() const;
|
||||
/**
|
||||
* Thread safe as this is backed by an atomic.
|
||||
* Once this returns true it will never return false.
|
||||
* @return Whether the bytes have actually been copied from the GPU to the buffer.
|
||||
*/
|
||||
bool bytesFilled() const;
|
||||
|
||||
private:
|
||||
struct Internal {
|
||||
explicit Internal(const ScreenshotParams& params);
|
||||
ScreenshotParams params;
|
||||
std::atomic<bool> bytesFilled = false;
|
||||
std::vector<unsigned char> bytes;
|
||||
};
|
||||
|
||||
// We need a memory location that won't be invalidated to pass to GPU callbacks as they can't
|
||||
// be canceled during the destructor.
|
||||
std::unique_ptr<Internal> mInternal;
|
||||
};
|
||||
|
||||
class ImageExpectation {
|
||||
public:
|
||||
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
|
||||
ScreenshotParams params, filament::backend::RenderTargetHandle renderTarget);
|
||||
|
||||
void evaluate();
|
||||
|
||||
private:
|
||||
void compareImage() const;
|
||||
|
||||
bool mEvaluated = false;
|
||||
const char* mFileName;
|
||||
int mLineNumber;
|
||||
ScreenshotParams mParams;
|
||||
RenderTargetDump mResult;
|
||||
};
|
||||
|
||||
class ImageExpectations {
|
||||
public:
|
||||
explicit ImageExpectations(filament::backend::DriverApi& api);
|
||||
~ImageExpectations();
|
||||
|
||||
/**
|
||||
* Not meant to be called directly, use EXPECT_IMAGE to get the file name and line number
|
||||
*/
|
||||
void addExpectation(const char* fileName, int lineNumber,
|
||||
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params);
|
||||
|
||||
void evaluate();
|
||||
|
||||
private:
|
||||
filament::backend::DriverApi& mApi;
|
||||
std::vector<ImageExpectation> mExpectations;
|
||||
};
|
||||
|
||||
#endif //TNT_IMAGE_EXPECTATIONS_H
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Shader.h"
|
||||
|
||||
#include "BackendTest.h"
|
||||
#include "ShaderGenerator.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament::backend;
|
||||
|
||||
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniformNames.size());
|
||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
||||
kLayouts[i] =
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
|
||||
};
|
||||
|
||||
filamat::DescriptorSets descriptors;
|
||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
||||
descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[i], {}}};
|
||||
}
|
||||
ShaderGenerator shaderGen(
|
||||
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
|
||||
BackendTest::sIsMobilePlatform, std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
||||
prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
|
||||
}
|
||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||
|
||||
mDescriptorSetLayout = cleanup.add(
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||
|
||||
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
||||
}
|
||||
|
||||
filament::backend::ProgramHandle Shader::getProgram() const {
|
||||
return mProgram;
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const {
|
||||
return mDescriptorSetLayout;
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
|
||||
return mDescriptorSet;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_SHADER_H
|
||||
#define TNT_SHADER_H
|
||||
|
||||
#include "Lifetimes.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
// All describing a shader that should be created.
|
||||
struct ShaderConfig {
|
||||
std::string vertexShader;
|
||||
std::string fragmentShader;
|
||||
std::vector<utils::CString> uniformNames;
|
||||
};
|
||||
|
||||
// All values describing a uniform.
|
||||
struct ResolvedUniformBindingConfig {
|
||||
size_t dataSize;
|
||||
size_t bufferSize;
|
||||
uint32_t byteOffset;
|
||||
filament::backend::descriptor_set_t set;
|
||||
filament::backend::descriptor_binding_t binding;
|
||||
};
|
||||
|
||||
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
|
||||
// resolve can be called to create a ResolvedBindingConfig with default values
|
||||
// use in place of nullopt values.
|
||||
struct UniformBindingConfig {
|
||||
std::optional<size_t> dataSize;
|
||||
std::optional<size_t> bufferSize;
|
||||
std::optional<uint32_t> byteOffset;
|
||||
std::optional<filament::backend::descriptor_set_t> set;
|
||||
std::optional<filament::backend::descriptor_binding_t> binding;
|
||||
|
||||
template<typename UniformType>
|
||||
ResolvedUniformBindingConfig resolve();
|
||||
};
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
// All graphics resources have their lifetime controlled by the Cleanup and not this object.
|
||||
// Shader must not outlive either the DriverApi or the Cleanup.
|
||||
Shader(filament::backend::DriverApi& api, Cleanup& cleanup, ShaderConfig config);
|
||||
|
||||
// Uploads a uniform with the default config.
|
||||
template<typename UniformType>
|
||||
void uploadUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
|
||||
UniformType uniforms) const;
|
||||
// Binds a uniform with the default config.
|
||||
template<typename UniformType>
|
||||
void bindUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer) const;
|
||||
// Uploads a uniform, with UniformBindingConfig providing the arguments.
|
||||
template<typename UniformType>
|
||||
void uploadUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
|
||||
UniformBindingConfig config, UniformType uniforms) const;
|
||||
// Binds a uniform, with UniformBindingConfig providing the arguments.
|
||||
template<typename UniformType>
|
||||
void bindUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
|
||||
UniformBindingConfig config) const;
|
||||
|
||||
filament::backend::ProgramHandle getProgram() const;
|
||||
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
||||
filament::backend::DescriptorSetHandle getDescriptorSet() const;
|
||||
|
||||
protected:
|
||||
filament::backend::ProgramHandle mProgram;
|
||||
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
||||
filament::backend::DescriptorSetHandle mDescriptorSet;
|
||||
};
|
||||
|
||||
template<typename UniformType>
|
||||
ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
|
||||
auto resolvedDataSize = dataSize.value_or(sizeof(UniformType));
|
||||
return ResolvedUniformBindingConfig{
|
||||
.dataSize = resolvedDataSize,
|
||||
.bufferSize = bufferSize.value_or(resolvedDataSize),
|
||||
.byteOffset = byteOffset.value_or(0),
|
||||
.set = set.value_or(1),
|
||||
.binding = binding.value_or(0)
|
||||
};
|
||||
}
|
||||
|
||||
template<typename UniformType>
|
||||
void Shader::uploadUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
|
||||
UniformBindingConfig config, UniformType uniforms) const {
|
||||
auto resolvedConfig = config.resolve<UniformType>();
|
||||
|
||||
UniformType* tmp = new UniformType(uniforms);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
UniformType* sp = (UniformType*)buffer;
|
||||
delete sp;
|
||||
};
|
||||
filament::backend::BufferDescriptor bd(tmp, resolvedConfig.dataSize, cb);
|
||||
api.updateBufferObject(hwBuffer, std::move(bd), resolvedConfig.byteOffset);
|
||||
}
|
||||
|
||||
template<typename UniformType>
|
||||
void Shader::bindUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
|
||||
UniformBindingConfig config) const {
|
||||
auto resolvedConfig = config.resolve<UniformType>();
|
||||
|
||||
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
|
||||
resolvedConfig.bufferSize);
|
||||
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
|
||||
}
|
||||
|
||||
template<typename UniformType>
|
||||
void Shader::uploadUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
|
||||
UniformType uniforms) const {
|
||||
uploadUniform(api, hwBuffer, UniformBindingConfig{}, uniforms);
|
||||
}
|
||||
|
||||
template<typename UniformType>
|
||||
void Shader::bindUniform(filament::backend::DriverApi& api,
|
||||
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer) const {
|
||||
bindUniform<UniformType>(api, hwBuffer, UniformBindingConfig{});
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif //TNT_SHADER_H
|
||||
+260
-208
@@ -16,9 +16,8 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "ImageExpectations.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
@@ -27,10 +26,8 @@
|
||||
#include <fstream>
|
||||
|
||||
#ifndef FILAMENT_IOS
|
||||
|
||||
#include <imageio/ImageEncoder.h>
|
||||
#include <image/ColorTransform.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
@@ -40,21 +37,6 @@ using namespace filament::backend;
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
struct MaterialParams {
|
||||
float4 color;
|
||||
float4 scale;
|
||||
};
|
||||
|
||||
class BlitTest : public BackendTest {
|
||||
public:
|
||||
BlitTest() : mCleanup(getDriverApi()) {}
|
||||
|
||||
protected:
|
||||
Shader createShader();
|
||||
|
||||
Cleanup mCleanup;
|
||||
};
|
||||
|
||||
static const char* const triangleVs = R"(#version 450 core
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params;
|
||||
@@ -74,12 +56,49 @@ void main() {
|
||||
fragColor = params.color;
|
||||
})";
|
||||
|
||||
Shader BlitTest::createShader() {
|
||||
return Shader(getDriverApi(), mCleanup, ShaderConfig{
|
||||
.vertexShader = triangleVs,
|
||||
.fragmentShader = triangleFs,
|
||||
.uniformNames = { "Params" },
|
||||
});
|
||||
struct MaterialParams {
|
||||
float4 color;
|
||||
float4 scale;
|
||||
};
|
||||
|
||||
struct ScreenshotParams {
|
||||
int width;
|
||||
int height;
|
||||
const char* filename;
|
||||
uint32_t pixelHashResult;
|
||||
};
|
||||
|
||||
#ifdef FILAMENT_IOS
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {}
|
||||
#else
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {
|
||||
using namespace image;
|
||||
const size_t size = params->width * params->height * 4;
|
||||
void* buffer = calloc(1, size);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
ScreenshotParams* params = (ScreenshotParams*) user;
|
||||
int w = params->width, h = params->height;
|
||||
const uint32_t* texels = (uint32_t*) buffer;
|
||||
params->pixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
|
||||
LinearImage image(w, h, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
||||
std::ofstream pngstrm(params->filename, std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", params->filename);
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
|
||||
(void*) params);
|
||||
dapi.readPixels(rt, 0, 0, params->width, params->height, std::move(pb));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
|
||||
MaterialParams* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
MaterialParams* sp = (MaterialParams*) buffer;
|
||||
delete sp;
|
||||
};
|
||||
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
|
||||
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
||||
}
|
||||
|
||||
static uint32_t toUintColor(float4 color) {
|
||||
@@ -97,7 +116,7 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
|
||||
const int width = baseWidth >> level;
|
||||
const int height = baseHeight >> level;
|
||||
const size_t size0 = height * width * 4;
|
||||
uint8_t* buffer0 = (uint8_t*)calloc(size0, 1);
|
||||
uint8_t* buffer0 = (uint8_t*) calloc(size0, 1);
|
||||
PixelBufferDescriptor pb(buffer0, size0, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
||||
|
||||
const float3 foreground = color;
|
||||
@@ -121,7 +140,7 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
|
||||
// | ........
|
||||
// low addresses
|
||||
// This is because OpenGL automatically flips image data when uploading into the texture.
|
||||
uint32_t* texels = (uint32_t*)buffer0;
|
||||
uint32_t* texels = (uint32_t*) buffer0;
|
||||
for (int row = 0; row < height; row++) {
|
||||
for (int col = 0; col < width; col++) {
|
||||
float2 uv = { (col - width / 2.0f) / width, (row - height / 2.0f) / height };
|
||||
@@ -149,7 +168,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
|
||||
const int width = baseWidth >> level;
|
||||
const int height = baseHeight >> level;
|
||||
const size_t size0 = height * width * 4 * 6;
|
||||
uint8_t* buffer0 = (uint8_t*)calloc(size0, 1);
|
||||
uint8_t* buffer0 = (uint8_t*) calloc(size0, 1);
|
||||
PixelBufferDescriptor pb(buffer0, size0, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
||||
|
||||
const float3 foreground = color;
|
||||
@@ -157,7 +176,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
|
||||
const float radius = 0.25f;
|
||||
|
||||
// Draw a circle on a yellow background.
|
||||
uint32_t* texels = (uint32_t*)buffer0;
|
||||
uint32_t* texels = (uint32_t*) buffer0;
|
||||
for (int face = 0; face < 6; face++) {
|
||||
for (int row = 0; row < height; row++) {
|
||||
for (int col = 0; col < width; col++) {
|
||||
@@ -175,9 +194,10 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
|
||||
dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb));
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorMagnify) {
|
||||
TEST_F(BackendTest, ColorMagnify) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -188,40 +208,37 @@ TEST_F(BlitTest, ColorMagnify) {
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
|
||||
{}));
|
||||
dstRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {},
|
||||
{}));
|
||||
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
|
||||
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
|
||||
}
|
||||
|
||||
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
const int srcLevel = 1;
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTargets[0],
|
||||
{ 0, 0, kDstTexWidth, kDstTexHeight }, srcRenderTargets[srcLevel],
|
||||
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
|
||||
SamplerMagFilter::LINEAR);
|
||||
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTargets[srcLevel],
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
{
|
||||
@@ -229,23 +246,29 @@ TEST_F(BlitTest, ColorMagnify) {
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
|
||||
{
|
||||
ImageExpectations expectations(api);
|
||||
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
EXPECT_IMAGE(dstRenderTargets[0], expectations,
|
||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
flushAndWait();
|
||||
RenderFrame frame(api);
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x410bdd31;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorMinify) {
|
||||
TEST_F(BackendTest, ColorMinify) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -256,55 +279,57 @@ TEST_F(BlitTest, ColorMinify) {
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
|
||||
{}));
|
||||
dstRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {},
|
||||
{}));
|
||||
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
|
||||
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
|
||||
}
|
||||
|
||||
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
const int srcLevel = 1;
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0,
|
||||
dstRenderTargets[0], { 0, 0, kDstTexWidth, kDstTexHeight },
|
||||
srcRenderTargets[srcLevel],
|
||||
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
|
||||
dstRenderTargets[0], {0, 0, kDstTexWidth, kDstTexHeight},
|
||||
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
{
|
||||
ImageExpectations expectations(api);
|
||||
|
||||
EXPECT_IMAGE(dstRenderTargets[0], expectations,
|
||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
|
||||
flushAndWait();
|
||||
}
|
||||
// Wait for the ReadPixels result to come back.
|
||||
flushAndWait();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xf3d9c53f;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorResolve) {
|
||||
TEST_F(BackendTest, ColorResolve) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -313,29 +338,49 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
|
||||
constexpr int kSampleCount = 4;
|
||||
|
||||
Shader shader = createShader();
|
||||
// Create a program.
|
||||
ProgramHandle program;
|
||||
{
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "Params",
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0 },
|
||||
{} } };
|
||||
ShaderGenerator shaderGen(
|
||||
triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
|
||||
program = cleanup.add(api.createProgram(std::move(prog)));
|
||||
}
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
|
||||
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
||||
TrianglePrimitive const triangle(api);
|
||||
|
||||
// Create 4-sample texture.
|
||||
Handle<HwTexture> const srcColorTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight,
|
||||
1,
|
||||
TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create 1-sample texture.
|
||||
Handle<HwTexture> const dstColorTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a 4-sample render target with the 4-sample texture.
|
||||
Handle<HwRenderTarget> const srcRenderTarget = mCleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> const srcRenderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
|
||||
{{ srcColorTexture }}, {}, {}));
|
||||
|
||||
// Create a 1-sample render target with the 1-sample texture.
|
||||
Handle<HwRenderTarget> const dstRenderTarget = mCleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> const dstRenderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
|
||||
{{ dstColorTexture }}, {}, {}));
|
||||
|
||||
@@ -349,52 +394,58 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
params.viewport.height = kSrcTexHeight;
|
||||
|
||||
PipelineState state = {};
|
||||
state.program = shader.getProgram();
|
||||
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||
state.program = program;
|
||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
// Draw red triangle into srcRenderTarget.
|
||||
shader.uploadUniform(api, ubuffer, MaterialParams{
|
||||
.color = float4(1, 0, 0, 1),
|
||||
.scale = float4(1, 1, 0.5, 0),
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.color = float4(1, 0, 0, 1),
|
||||
.scale = float4(1, 1, 0.5, 0),
|
||||
});
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer);
|
||||
|
||||
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams));
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
// FIXME: on Metal this triangle is not drawn. Can't understand why.
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
// Resolve the MSAA render target into the single-sample render target.
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR,
|
||||
dstRenderTarget, { 0, 0, kDstTexWidth, kDstTexHeight },
|
||||
srcRenderTarget, { 0, 0, kSrcTexWidth, kSrcTexHeight },
|
||||
dstRenderTarget, {0, 0, kDstTexWidth, kDstTexHeight},
|
||||
srcRenderTarget, {0, 0, kSrcTexWidth, kSrcTexHeight},
|
||||
SamplerMagFilter::NEAREST);
|
||||
|
||||
{
|
||||
ImageExpectations expectations(api);
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, &sparams);
|
||||
|
||||
EXPECT_IMAGE(dstRenderTarget, expectations,
|
||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
|
||||
// Wait for the ReadPixels result to come back.
|
||||
flushAndWait();
|
||||
|
||||
flushAndWait();
|
||||
}
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xebfac2ef;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
|
||||
EXPECT_TRUE(sparams.pixelHashResult == expected);
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, Blit2DTextureArray) {
|
||||
TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
api.startCapture(0);
|
||||
mCleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -409,40 +460,33 @@ TEST_F(BlitTest, Blit2DTextureArray) {
|
||||
constexpr int kDstTexLayer = 0;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth,
|
||||
kSrcTexHeight, kSrcTexDepth,
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0),
|
||||
flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
|
||||
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create two RenderTargets.
|
||||
const int level = 0;
|
||||
Handle<HwRenderTarget> srcRenderTarget = mCleanup.add(
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0,
|
||||
{ srcTexture, level, kSrcTexLayer }, {}, {}));
|
||||
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0,
|
||||
{ dstTexture, level, kDstTexLayer }, {}, {}));
|
||||
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {}));
|
||||
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {}));
|
||||
|
||||
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
|
||||
const int srcLevel = 0;
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
{ 0, 0, kDstTexWidth, kDstTexHeight }, srcRenderTarget,
|
||||
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
|
||||
SamplerMagFilter::LINEAR);
|
||||
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
{
|
||||
@@ -450,24 +494,29 @@ TEST_F(BlitTest, Blit2DTextureArray) {
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
|
||||
{
|
||||
ImageExpectations expectations(api);
|
||||
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
EXPECT_IMAGE(dstRenderTarget, expectations,
|
||||
ScreenshotParams(kDstTexWidth, kDstTexHeight, "Blit2DTextureArray",
|
||||
0x8de7d55b));
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
flushAndWait();
|
||||
RenderFrame frame(api);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x8de7d55b;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, BlitRegion) {
|
||||
TEST_F(BackendTest, BlitRegion) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -480,48 +529,47 @@ TEST_F(BlitTest, BlitRegion) {
|
||||
constexpr int kDstLevel = 0;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Blit one-quarter of src level 1 to dst level 0.
|
||||
Viewport srcRect = {
|
||||
.left = 0,
|
||||
.bottom = 0,
|
||||
.width = (kSrcTexWidth >> kSrcLevel) / 2,
|
||||
.height = (kSrcTexHeight >> kSrcLevel) / 2,
|
||||
.left = 0,
|
||||
.bottom = 0,
|
||||
.width = (kSrcTexWidth >> kSrcLevel) / 2,
|
||||
.height = (kSrcTexHeight >> kSrcLevel) / 2,
|
||||
};
|
||||
Viewport dstRect = {
|
||||
.left = 10,
|
||||
.bottom = 10,
|
||||
.width = kDstTexWidth - 10,
|
||||
.height = kDstTexHeight - 10,
|
||||
.left = 10,
|
||||
.bottom = 10,
|
||||
.width = kDstTexWidth - 10,
|
||||
.height = kDstTexHeight - 10,
|
||||
};
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
// We purposely set the render target width and height to smaller than the texture, to check
|
||||
// that this case is handled correctly.
|
||||
Handle<HwRenderTarget> srcRenderTarget =
|
||||
mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
|
||||
srcRect.height, 1, 0, { srcTexture, kSrcLevel, 0 }, {}, {}));
|
||||
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
|
||||
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {}));
|
||||
Handle<HwRenderTarget> dstRenderTarget =
|
||||
mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
|
||||
kDstTexHeight >> kDstLevel, 1, 0, { dstTexture, kDstLevel, 0 }, {}, {}));
|
||||
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
|
||||
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {}));
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget,
|
||||
srcRect,
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
@@ -530,25 +578,33 @@ TEST_F(BlitTest, BlitRegion) {
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
|
||||
{
|
||||
ImageExpectations expectations(api);
|
||||
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
// TODO: for some reason, this test has very, very slight (as in one pixel) differences
|
||||
// between OpenGL and Metal. So disable golden checking for now.
|
||||
// EXPECT_IMAGE(dstRenderTarget, expectations, ScreenshotParams(kDstTexWidth,
|
||||
// kDstTexHeight, "BlitRegion", 0x74fa34ed));
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
flushAndWait();
|
||||
RenderFrame frame(api);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
|
||||
// OpenGL and Metal. So disable golden checking for now.
|
||||
// Use the compare tool from ImageMagick to see visual differences:
|
||||
// compare -verbose -metric mae BlitRegion_Metal.png BlitRegion_OpenGL.png difference.png
|
||||
//
|
||||
// const uint32_t expected = 0x74fa34ed;
|
||||
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
// EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, BlitRegionToSwapChain) {
|
||||
TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -558,15 +614,15 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current.
|
||||
auto swapChain = mCleanup.add(createSwapChain());
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(api.createDefaultRenderTarget());
|
||||
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
@@ -574,46 +630,42 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
|
||||
{}));
|
||||
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
|
||||
}
|
||||
|
||||
// Blit one-quarter of src level 1 to dst level 0.
|
||||
const int srcLevel = 1;
|
||||
Viewport srcRect = {
|
||||
.left = (kSrcTexWidth >> srcLevel) / 2,
|
||||
.bottom = (kSrcTexHeight >> srcLevel) / 2,
|
||||
.width = (kSrcTexWidth >> srcLevel) / 2,
|
||||
.height = (kSrcTexHeight >> srcLevel) / 2,
|
||||
.left = (kSrcTexWidth >> srcLevel) / 2,
|
||||
.bottom = (kSrcTexHeight >> srcLevel) / 2,
|
||||
.width = (kSrcTexWidth >> srcLevel) / 2,
|
||||
.height = (kSrcTexHeight >> srcLevel) / 2,
|
||||
};
|
||||
Viewport dstRect = {
|
||||
.left = 10,
|
||||
.bottom = 10,
|
||||
.width = kDstTexWidth - 10,
|
||||
.height = kDstTexHeight - 10,
|
||||
.left = 10,
|
||||
.bottom = 10,
|
||||
.width = kDstTexWidth - 10,
|
||||
.height = kDstTexHeight - 10,
|
||||
};
|
||||
|
||||
{
|
||||
ImageExpectations expectations(api);
|
||||
{
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
|
||||
api.commit(swapChain);
|
||||
}
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
|
||||
// TODO: for some reason, this test has been disabled. It needs to be tested on all
|
||||
// machines.
|
||||
// EXPECT_IMAGE(dstRenderTarget, expectations,
|
||||
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
|
||||
}
|
||||
flushAndWait();
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -80,28 +78,8 @@ struct MaterialParams {
|
||||
math::float4 offset;
|
||||
};
|
||||
static_assert(sizeof(MaterialParams) == 8 * sizeof(float));
|
||||
// Uniform config for writing MaterialParams to the shader uniform with 64 bytes of padding.
|
||||
const UniformBindingConfig kBindingConfig = {
|
||||
.dataSize = sizeof(MaterialParams),
|
||||
.bufferSize = sizeof(MaterialParams) + 64,
|
||||
.byteOffset = 64
|
||||
};
|
||||
|
||||
class BufferUpdatesTest : public BackendTest {
|
||||
public:
|
||||
BufferUpdatesTest() : mCleanup(getDriverApi()) {}
|
||||
|
||||
protected:
|
||||
Shader createShader() {
|
||||
return Shader(getDriverApi(), mCleanup, ShaderConfig{
|
||||
vertex, fragment, {"Params"}
|
||||
});
|
||||
}
|
||||
|
||||
Cleanup mCleanup;
|
||||
};
|
||||
|
||||
TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
TEST_F(BackendTest, VertexBufferUpdate) {
|
||||
const bool largeBuffers = false;
|
||||
|
||||
// If updateIndices is true, then even-numbered triangles will have their indices set to
|
||||
@@ -112,15 +90,31 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
// executeCommands().
|
||||
{
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
Shader shader = createShader();
|
||||
// Create a program.
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "Params",
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } };
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
|
||||
auto program = api.createProgram(std::move(p));
|
||||
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// To test large buffers (which exercise a different code path) create an extra large
|
||||
// buffer. Only the first 3 vertices will be used.
|
||||
@@ -134,29 +128,38 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState state;
|
||||
state.program = shader.getProgram();
|
||||
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
|
||||
state.program = program;
|
||||
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
// Create a uniform buffer.
|
||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use
|
||||
// a GPU buffer, which is more interesting to test.
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
|
||||
// GPU buffer, which is more interesting to test.
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
|
||||
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
api.startCapture(0);
|
||||
|
||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||
// uniform definition.
|
||||
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
|
||||
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
});
|
||||
// Upload uniforms.
|
||||
{
|
||||
MaterialParams params {
|
||||
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
};
|
||||
auto* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
auto* sp = (MaterialParams*) buffer;
|
||||
delete sp;
|
||||
};
|
||||
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
|
||||
api.updateBufferObject(ubuffer, std::move(bd), 64);
|
||||
}
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
@@ -199,6 +202,13 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
}
|
||||
|
||||
executeCommands();
|
||||
@@ -206,38 +216,62 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
|
||||
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
|
||||
// buffer object is partially updated.
|
||||
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
Shader shader = createShader();
|
||||
filamat::DescriptorSets descriptors;
|
||||
descriptors[1] = { { "Params",
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } };
|
||||
ShaderGenerator shaderGen(
|
||||
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
|
||||
auto program = api.createProgram(std::move(p));
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
|
||||
|
||||
// Create a uniform buffer.
|
||||
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
|
||||
// GPU buffer, which is more interesting to test.
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64,
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
|
||||
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
// Create a render target.
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {}));
|
||||
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
|
||||
auto renderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {});
|
||||
|
||||
// Upload uniforms for the first triangle.
|
||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||
// uniform definition.
|
||||
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
|
||||
{
|
||||
MaterialParams params {
|
||||
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
|
||||
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||
});
|
||||
};
|
||||
auto* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
auto* sp = (MaterialParams*) buffer;
|
||||
delete sp;
|
||||
};
|
||||
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
|
||||
api.updateBufferObject(ubuffer, std::move(bd), 64);
|
||||
}
|
||||
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
@@ -246,27 +280,29 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
params.viewport.height = 512;
|
||||
params.viewport.width = 512;
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||
renderTarget, swapChain, shader.getProgram(), params);
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
||||
renderTarget, swapChain, program, params);
|
||||
|
||||
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
|
||||
// color.b, color.a, offset.x, and offset.y.
|
||||
shader.uploadUniform(api, ubuffer, UniformBindingConfig{
|
||||
.dataSize = sizeof(std::array<float, 4>),
|
||||
.bufferSize = kBindingConfig.bufferSize,
|
||||
.byteOffset = *kBindingConfig.byteOffset + offsetof(MaterialParams, color.b),
|
||||
},
|
||||
std::array<float, 4>{
|
||||
// color.b, color.a
|
||||
1.0f, 1.0f,
|
||||
// offset.x, offset.y
|
||||
0.5f, 0.5f }
|
||||
);
|
||||
{
|
||||
MaterialParams params {
|
||||
.color = { 1.0f, 0.0f, 1.0f, 1.0f },
|
||||
.offset = { 0.5f, 0.5f, 0.0f, 0.0f }
|
||||
};
|
||||
auto* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
auto* sp = (MaterialParams*) ((char*)buffer - offsetof(MaterialParams, color.b));
|
||||
delete sp;
|
||||
};
|
||||
BufferDescriptor bd((char*)tmp + offsetof(MaterialParams, color.b), sizeof(float) * 4, cb);
|
||||
api.updateBufferObject(ubuffer, std::move(bd), 64 + offsetof(MaterialParams, color.b));
|
||||
}
|
||||
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
|
||||
renderTarget, swapChain, shader.getProgram(), params);
|
||||
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
|
||||
renderTarget, swapChain, program, params);
|
||||
|
||||
static const uint32_t expectedHash = 91322442;
|
||||
readPixelsAndAssertHash(
|
||||
@@ -276,6 +312,14 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
api.destroyTexture(colorTexture);
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
api.finish();
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
@@ -25,14 +23,13 @@ namespace test {
|
||||
|
||||
TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a SwapChain.
|
||||
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
|
||||
// headless) so we obtain a drawable.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
|
||||
|
||||
int callbackCountA = 0;
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
|
||||
@@ -82,10 +79,9 @@ TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
|
||||
TEST_F(BackendTest, FrameCompletedCallback) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a SwapChain.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
|
||||
int callbackCountA = 0;
|
||||
api.setFrameCompletedCallback(swapChain, nullptr,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -130,13 +129,12 @@ static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
||||
// backend's readPixels does not work correctly with textures that have image data uploaded.
|
||||
TEST_F(BackendTest, FeedbackLoops) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
@@ -157,10 +155,10 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
{ "test_tex", DescriptorType::SAMPLER, 0 },
|
||||
{ "Params", DescriptorType::UNIFORM_BUFFER, 1 }
|
||||
});
|
||||
program = cleanup.add(api.createProgram(std::move(prog)));
|
||||
program = api.createProgram(std::move(prog));
|
||||
}
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
@@ -170,27 +168,27 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 1,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
}}});
|
||||
|
||||
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
|
||||
// Create a texture.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage));
|
||||
Handle<HwTexture> const texture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage);
|
||||
|
||||
// Create ubo
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
|
||||
// Create a RenderTarget for each miplevel.
|
||||
Handle<HwRenderTarget> renderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
slog.i << "Level " << int(level) << ": " <<
|
||||
(kTexWidth >> level) << "x" << (kTexHeight >> level) << io::endl;
|
||||
renderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {}));
|
||||
renderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {});
|
||||
}
|
||||
|
||||
// Fill the base level of the texture with interesting colors.
|
||||
@@ -228,13 +226,12 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
state.rasterState.disableBlending();
|
||||
for (int targetLevel = 1; targetLevel < kNumLevels; targetLevel++) {
|
||||
Cleanup passCleanup(api);
|
||||
const uint32_t sourceLevel = targetLevel - 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
|
||||
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
||||
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
auto textureView = api.createTextureView(texture, sourceLevel, 1);
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
@@ -250,6 +247,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
api.destroyTexture(textureView);
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
}
|
||||
|
||||
// Upsample passes
|
||||
@@ -257,13 +256,12 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
state.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
state.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
for (int targetLevel = kNumLevels - 2; targetLevel >= 0; targetLevel--) {
|
||||
Cleanup passCleanup(api);
|
||||
const uint32_t sourceLevel = targetLevel + 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
|
||||
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
|
||||
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
auto textureView = api.createTextureView(texture, sourceLevel, 1);
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
@@ -279,6 +277,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
api.destroyTexture(textureView);
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
}
|
||||
|
||||
// Read back the render target corresponding to the base level.
|
||||
@@ -296,6 +296,15 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyTexture(texture);
|
||||
api.destroyBufferObject(ubuffer);
|
||||
for (auto rt : renderTargets) {
|
||||
api.destroyRenderTarget(rt);
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t expected = 0x70695aa1;
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "BackendTestUtils.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
@@ -296,12 +295,10 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
for (const auto& t : testCases) {
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// Create a program.
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
@@ -318,21 +315,21 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
|
||||
ProgramHandle const program = cleanup.add(api.createProgram(std::move(prog)));
|
||||
ProgramHandle const program = api.createProgram(std::move(prog));
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
|
||||
// Create a Texture.
|
||||
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
t.textureFormat, 1, 512, 512, 1u, usage));
|
||||
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
t.textureFormat, 1, 512, 512, 1u, usage);
|
||||
|
||||
// Upload some pixel data.
|
||||
if (t.uploadSubregions) {
|
||||
@@ -362,6 +359,13 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
api.destroyTexture(texture);
|
||||
}
|
||||
|
||||
api.finish();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -61,28 +60,25 @@ using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
TEST_F(BackendTest, MRT) {
|
||||
DriverApi& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto swapChain = createSwapChain();
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
auto program = getDriverApi().createProgram(std::move(p));
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
TrianglePrimitive triangle(getDriverApi());
|
||||
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
|
||||
|
||||
// Create two Textures.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
Handle<HwTexture> textureA = cleanup.add(api.createTexture(
|
||||
Handle<HwTexture> textureA = getDriverApi().createTexture(
|
||||
SamplerType::SAMPLER_2D, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
@@ -90,8 +86,8 @@ TEST_F(BackendTest, MRT) {
|
||||
512, // width
|
||||
512, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
|
||||
usage); // usage
|
||||
Handle<HwTexture> textureB = getDriverApi().createTexture(
|
||||
SamplerType::SAMPLER_2D, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
@@ -99,10 +95,10 @@ TEST_F(BackendTest, MRT) {
|
||||
512, // width
|
||||
512, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
usage); // usage
|
||||
|
||||
// Create a RenderTarget with two attachments.
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
|
||||
// The width and height must match the width and height of the respective mip
|
||||
// level (at least for OpenGL).
|
||||
@@ -112,7 +108,7 @@ TEST_F(BackendTest, MRT) {
|
||||
0, // layerCount
|
||||
{{textureA },{textureB }}, // color
|
||||
{}, // depth
|
||||
{})); // stencil
|
||||
{}); // stencil
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -128,21 +124,25 @@ TEST_F(BackendTest, MRT) {
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
api.startCapture(0);
|
||||
getDriverApi().startCapture(0);
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Draw a triangle.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
getDriverApi().flush();
|
||||
getDriverApi().commit(swapChain);
|
||||
getDriverApi().endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
getDriverApi().stopCapture(0);
|
||||
|
||||
getDriverApi().destroyProgram(program);
|
||||
getDriverApi().destroySwapChain(swapChain);
|
||||
getDriverApi().destroyRenderTarget(defaultRenderTarget);
|
||||
}
|
||||
|
||||
executeCommands();
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "BackendTestUtils.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
@@ -79,13 +78,12 @@ using namespace filament::backend;
|
||||
TEST_F(BackendTest, TextureViewLod) {
|
||||
auto& api = getDriverApi();
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
// Create a SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program that draws only white.
|
||||
@@ -94,7 +92,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
|
||||
whiteProgram = cleanup.add(api.createProgram(std::move(p)));
|
||||
whiteProgram = api.createProgram(std::move(p));
|
||||
}
|
||||
|
||||
// Create a program that samples a texture.
|
||||
@@ -108,19 +106,19 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
|
||||
textureProgram = cleanup.add(api.createProgram(std::move(p)));
|
||||
textureProgram = api.createProgram(std::move(p));
|
||||
}
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet[2];
|
||||
descriptorSet[0] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
descriptorSet[1] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
descriptorSet[0] = api.createDescriptorSet(descriptorSetLayout);
|
||||
descriptorSet[1] = api.createDescriptorSet(descriptorSetLayout);
|
||||
|
||||
// Create a texture that has 4 mip levels. Each level is a different color.
|
||||
// Level 0: 128x128 (red)
|
||||
@@ -129,9 +127,9 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
// Level 3: 16x16 (yellow)
|
||||
const size_t kTextureSize = 128;
|
||||
const size_t kMipLevels = 4;
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
|
||||
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
|
||||
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE);
|
||||
|
||||
// Create image data.
|
||||
auto pixelFormat = PixelDataFormat::RGBA;
|
||||
@@ -165,13 +163,13 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
// Level 1: 64x64 (green) <-- base
|
||||
// Level 2: 32x32 (blue) <--- white triangle rendered
|
||||
// Level 3: 16x16 (yellow) <-- max
|
||||
auto texture13 = cleanup.add(api.createTextureView(texture, 1, 3));
|
||||
auto texture13 = api.createTextureView(texture, 1, 3);
|
||||
|
||||
// Render a white triangle into level 2.
|
||||
// We specify mip level 2, because minMaxLevels has no effect when rendering into a texture.
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, 32, 32, 1, 0,
|
||||
{texture, 2 /* level */, 0 /* layer */}, {}, {}));
|
||||
{texture, 2 /* level */, 0 /* layer */}, {}, {});
|
||||
{
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -187,7 +185,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -219,7 +217,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
api.endRenderPass();
|
||||
|
||||
// Adjust the base mip to 2.
|
||||
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
|
||||
auto texture22 = api.createTextureView(texture, 2, 2);
|
||||
|
||||
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
|
||||
.filterMag = SamplerMagFilter::NEAREST,
|
||||
@@ -246,6 +244,18 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
// Cleanup.
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
api.destroyDescriptorSet(descriptorSet[0]);
|
||||
api.destroyDescriptorSet(descriptorSet[1]);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyTexture(texture);
|
||||
api.destroyTexture(texture13);
|
||||
api.destroyTexture(texture22);
|
||||
api.destroyProgram(whiteProgram);
|
||||
api.destroyProgram(textureProgram);
|
||||
}
|
||||
|
||||
api.finish();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -71,20 +70,18 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
DriverApi& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto swapChain = createSwapChain();
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
auto program = getDriverApi().createProgram(std::move(p));
|
||||
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
TrianglePrimitive triangle(getDriverApi());
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -100,21 +97,25 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
api.startCapture(0);
|
||||
getDriverApi().startCapture(0);
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Render a triangle.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
getDriverApi().flush();
|
||||
getDriverApi().commit(swapChain);
|
||||
getDriverApi().endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
getDriverApi().stopCapture(0);
|
||||
|
||||
getDriverApi().destroyProgram(program);
|
||||
getDriverApi().destroySwapChain(swapChain);
|
||||
getDriverApi().destroyRenderTarget(defaultRenderTarget);
|
||||
}
|
||||
|
||||
executeCommands();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -81,21 +80,20 @@ TEST_F(BackendTest, PushConstants) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
// Create a SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
|
||||
ProgramHandle program = api.createProgram(std::move(p));
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
@@ -152,6 +150,10 @@ TEST_F(BackendTest, PushConstants) {
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Cleanup.
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "BackendTestUtils.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
@@ -237,20 +236,17 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
|
||||
TestCase const testCases[] = { t0, t2, t3, t4, t5, t6, t7, t8 };
|
||||
|
||||
DriverApi& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create programs.
|
||||
Handle<HwProgram> programFloat, programUint;
|
||||
{
|
||||
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
programFloat = cleanup.add(api.createProgram(std::move(p)));
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
programFloat = getDriverApi().createProgram(std::move(p));
|
||||
}
|
||||
{
|
||||
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
programUint = cleanup.add(api.createProgram(std::move(p)));
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
programUint = getDriverApi().createProgram(std::move(p));
|
||||
}
|
||||
|
||||
|
||||
@@ -259,35 +255,35 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
Handle<HwSwapChain> swapChain;
|
||||
if (t.useDefaultRT) {
|
||||
swapChain = cleanup.add(createSwapChain());
|
||||
swapChain = createSwapChain();
|
||||
} else {
|
||||
swapChain = cleanup.add(api.createSwapChainHeadless(t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), 0));
|
||||
swapChain = getDriverApi().createSwapChainHeadless(t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), 0);
|
||||
}
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a Texture and RenderTarget to render into.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
Handle<HwTexture> const texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D,
|
||||
t.mipLevels, t.textureFormat, 1, renderTargetBaseSize, renderTargetBaseSize, 1,
|
||||
usage));
|
||||
usage);
|
||||
|
||||
Handle<HwRenderTarget> renderTarget;
|
||||
if (t.useDefaultRT) {
|
||||
// The width and height must match the width and height of the respective mip
|
||||
// level (at least for OpenGL).
|
||||
renderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
renderTarget = getDriverApi().createDefaultRenderTarget();
|
||||
} else {
|
||||
// The width and height must match the width and height of the respective mip
|
||||
// level (at least for OpenGL).
|
||||
renderTarget = cleanup.add(api.createRenderTarget(
|
||||
renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
|
||||
t.getRenderTargetSize(), t.samples, 0, {{ texture, uint8_t(t.mipLevel) }}, {},
|
||||
{}));
|
||||
{});
|
||||
}
|
||||
|
||||
TrianglePrimitive const triangle(api);
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -298,11 +294,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
params.viewport.height = t.getRenderTargetSize();
|
||||
params.viewport.width = t.getRenderTargetSize();
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Render a white triangle over blue.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
|
||||
PipelineState state;
|
||||
state.program = programFloat;
|
||||
@@ -313,21 +309,21 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
|
||||
api.endRenderPass();
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
if (t.mipLevel > 0) {
|
||||
Cleanup localCleanup(api);
|
||||
// Render red to the first mip level to check that the backend is actually reading the
|
||||
// correct mip.
|
||||
RenderPassParams p = params;
|
||||
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> mipLevelOneRT = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
|
||||
{{ texture }}, {}, {}));
|
||||
{{ texture }}, {}, {});
|
||||
p.clearColor = {1.f, 0.f, 0.f, 1.f};
|
||||
api.beginRenderPass(mipLevelOneRT, p);
|
||||
api.endRenderPass();
|
||||
getDriverApi().beginRenderPass(mipLevelOneRT, p);
|
||||
getDriverApi().endRenderPass();
|
||||
getDriverApi().destroyRenderTarget(mipLevelOneRT);
|
||||
}
|
||||
|
||||
// Read pixels.
|
||||
@@ -351,19 +347,26 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
free(buffer);
|
||||
}, (void*) &t);
|
||||
|
||||
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
|
||||
getDriverApi().readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
|
||||
t.readRect.height, std::move(descriptor));
|
||||
|
||||
// Now render red over what was just rendered. This ensures that readPixels captures the
|
||||
// state of rendering between render passes.
|
||||
params.clearColor = {1.f, 0.f, 0.f, 1.f};
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.endRenderPass();
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
getDriverApi().commit(swapChain);
|
||||
getDriverApi().endFrame(0);
|
||||
|
||||
getDriverApi().destroySwapChain(swapChain);
|
||||
getDriverApi().destroyRenderTarget(renderTarget);
|
||||
getDriverApi().destroyTexture(texture);
|
||||
}
|
||||
|
||||
getDriverApi().destroyProgram(programFloat);
|
||||
getDriverApi().destroyProgram(programUint);
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
flushAndWait();
|
||||
}
|
||||
@@ -372,21 +375,18 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
const size_t renderTargetSize = 2000;
|
||||
const int iterationCount = 100;
|
||||
|
||||
DriverApi& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto swapChain = getDriverApi().createSwapChainHeadless(renderTargetSize, renderTargetSize, 0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
auto program = getDriverApi().createProgram(std::move(p));
|
||||
|
||||
// Create a Texture and RenderTarget to render into.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
||||
Handle<HwTexture> texture = getDriverApi().createTexture(
|
||||
SamplerType::SAMPLER_2D, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
@@ -394,9 +394,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
renderTargetSize, // width
|
||||
renderTargetSize, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
usage); // usage
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR,
|
||||
renderTargetSize, // width
|
||||
renderTargetSize, // height
|
||||
@@ -404,9 +404,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
0, // layerCount
|
||||
{{ texture }}, // color
|
||||
{}, // depth
|
||||
{})); // stencil
|
||||
{}); // stencil
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
TrianglePrimitive triangle(getDriverApi());
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -433,13 +433,13 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
printf("Executing test %d / %d\n", iteration, iterationCount);
|
||||
}
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
getDriverApi().makeCurrent(swapChain, swapChain);
|
||||
getDriverApi().beginFrame(0, 0, 0);
|
||||
|
||||
// Render some content, just so we don't read back uninitialized data.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
getDriverApi().endRenderPass();
|
||||
|
||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
|
||||
@@ -448,9 +448,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
test->readPixelsFinished = true;
|
||||
}, this);
|
||||
|
||||
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
getDriverApi().readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
|
||||
getDriverApi().commit(swapChain);
|
||||
getDriverApi().endFrame(0);
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
@@ -460,7 +460,11 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
|
||||
free(buffer);
|
||||
|
||||
api.finish();
|
||||
getDriverApi().destroyProgram(program);
|
||||
getDriverApi().destroySwapChain(swapChain);
|
||||
getDriverApi().destroyRenderTarget(renderTarget);
|
||||
getDriverApi().destroyTexture(texture);
|
||||
getDriverApi().finish();
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -67,11 +66,10 @@ using namespace filament::backend;
|
||||
// Rendering an external image without setting any data should not crash.
|
||||
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
@@ -84,22 +82,22 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
// Create a program that samples a texture.
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
backend::Handle<HwProgram> program = cleanup.add(api.createProgram(std::move(p)));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
backend::Handle<HwProgram> program = api.createProgram(std::move(p));
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// Create a texture that will be backed by an external image.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
const NativeView& view = getNativeView();
|
||||
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
|
||||
backend::Handle<HwTexture> texture = api.createTexture(
|
||||
SamplerType::SAMPLER_EXTERNAL, // target
|
||||
1, // levels
|
||||
TextureFormat::RGBA8, // format
|
||||
@@ -107,7 +105,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
view.width, // width
|
||||
view.height, // height
|
||||
1, // depth
|
||||
usage)); // usage
|
||||
usage); // usage
|
||||
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
@@ -142,6 +140,15 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
// Delete our resources.
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyTexture(texture);
|
||||
|
||||
// Destroy frame resources.
|
||||
api.destroyProgram(program);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
|
||||
api.finish();
|
||||
|
||||
executeCommands();
|
||||
@@ -149,11 +156,10 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
TEST_F(BackendTest, RenderExternalImage) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = createSwapChain();
|
||||
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
@@ -166,18 +172,18 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
// Create a program that samples a texture.
|
||||
Program p = shaderGen.getProgram(api);
|
||||
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
|
||||
auto program = cleanup.add(api.createProgram(std::move(p)));
|
||||
auto program = api.createProgram(std::move(p));
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::SAMPLER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}}));
|
||||
}}});
|
||||
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// require users to create two Filament textures and have two material parameters
|
||||
// add a "plane" parameter to setExternalImage
|
||||
@@ -221,8 +227,8 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
|
||||
api.setupExternalImage(pixBuffer);
|
||||
backend::Handle<HwTexture> texture =
|
||||
cleanup.add(api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
|
||||
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer));
|
||||
api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
|
||||
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer);
|
||||
|
||||
// We're now free to release the buffer.
|
||||
CVBufferRelease(pixBuffer);
|
||||
@@ -263,6 +269,15 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
// Delete our resources.
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyTexture(texture);
|
||||
|
||||
// Destroy frame resources.
|
||||
api.destroyProgram(program);
|
||||
api.destroyRenderTarget(defaultRenderTarget);
|
||||
|
||||
api.finish();
|
||||
|
||||
executeCommands();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -56,7 +55,6 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
constexpr int kSrcRtHeight = 384;
|
||||
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// color texture (mip level 1) 512x512 depth texture (mip level 0) 512x512
|
||||
// +----------------------------------------+ +------------------------------------------+
|
||||
@@ -82,21 +80,21 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
// executeCommands().
|
||||
{
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
|
||||
ProgramHandle program = api.createProgram(std::move(p));
|
||||
|
||||
// Create source color and depth textures.
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
|
||||
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
|
||||
kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
Handle<HwTexture> depthTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
Handle<HwTexture> depthTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH16, 1, 512, 512, 1,
|
||||
TextureUsage::DEPTH_ATTACHMENT));
|
||||
TextureUsage::DEPTH_ATTACHMENT);
|
||||
|
||||
// Render into the bottom-left quarter of the texture.
|
||||
Viewport srcRect = {
|
||||
@@ -114,13 +112,13 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
|
||||
// We purposely set the render target width and height to smaller than the texture, to check
|
||||
// that this case is handled correctly.
|
||||
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1, 0,
|
||||
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {}));
|
||||
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {});
|
||||
|
||||
Handle<HwRenderTarget> fullRenderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
Handle<HwRenderTarget> fullRenderTarget = api.createRenderTarget(TargetBufferFlags::COLOR,
|
||||
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1, 0,
|
||||
{srcTexture, kSrcLevel, 0}, {}, {}));
|
||||
{srcTexture, kSrcLevel, 0}, {}, {});
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
@@ -152,6 +150,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(srcRenderTarget);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
@@ -166,24 +169,23 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
|
||||
ProgramHandle program = api.createProgram(std::move(p));
|
||||
|
||||
// Create a source color textures.
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
|
||||
// Render into the bottom-left quarter of the texture, checking 3 special cases.
|
||||
// 1. negative viewport left/bottom
|
||||
@@ -205,9 +207,9 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
Viewport scissor = {0, 0, (uint32_t)std::numeric_limits<int32_t>::max(),
|
||||
(uint32_t)std::numeric_limits<int32_t>::max()};
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, 512, 512, 1, 0,
|
||||
{srcTexture, 0, 0}, {}, {}));
|
||||
{srcTexture, 0, 0}, {}, {});
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
@@ -247,6 +249,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -64,23 +63,20 @@ namespace test {
|
||||
class BasicStencilBufferTest : public BackendTest {
|
||||
public:
|
||||
|
||||
Handle<HwSwapChain> mSwapChain;
|
||||
ProgramHandle mProgram;
|
||||
Cleanup mCleanup;
|
||||
|
||||
BasicStencilBufferTest() : mCleanup(getDriverApi()) {}
|
||||
Handle<HwSwapChain> swapChain;
|
||||
ProgramHandle program;
|
||||
|
||||
void SetUp() override {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
mSwapChain = mCleanup.add(createSwapChain());
|
||||
api.makeCurrent(mSwapChain, mSwapChain);
|
||||
swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program p = shaderGen.getProgram(api);
|
||||
mProgram = mCleanup.add(api.createProgram(std::move(p)));
|
||||
program = api.createProgram(std::move(p));
|
||||
}
|
||||
|
||||
void RunTest(Handle<HwRenderTarget> renderTarget) {
|
||||
@@ -107,13 +103,13 @@ public:
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState ps = {};
|
||||
ps.program = mProgram;
|
||||
ps.program = program;
|
||||
ps.rasterState.colorWrite = false;
|
||||
ps.rasterState.depthWrite = false;
|
||||
ps.stencilState.stencilWrite = true;
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(mSwapChain, mSwapChain);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
@@ -133,27 +129,29 @@ public:
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(mSwapChain);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
auto& api = getDriverApi();
|
||||
api.destroyProgram(program);
|
||||
api.destroySwapChain(swapChain);
|
||||
flushAndWait();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create two textures: a color and a stencil, and an associated RenderTarget.
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||
auto stencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
|
||||
auto stencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT);
|
||||
auto renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||
{{colorTexture}}, {}, {{stencilTexture}}));
|
||||
{{colorTexture}}, {}, {{stencilTexture}});
|
||||
|
||||
RunTest(renderTarget);
|
||||
|
||||
@@ -161,20 +159,23 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
|
||||
api.destroyTexture(colorTexture);
|
||||
api.destroyTexture(stencilTexture);
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create two textures: a color and a stencil, and an associated RenderTarget.
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
||||
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
|
||||
auto depthStencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
|
||||
auto renderTarget = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
||||
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
|
||||
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}});
|
||||
|
||||
RunTest(renderTarget);
|
||||
|
||||
@@ -182,27 +183,30 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
|
||||
api.destroyTexture(colorTexture);
|
||||
api.destroyTexture(depthStencilTexture);
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create two textures: a single-sampled color and a MSAA stencil texture.
|
||||
// We also create two RenderTargets, one for each pass:
|
||||
// Pass 0: Render a triangle only into the MSAA stencil buffer.
|
||||
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
|
||||
// Performs an auto-resolve on the color.
|
||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||
auto renderTarget0 = cleanup.add(api.createRenderTarget(
|
||||
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE);
|
||||
auto depthStencilTextureMSAA = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
|
||||
auto renderTarget0 = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
|
||||
auto renderTarget1 = cleanup.add(api.createRenderTarget(
|
||||
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
|
||||
auto renderTarget1 = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
||||
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
|
||||
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
|
||||
|
||||
api.startCapture(0);
|
||||
|
||||
@@ -226,13 +230,13 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState ps = {};
|
||||
ps.program = mProgram;
|
||||
ps.program = program;
|
||||
ps.rasterState.colorWrite = false;
|
||||
ps.rasterState.depthWrite = false;
|
||||
ps.stencilState.stencilWrite = true;
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(mSwapChain, mSwapChain);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget0, params);
|
||||
@@ -254,7 +258,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(mSwapChain);
|
||||
api.commit(swapChain);
|
||||
api.stopCapture(0);
|
||||
api.endFrame(0);
|
||||
|
||||
@@ -262,6 +266,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
|
||||
api.destroyTexture(colorTexture);
|
||||
api.destroyTexture(depthStencilTextureMSAA);
|
||||
api.destroyRenderTarget(renderTarget0);
|
||||
api.destroyRenderTarget(renderTarget1);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <backend/BufferDescriptor.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -91,21 +90,9 @@ public:
|
||||
* @param name A string to identify this BufferObject
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this BufferObject for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this BufferObject
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
|
||||
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -62,17 +61,11 @@ UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, siz
|
||||
template <typename Builder>
|
||||
class UTILS_PUBLIC BuilderNameMixin {
|
||||
public:
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* name, size_t len) noexcept {
|
||||
builderMakeName(mName, name, len);
|
||||
return static_cast<Builder&>(*this);
|
||||
}
|
||||
|
||||
Builder& name(utils::StaticString const& name) noexcept {
|
||||
builderMakeName(mName, name.data(), name.size());
|
||||
return static_cast<Builder&>(*this);
|
||||
}
|
||||
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <backend/BufferDescriptor.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -96,21 +95,9 @@ public:
|
||||
* @param name A string to identify this IndexBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this IndexBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this IndexBuffer
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
|
||||
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <filament/Engine.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
@@ -83,21 +82,9 @@ public:
|
||||
* @param name A string to identify this InstanceBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this InstanceBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this InstanceBuffer
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the InstanceBuffer object and returns a pointer to it.
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <filament/Engine.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
@@ -76,21 +75,9 @@ public:
|
||||
* @param name A string to identify this MorphTargetBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this MorphTargetBuffer
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the MorphTargetBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <filament/RenderableManager.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
@@ -82,21 +81,9 @@ public:
|
||||
* @param name A string to identify this SkinningBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this SkinningBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this SkinningBuffer
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the SkinningBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <backend/CallbackHandler.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <math/mat3.h>
|
||||
|
||||
@@ -151,21 +150,9 @@ public:
|
||||
* @param name A string to identify this Stream
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this Stream for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this Stream
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the Stream object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -230,20 +229,8 @@ public:
|
||||
* @param name A string to identify this Texture
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this Texture for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this Texture
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Creates an external texture. The content must be set using setExternalImage().
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -171,21 +170,9 @@ public:
|
||||
* @param name A string to identify this VertexBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
* @deprecated Use name(utils::StaticString const&) instead.
|
||||
*/
|
||||
UTILS_DEPRECATED
|
||||
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this VertexBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible.
|
||||
*
|
||||
* @param name A string literal to identify this VertexBuffer
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the VertexBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -2341,9 +2341,6 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
|
||||
}
|
||||
|
||||
void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
|
||||
|
||||
bindPostProcessDescriptorSet(driver);
|
||||
|
||||
FEngine const& engine = mEngine;
|
||||
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
|
||||
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
|
||||
@@ -3039,7 +3036,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg, bool sourceHasLuminance,
|
||||
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
|
||||
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc) noexcept {
|
||||
filament::Viewport const&, FrameGraphTexture::Descriptor const& outDesc) noexcept {
|
||||
|
||||
struct QuadBlitData {
|
||||
FrameGraphId<FrameGraphTexture> input;
|
||||
@@ -3055,7 +3052,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
|
||||
.attachments = { .color = { data.output } },
|
||||
.clearFlags = TargetBufferFlags::DEPTH });
|
||||
},
|
||||
[this, vp, sourceHasLuminance](FrameGraphResources const& resources,
|
||||
[this, sourceHasLuminance](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
bindPostProcessDescriptorSet(driver);
|
||||
|
||||
@@ -3081,17 +3078,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
|
||||
});
|
||||
|
||||
mi->setParameter("viewport", float4{
|
||||
float(vp.left) / inputDesc.width,
|
||||
float(vp.bottom) / inputDesc.height,
|
||||
float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height
|
||||
});
|
||||
|
||||
mi->setParameter("viewportInfo", float4{
|
||||
1.0f / inputDesc.width,
|
||||
1.0f / inputDesc.height,
|
||||
float(inputDesc.width),
|
||||
float(inputDesc.height)
|
||||
float(inputDesc.height),
|
||||
});
|
||||
|
||||
mi->commit(driver);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -52,10 +51,6 @@ BufferObject::Builder& BufferObject::Builder::name(const char* name, size_t cons
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
BufferObject::Builder& BufferObject::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
BufferObject* BufferObject::Builder::build(Engine& engine) {
|
||||
return downcast(engine).createBufferObject(*this);
|
||||
}
|
||||
|
||||
@@ -791,17 +791,6 @@ int FEngine::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
if(debug.server) {
|
||||
delete debug.server;
|
||||
}
|
||||
#endif
|
||||
#if FILAMENT_ENABLE_FGVIEWER
|
||||
if(debug.fgviewerServer) {
|
||||
delete debug.fgviewerServer;
|
||||
}
|
||||
#endif
|
||||
|
||||
// terminate() is a synchronous API
|
||||
getDriverApi().terminate();
|
||||
return 0;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -52,10 +51,6 @@ IndexBuffer::Builder& IndexBuffer::Builder::name(const char* name, size_t const
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
IndexBuffer::Builder& IndexBuffer::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
IndexBuffer* IndexBuffer::Builder::build(Engine& engine) {
|
||||
return downcast(engine).createIndexBuffer(*this);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
@@ -56,10 +54,6 @@ InstanceBuffer::Builder& InstanceBuffer::Builder::name(const char* name, size_t
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
InstanceBuffer::Builder& InstanceBuffer::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount >= 1) << "instanceCount must be >= 1.";
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount <= engine.getMaxAutomaticInstances())
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <math/norm.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -60,10 +59,6 @@ MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::name(const char* name, s
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
MorphTargetBuffer* MorphTargetBuffer::Builder::build(Engine& engine) {
|
||||
return downcast(engine).createMorphTargetBuffer(*this);
|
||||
}
|
||||
|
||||
@@ -1364,7 +1364,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
xvp.left = xvp.bottom = 0;
|
||||
svp = xvp;
|
||||
}
|
||||
|
||||
if (scaled) {
|
||||
mightNeedFinalBlit = false;
|
||||
auto viewport = DEBUG_DYNAMIC_SCALING ? xvp : vp;
|
||||
@@ -1386,59 +1385,34 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
SamplerMagFilter::NEAREST, SamplerMinFilter::NEAREST);
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY((input == postProcessInput && viewRenderTarget == mRenderTargetHandle) &&
|
||||
view.isStencilBufferEnabled())) {
|
||||
// FIXME: I think this check is incomplete, if we're rendering into a custom rendertarget
|
||||
// we need to check that it (not the swapchain) has a stencil buffer.
|
||||
assert_invariant(mSwapChain);
|
||||
FILAMENT_CHECK_PRECONDITION(mSwapChain->hasStencilBuffer())
|
||||
<< "View has stencil buffer enabled, but SwapChain does not have "
|
||||
"SwapChain::CONFIG_HAS_STENCIL_BUFFER flag set.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we're ready to present the output of the framegraph, which is held in `input`.
|
||||
* The presentation itself happens by forwarding `input` to the render target, which can
|
||||
* either be the SwapChain's or the View's custom render target. This is done below with
|
||||
* `forwardResource()`.
|
||||
*
|
||||
* There are however a few situations where `input` cannot be forwarded in this manner, and
|
||||
* an intermediate buffer is needed instead.
|
||||
*
|
||||
* 1. Blending is needed (blendModeTranslucent)
|
||||
* 2. Dimensions don't match, e.g.: because we have guard bands (xvp != svp)
|
||||
* 3. MRT is needed (colorGradingConfig.asSubpass)
|
||||
* 4. Frame history is needed (hasScreenSpaceRefraction)
|
||||
* 5. Refraction is used because how clear flags work (ssReflectionsOptions.enabled)
|
||||
* 6. `input` and the render target are not compatible:
|
||||
* - MSAA doesn't match. Note: auto-resolve could work if the SwapChain was configured
|
||||
* this way.
|
||||
*
|
||||
* One complication is that post-processing passes handle some of these issues automatically:
|
||||
* - each post-processing pass will create an intermediate buffer, which takes care of
|
||||
* - MSAA (because of auto-resolve)
|
||||
* - MRT is needed
|
||||
* - Frame history is needed
|
||||
* - Refraction
|
||||
* - the upscaling pass takes care of all the above plus blending and dimensions mismatch.
|
||||
*
|
||||
* TODO: we could make this work with custom render targets because we can access the texture
|
||||
* for those and it could behave just like a regular resource. This would lift
|
||||
* some (but not all) of those limitations. For instance we could use MRTs.
|
||||
*/
|
||||
// We need to do special processing when rendering directly into the swap-chain, that is when
|
||||
// the viewRenderTarget is the default render target (mRenderTarget) and we're rendering into
|
||||
// it.
|
||||
// * This is because the default render target is not multi-sampled, so we need an
|
||||
// intermediate buffer when MSAA is enabled.
|
||||
// * We also need an extra buffer for blending the result to the framebuffer if the view
|
||||
// is translucent AND we've not already done it as part of upscaling.
|
||||
// * And we can't use the default rendertarget if MRT is required (e.g. with color grading
|
||||
// as a subpass)
|
||||
// * And we also can't use the default rendertarget if frame history is needed (e.g. with
|
||||
// screen-space reflections)
|
||||
// * And we also can't use the default rendertarget with refractions, which need to reuse
|
||||
// the rendertarget due to how the clear flags work.
|
||||
// * We also need an extra blit if we haven't yet handled "xvp"
|
||||
// TODO: in that specific scenario it would be better to just not use xvp
|
||||
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
|
||||
|
||||
const bool outputIsSwapChain =
|
||||
(input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle);
|
||||
if (mightNeedFinalBlit) {
|
||||
assert_invariant(!scaled);
|
||||
// Determine if our `input` is in fact the output of the color pass, in which case
|
||||
// many of the caveat above apply.
|
||||
bool const inputIsColorPass = (input == postProcessInput);
|
||||
if (blendModeTranslucent ||
|
||||
xvp != svp ||
|
||||
(inputIsColorPass &&
|
||||
(outputIsSwapChain &&
|
||||
(msaaSampleCount > 1 ||
|
||||
colorGradingConfig.asSubpass ||
|
||||
hasScreenSpaceRefraction ||
|
||||
ssReflectionsOptions.enabled))) {
|
||||
assert_invariant(!scaled);
|
||||
input = ppm.blit(fg, blendModeTranslucent, input, xvp, {
|
||||
.width = vp.width, .height = vp.height,
|
||||
.format = colorGradingConfig.ldrFormat },
|
||||
@@ -1446,6 +1420,13 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(outputIsSwapChain && view.isStencilBufferEnabled())) {
|
||||
assert_invariant(mSwapChain);
|
||||
FILAMENT_CHECK_PRECONDITION(mSwapChain->hasStencilBuffer())
|
||||
<< "View has stencil buffer enabled, but SwapChain does not have "
|
||||
"SwapChain::CONFIG_HAS_STENCIL_BUFFER flag set.";
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(engine.debug.shadowmap.display_shadow_texture)) {
|
||||
auto shadowmap = blackboard.get<FrameGraphTexture>("shadowmap");
|
||||
input = ppm.debugDisplayShadowTexture(fg, input, shadowmap,
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
@@ -65,10 +64,6 @@ SkinningBuffer::Builder& SkinningBuffer::Builder::name(const char* name, size_t
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
SkinningBuffer::Builder& SkinningBuffer::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
SkinningBuffer* SkinningBuffer::Builder::build(Engine& engine) {
|
||||
return downcast(engine).createSkinningBuffer(*this);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <filament/Stream.h>
|
||||
|
||||
@@ -66,10 +65,6 @@ Stream::Builder& Stream::Builder::name(const char* name, size_t const len) noexc
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
Stream::Builder& Stream::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
Stream* Stream::Builder::build(Engine& engine) {
|
||||
return downcast(engine).createStream(*this);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Panic.h>
|
||||
@@ -154,10 +152,6 @@ Texture::Builder& Texture::Builder::name(const char* name, size_t const len) noe
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
Texture::Builder& Texture::Builder::name(StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
Texture* Texture::Builder::build(Engine& engine) {
|
||||
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
|
||||
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/StaticString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
@@ -153,10 +152,6 @@ VertexBuffer::Builder& VertexBuffer::Builder::name(const char* name, size_t cons
|
||||
return BuilderNameMixin::name(name, len);
|
||||
}
|
||||
|
||||
VertexBuffer::Builder& VertexBuffer::Builder::name(utils::StaticString const& name) noexcept {
|
||||
return BuilderNameMixin::name(name);
|
||||
}
|
||||
|
||||
VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mVertexCount > 0) << "vertexCount cannot be 0";
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount > 0) << "bufferCount cannot be 0";
|
||||
|
||||
@@ -10,11 +10,6 @@ material {
|
||||
type : float4,
|
||||
name : viewport,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : viewportInfo,
|
||||
precision: high
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
@@ -27,8 +22,8 @@ material {
|
||||
|
||||
vertex {
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
|
||||
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy);
|
||||
postProcess.normalizedUV = uvToRenderTargetUV(postProcess.normalizedUV);
|
||||
postProcess.vertex.xy = postProcess.normalizedUV;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +38,7 @@ fragment {
|
||||
#define gather textureGather
|
||||
#else
|
||||
vec4 gather(const mediump sampler2D color, highp vec2 p, const int comp) {
|
||||
highp ivec2 i = ivec2(p * materialParams.viewportInfo.zw - 0.5);
|
||||
highp ivec2 i = ivec2(p * materialParams.viewport.zw - 0.5);
|
||||
vec4 d;
|
||||
d[0] = texelFetchOffset(color, i, 0, ivec2(0, 1))[comp];
|
||||
d[1] = texelFetchOffset(color, i, 0, ivec2(1, 1))[comp];
|
||||
@@ -79,7 +74,7 @@ fragment {
|
||||
#include "sgsr1_shader_mobile.fs"
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec4 vp[1] = vec4[1]( materialParams.viewportInfo );
|
||||
highp vec4 vp[1] = vec4[1]( materialParams.viewport );
|
||||
postProcess.color = sgsr(materialParams_color, variable_vertex.xy, vp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.59.0"
|
||||
spec.version = "1.58.1"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.0/filament-v1.59.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.1/filament-v1.58.1-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 59;
|
||||
static constexpr size_t MATERIAL_VERSION = 58;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -517,13 +517,28 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
|
||||
MetalArgumentBuffer::destroy(&argBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t> &spirv) {
|
||||
void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t>& spirv) {
|
||||
constexpr size_t HEADER_SIZE = 5;
|
||||
size_t const dataSize = spirv.size();
|
||||
uint32_t *data = spirv.data();
|
||||
uint32_t* data = spirv.data();
|
||||
|
||||
std::set<uint32_t> samplerTargetIDs;
|
||||
struct Descriptor {
|
||||
uint32_t binding = 0xFFFFFFFF;
|
||||
bool fixed = false;
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, Descriptor> descriptors;
|
||||
std::set<uint32_t> unidentifiedLoadTargets;
|
||||
std::vector<uint32_t> bindingTargets;
|
||||
|
||||
auto getDesc = [&](uint32_t var) -> Descriptor& {
|
||||
using itertype = std::unordered_map<uint32_t, Descriptor>::iterator;
|
||||
itertype itr;
|
||||
if (itr = descriptors.find(var); itr == descriptors.end()) {
|
||||
itr = descriptors.insert({var, {}}).first;
|
||||
}
|
||||
return itr->second;
|
||||
};
|
||||
|
||||
auto pass = [&](uint32_t targetOp, std::function<void(uint32_t)> f) {
|
||||
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
|
||||
@@ -531,35 +546,33 @@ void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t> &spirv)
|
||||
uint32_t const wordCount = firstWord >> 16;
|
||||
uint32_t const op = firstWord & 0x0000FFFF;
|
||||
if (targetOp == op) {
|
||||
f(cursor + 1);
|
||||
f(cursor+1);
|
||||
}
|
||||
cursor += wordCount;
|
||||
}
|
||||
};
|
||||
|
||||
//Parse through debug name info to determine which bindings are samplers and which are not.
|
||||
// This is possible because the sampler splitting pass outputs sampler and texture pairs of the form:
|
||||
// `uniform sampler2D var_x` => `uniform sampler var_sampler` and `uniform texture2D var_texture`;
|
||||
// TODO: This works, but may limit what optimizations can be done and has the potential to collide with user
|
||||
// variable names. Ideally, trace usage to determine binding type.
|
||||
uint32_t samplerTypeId = 0;
|
||||
pass(spv::Op::OpName, [&](uint32_t pos) {
|
||||
auto target = data[pos];
|
||||
char *name = (char *) &data[pos + 1];
|
||||
char* name = (char*)&data[pos+1];
|
||||
std::string_view view(name);
|
||||
if (view.find("_sampler") != std::string_view::npos) {
|
||||
samplerTargetIDs.insert(target);
|
||||
unidentifiedLoadTargets.insert(target);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Write out the offset bindings
|
||||
|
||||
// Write out the offsetted bindings
|
||||
pass(spv::Op::OpDecorate, [&](uint32_t pos) {
|
||||
uint32_t const type = data[pos + 1];
|
||||
if (type == spv::Decoration::DecorationBinding) {
|
||||
uint32_t const targetVar = data[pos];
|
||||
if (samplerTargetIDs.find(targetVar) != samplerTargetIDs.end()) {
|
||||
data[pos + 2] = data[pos + 2] * 2 + 1;
|
||||
} else {
|
||||
data[pos + 2] = data[pos + 2] * 2;
|
||||
if (unidentifiedLoadTargets.find(targetVar) != unidentifiedLoadTargets.end()) {
|
||||
data[pos + 2] = data[pos + 2]*2 + 1;
|
||||
}
|
||||
else {
|
||||
data[pos + 2] = data[pos + 2] *2;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -570,9 +583,12 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
//We need to run some opt-passes at all times to transpile to WGSL
|
||||
auto optimizer = createEmptyOptimizer();
|
||||
optimizer->RegisterPass(CreateSplitCombinedImageSamplerPass());
|
||||
//optimizer->RegisterPass(CreateResolveBindingConflictsPass());
|
||||
|
||||
optimizeSpirv(optimizer, *spirv);
|
||||
|
||||
//After splitting the image samplers, we need to remap the bindings to separate them.
|
||||
//TODO: This has issues, so use CreateResolveBindingConflictsPass instead. Mapping is less predictable but short term
|
||||
rebindImageSamplerForWGSL(*spirv);
|
||||
|
||||
//Allow non-uniform derivitives due to our nested shaders. See https://github.com/gpuweb/gpuweb/issues/3479
|
||||
@@ -595,10 +611,8 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_COLOR,
|
||||
&text,
|
||||
&diagnostic);
|
||||
slog.e << "Beginning SpirV-output dump with ret " << result << "\n\n" << text->str << "\n\nEndSPIRV\n" <<
|
||||
io::endl;
|
||||
slog.e << "Beginning SpirV-output dump\n\n" << text->str << "\n\nEndSPIRV\n" << io::endl;
|
||||
spvTextDestroy(text);
|
||||
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
@@ -608,7 +622,7 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
tint::SuccessType tintSuccess;
|
||||
|
||||
if (wgslOut != tintSuccess) {
|
||||
slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl;
|
||||
slog.e << "Tint writer error: " << wgslOut.Failure().reason.Str() << io::endl;
|
||||
return false;
|
||||
}
|
||||
*outWsl = wgslOut->wgsl;
|
||||
@@ -1020,9 +1034,6 @@ void GLSLPostProcessor::fixupClipDistance(
|
||||
// - triggers a segfault with AMD OpenGL drivers on macOS
|
||||
// - triggers a crash on some Adreno drivers (b/291140208, b/289401984, b/289393290)
|
||||
// However Metal requires this pass in order to correctly generate half-precision MSL
|
||||
// CreateMergeReturnPass() also creates issues with Tint conversion related to the
|
||||
// bitwise "<<" Operator used in shaders/src/surface_light_directional.fs against
|
||||
// a signed integer.
|
||||
//
|
||||
// CreateSimplificationPass() creates a lot of problems:
|
||||
// - Adreno GPU show artifacts after running simplification passes (Vulkan)
|
||||
@@ -1033,11 +1044,6 @@ void GLSLPostProcessor::fixupClipDistance(
|
||||
// However, the simplification passes below are necessary when targeting Metal, otherwise the
|
||||
// result is mismatched half / float assignments in MSL.
|
||||
|
||||
// CreateInlineExhaustivePass() expects CreateMergeReturnPass() to be run beforehand
|
||||
// (Throwing many warnings if this is not the case), but we don't consistently do so for the above
|
||||
// reasons. While running it alone may have some value, we will disable it for the new WebGPU backend
|
||||
// while minimizing other changes.
|
||||
|
||||
|
||||
void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config const& config) {
|
||||
auto RegisterPass = [&](spvtools::Optimizer::PassToken&& pass,
|
||||
@@ -1050,8 +1056,8 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
|
||||
|
||||
RegisterPass(CreateWrapOpKillPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
RegisterPass(CreateMergeReturnPass(), MaterialBuilder::TargetApi::METAL);
|
||||
RegisterPass(CreateInlineExhaustivePass(), MaterialBuilder::TargetApi::ALL & ~MaterialBuilder::TargetApi::WEBGPU);
|
||||
RegisterPass(CreateMergeReturnPass(), MaterialBuilder::TargetApi::METAL | MaterialBuilder::TargetApi::WEBGPU);
|
||||
RegisterPass(CreateInlineExhaustivePass());
|
||||
RegisterPass(CreateAggressiveDCEPass());
|
||||
RegisterPass(CreatePrivateToLocalPass());
|
||||
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
|
||||
@@ -1095,8 +1101,7 @@ void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& c
|
||||
|
||||
RegisterPass(CreateWrapOpKillPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
// Disable for WebGPU, see comment above registerPerformancePasses()
|
||||
RegisterPass(CreateInlineExhaustivePass(), MaterialBuilder::TargetApi::ALL & ~MaterialBuilder::TargetApi::WEBGPU);
|
||||
RegisterPass(CreateInlineExhaustivePass());
|
||||
RegisterPass(CreateEliminateDeadFunctionsPass());
|
||||
RegisterPass(CreatePrivateToLocalPass());
|
||||
RegisterPass(CreateScalarReplacementPass(0));
|
||||
|
||||
@@ -37,7 +37,6 @@ set(DIST_HDRS
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/PrivateImplementation-impl.h
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/SingleInstanceComponentManager.h
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/Slice.h
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/StaticString.h
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/StructureOfArrays.h
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/Systrace.h
|
||||
${PUBLIC_HDR_DIR}/${TARGET}/sstream.h
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -199,13 +197,16 @@ private:
|
||||
};
|
||||
|
||||
int compare(const CString& rhs) const noexcept {
|
||||
auto const l = std::string_view{data(), size()};
|
||||
auto const r = std::string_view{rhs.data(), rhs.size()};
|
||||
return l.compare(r);
|
||||
size_type const lhs_size = size();
|
||||
size_type const rhs_size = rhs.size();
|
||||
if (lhs_size < rhs_size) return -1;
|
||||
if (lhs_size > rhs_size) return 1;
|
||||
return strncmp(data(), rhs.data(), size());
|
||||
}
|
||||
|
||||
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
|
||||
return lhs.compare(rhs) == 0;
|
||||
return (lhs.data() == rhs.data()) ||
|
||||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
|
||||
}
|
||||
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_STATICSTRING_H
|
||||
#define TNT_UTILS_STATICSTRING_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <string_view>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* @brief A lightweight string class that stores a pointer to a string literal and its size, without dynamic allocation.
|
||||
*
|
||||
* This class is designed to efficiently represent string literals. It does not allocate any memory
|
||||
* and instead relies on the compiler to manage the memory of the string literals.
|
||||
*/
|
||||
class UTILS_PUBLIC StaticString {
|
||||
public:
|
||||
using value_type = std::string_view::value_type;
|
||||
using const_pointer = std::string_view::const_pointer;
|
||||
using const_reference = std::string_view::const_reference;
|
||||
using size_type = std::string_view::size_type;
|
||||
using const_iterator = std::string_view::const_iterator;
|
||||
|
||||
// Constructor from string literal
|
||||
template <size_t M>
|
||||
constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor)
|
||||
|
||||
constexpr StaticString() noexcept = default;
|
||||
|
||||
constexpr const_pointer c_str() const noexcept { return mString.data(); }
|
||||
constexpr const_pointer data() const noexcept { return mString.data(); }
|
||||
constexpr size_type size() const noexcept { return mString.size(); }
|
||||
constexpr size_type length() const noexcept { return mString.size(); }
|
||||
constexpr bool empty() const noexcept { return mString.empty(); }
|
||||
|
||||
constexpr const_iterator begin() const noexcept { return mString.begin(); }
|
||||
constexpr const_iterator end() const noexcept { return mString.end(); }
|
||||
constexpr const_iterator cbegin() const noexcept { return mString.begin(); }
|
||||
constexpr const_iterator cend() const noexcept { return mString.end(); }
|
||||
|
||||
constexpr const_reference operator[](size_type const pos) const noexcept {
|
||||
return mString[pos];
|
||||
}
|
||||
|
||||
constexpr const_reference at(size_type pos) const {
|
||||
return mString[pos];
|
||||
}
|
||||
|
||||
constexpr const_reference front() const noexcept {
|
||||
return mString.front();
|
||||
}
|
||||
|
||||
constexpr const_reference back() const noexcept {
|
||||
return mString.back();
|
||||
}
|
||||
|
||||
constexpr int compare(const StaticString& rhs) const noexcept {
|
||||
return mString.compare(rhs.mString);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string_view mString;
|
||||
|
||||
friend constexpr bool operator==(const StaticString& lhs, const StaticString& rhs) noexcept {
|
||||
return lhs.mString == rhs.mString;
|
||||
}
|
||||
|
||||
friend constexpr bool operator!=(const StaticString& lhs, const StaticString& rhs) noexcept {
|
||||
return lhs.mString != rhs.mString;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<(const StaticString& lhs, const StaticString& rhs) noexcept {
|
||||
return lhs.mString < rhs.mString;
|
||||
}
|
||||
|
||||
friend constexpr bool operator>(const StaticString& lhs, const StaticString& rhs) noexcept {
|
||||
return lhs.mString > rhs.mString;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<=(const StaticString& lhs, const StaticString& rhs) noexcept {
|
||||
return lhs.mString <= rhs.mString;
|
||||
}
|
||||
|
||||
friend constexpr bool operator>=(const StaticString& lhs, const StaticString& rhs) noexcept {
|
||||
return lhs.mString >= rhs.mString;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_STATICSTRING_H
|
||||
@@ -129,14 +129,14 @@ bool isDoubleSided() {
|
||||
/**
|
||||
* Returns the cascade index for this fragment (between 0 and CONFIG_MAX_SHADOW_CASCADES - 1).
|
||||
*/
|
||||
int getShadowCascade() {
|
||||
uint getShadowCascade() {
|
||||
highp float z = mulMat4x4Float3(getViewFromWorldMatrix(), getWorldPosition()).z;
|
||||
ivec4 greaterZ = ivec4(greaterThan(frameUniforms.cascadeSplits, vec4(z)));
|
||||
int cascadeCount = frameUniforms.cascades & 0xF;
|
||||
uint cascadeCount = frameUniforms.cascades & 0xF;
|
||||
return clamp(greaterZ.x + greaterZ.y + greaterZ.z + greaterZ.w, 0, cascadeCount - 1);
|
||||
}
|
||||
|
||||
highp vec4 getCascadeLightSpacePosition(int cascade) {
|
||||
highp vec4 getCascadeLightSpacePosition(uint cascade) {
|
||||
// For the first cascade, return the interpolated light space position.
|
||||
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
|
||||
// the matrix multiply inside computeLightSpacePosition.
|
||||
|
||||
@@ -52,12 +52,12 @@ void evaluateDirectionalLight(const MaterialInputs material,
|
||||
if (light.NoL > 0.0) {
|
||||
float ssContactShadowOcclusion = 0.0;
|
||||
|
||||
int cascade = getShadowCascade();
|
||||
uint cascade = getShadowCascade();
|
||||
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
|
||||
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
|
||||
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
|
||||
highp vec4 shadowPosition = getShadowPosition(cascade);
|
||||
visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0);
|
||||
visibility = shadow(true, sampler0_shadowMap, int(cascade), shadowPosition, 0.0);
|
||||
#if defined(MATERIAL_HAS_SHADOW_STRENGTH)
|
||||
applyShadowStrength(visibility, material.shadowStrength);
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,7 @@ vec4 evaluateMaterial(const MaterialInputs material) {
|
||||
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
|
||||
#if defined(VARIANT_HAS_SHADOWING)
|
||||
float visibility = 1.0;
|
||||
int cascade = getShadowCascade();
|
||||
uint cascade = getShadowCascade();
|
||||
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1 << cascade) << 8));
|
||||
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1);
|
||||
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
|
||||
|
||||
@@ -485,7 +485,7 @@ float ShadowSample_VSM(const bool ELVSM, const highp sampler2DArray shadowMap,
|
||||
|
||||
// get texture coordinate for directional and spot shadow maps
|
||||
#if defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
|
||||
highp vec4 getShadowPosition(const int cascade) {
|
||||
highp vec4 getShadowPosition(const uint cascade) {
|
||||
return getCascadeLightSpacePosition(cascade);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Bzlmod lockfile
|
||||
MODULE.bazel.lock
|
||||
# Ignore all bazel-* symlinks.
|
||||
/bazel-*
|
||||
# Ignore Bazel verbose explanations
|
||||
--verbose_explanations
|
||||
# Ignore CMake usual build directory
|
||||
build
|
||||
# Ignore Vim files
|
||||
*.swp
|
||||
# Ignore QtCreator Project file
|
||||
CMakeLists.txt.user
|
||||
# Ignore VS Code files
|
||||
.vscode/*
|
||||
# Ignore generated python artifacts
|
||||
*.pyc
|
||||
copts/__pycache__/
|
||||
Vendored
+374
@@ -0,0 +1,374 @@
|
||||
# Copyright 2018 The Chromium Authors
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
# Flags specified here must not impact ABI. Code compiled with and without these
|
||||
# opts will be linked together, and in some cases headers compiled with and
|
||||
# without these options will be part of the same program.
|
||||
|
||||
import("//build/config/c++/c++.gni")
|
||||
import("//build/config/nacl/config.gni")
|
||||
import("//build/config/sanitizers/sanitizers.gni")
|
||||
import("//build/toolchain/toolchain.gni")
|
||||
import("//build_overrides/build.gni")
|
||||
import("//third_party/abseil-cpp/absl.gni")
|
||||
|
||||
config("absl_component_build") {
|
||||
defines = [ "ABSL_CONSUME_DLL" ]
|
||||
}
|
||||
|
||||
assert(!is_nacl || is_nacl_saigo,
|
||||
"base must not be built in most nacl toolchains")
|
||||
|
||||
component("absl") {
|
||||
public_deps = [ ":absl_component_deps" ]
|
||||
if (is_component_build) {
|
||||
public_configs = [ ":absl_component_build" ]
|
||||
|
||||
if (is_win && is_clang) {
|
||||
if (current_cpu == "x64") {
|
||||
if (is_debug) {
|
||||
sources = [ "symbols_x64_dbg.def" ]
|
||||
} else {
|
||||
if (is_asan) {
|
||||
sources = [ "symbols_x64_rel_asan.def" ]
|
||||
} else {
|
||||
sources = [ "symbols_x64_rel.def" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current_cpu == "x86") {
|
||||
if (is_debug) {
|
||||
sources = [ "symbols_x86_dbg.def" ]
|
||||
} else {
|
||||
sources = [ "symbols_x86_rel.def" ]
|
||||
}
|
||||
}
|
||||
if (current_cpu == "arm64") {
|
||||
if (is_debug) {
|
||||
sources = [ "symbols_arm64_dbg.def" ]
|
||||
} else {
|
||||
sources = [ "symbols_arm64_rel.def" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group("absl_component_deps") {
|
||||
public_deps = [
|
||||
"//third_party/abseil-cpp/absl/algorithm:container",
|
||||
"//third_party/abseil-cpp/absl/base",
|
||||
"//third_party/abseil-cpp/absl/base:config",
|
||||
"//third_party/abseil-cpp/absl/base:core_headers",
|
||||
"//third_party/abseil-cpp/absl/base:no_destructor",
|
||||
"//third_party/abseil-cpp/absl/base:nullability",
|
||||
"//third_party/abseil-cpp/absl/base:prefetch",
|
||||
"//third_party/abseil-cpp/absl/cleanup",
|
||||
"//third_party/abseil-cpp/absl/container:btree",
|
||||
"//third_party/abseil-cpp/absl/container:fixed_array",
|
||||
"//third_party/abseil-cpp/absl/container:flat_hash_map",
|
||||
"//third_party/abseil-cpp/absl/container:flat_hash_set",
|
||||
"//third_party/abseil-cpp/absl/container:inlined_vector",
|
||||
"//third_party/abseil-cpp/absl/container:node_hash_map",
|
||||
"//third_party/abseil-cpp/absl/container:node_hash_set",
|
||||
"//third_party/abseil-cpp/absl/debugging:failure_signal_handler",
|
||||
"//third_party/abseil-cpp/absl/debugging:stacktrace",
|
||||
"//third_party/abseil-cpp/absl/debugging:symbolize",
|
||||
"//third_party/abseil-cpp/absl/functional:any_invocable",
|
||||
"//third_party/abseil-cpp/absl/functional:bind_front",
|
||||
"//third_party/abseil-cpp/absl/functional:function_ref",
|
||||
"//third_party/abseil-cpp/absl/functional:overload",
|
||||
"//third_party/abseil-cpp/absl/hash",
|
||||
"//third_party/abseil-cpp/absl/log:absl_check",
|
||||
"//third_party/abseil-cpp/absl/log:absl_log",
|
||||
"//third_party/abseil-cpp/absl/log:die_if_null",
|
||||
"//third_party/abseil-cpp/absl/memory",
|
||||
"//third_party/abseil-cpp/absl/meta:type_traits",
|
||||
"//third_party/abseil-cpp/absl/numeric:bits",
|
||||
"//third_party/abseil-cpp/absl/numeric:int128",
|
||||
"//third_party/abseil-cpp/absl/random",
|
||||
"//third_party/abseil-cpp/absl/random:distributions",
|
||||
"//third_party/abseil-cpp/absl/status",
|
||||
"//third_party/abseil-cpp/absl/status:statusor",
|
||||
"//third_party/abseil-cpp/absl/strings",
|
||||
"//third_party/abseil-cpp/absl/strings:cord",
|
||||
"//third_party/abseil-cpp/absl/strings:has_ostream_operator",
|
||||
"//third_party/abseil-cpp/absl/strings:str_format",
|
||||
"//third_party/abseil-cpp/absl/strings:string_view",
|
||||
"//third_party/abseil-cpp/absl/synchronization",
|
||||
"//third_party/abseil-cpp/absl/time",
|
||||
"//third_party/abseil-cpp/absl/types:optional",
|
||||
"//third_party/abseil-cpp/absl/types:span",
|
||||
"//third_party/abseil-cpp/absl/types:variant",
|
||||
"//third_party/abseil-cpp/absl/utility",
|
||||
]
|
||||
|
||||
if (is_component_build) {
|
||||
public_deps += [ ":absl_full_deps" ]
|
||||
}
|
||||
|
||||
# The following dependencies currently don't build with NaCl.
|
||||
# TODO(https://crbug.com/1114625): Fix build errors and remove this section.
|
||||
if (is_nacl) {
|
||||
public_deps -=
|
||||
[ "//third_party/abseil-cpp/absl/debugging:failure_signal_handler" ]
|
||||
}
|
||||
|
||||
visibility = [ ":absl" ]
|
||||
}
|
||||
|
||||
# Some third-party libraries use additional parts of absl that are banned in
|
||||
# Chrome (e.g. flags generates static initializers that are undesirable in
|
||||
# non-test binaries). These third-party libraries can depend on `absl_full`
|
||||
# instead of `absl` to use those banned subsets of Abseil as well.
|
||||
source_set("absl_full") {
|
||||
public_deps = [ ":absl" ]
|
||||
|
||||
# In component builds, `absl` itself depends on `absl_full_deps` directly so
|
||||
# that all of absl is built as a single shared library. This is to prevent
|
||||
# globals from being duplicated between both `absl` and `absl_full` (e.g.
|
||||
# inline variables), and the subtle breakage that could cause.
|
||||
#
|
||||
# Deps violations will still be enforced by the non-component build.
|
||||
if (!is_component_build) {
|
||||
public_deps += [ ":absl_full_deps" ]
|
||||
}
|
||||
|
||||
if (build_with_chromium) {
|
||||
visibility = [
|
||||
"//third_party/fuzztest:*",
|
||||
|
||||
# GoogleTest doesn't actually need absl_full, but this makes gn check
|
||||
# happier.
|
||||
"//third_party/googletest:*",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
group("absl_full_deps") {
|
||||
public_deps = [
|
||||
# Banned because each flag registration generates a static initializer, and
|
||||
# the flag registration mechanism is problematic in component builds.
|
||||
"//third_party/abseil-cpp/absl/flags:config",
|
||||
"//third_party/abseil-cpp/absl/flags:flag",
|
||||
"//third_party/abseil-cpp/absl/flags:parse",
|
||||
"//third_party/abseil-cpp/absl/flags:reflection",
|
||||
"//third_party/abseil-cpp/absl/flags:usage",
|
||||
|
||||
# Banned because the LOG and CHECK macros conflict with Chrome's version of
|
||||
# those macros.
|
||||
"//third_party/abseil-cpp/absl/log",
|
||||
"//third_party/abseil-cpp/absl/log:check",
|
||||
|
||||
# TODO(danilchap): Investigate if these targets need to be banned in 3rd party library code.
|
||||
"//third_party/abseil-cpp/absl/log:globals",
|
||||
"//third_party/abseil-cpp/absl/log:initialize",
|
||||
"//third_party/abseil-cpp/absl/random:bit_gen_ref",
|
||||
]
|
||||
|
||||
visibility = [
|
||||
":absl_component_deps",
|
||||
":absl_full",
|
||||
]
|
||||
|
||||
public_configs = [ "//third_party/abseil-cpp/absl/flags:absl_flags_config" ]
|
||||
}
|
||||
|
||||
config("absl_include_config") {
|
||||
include_dirs = [ "." ]
|
||||
}
|
||||
|
||||
config("absl_define_config") {
|
||||
defines = [ "ABSL_ALLOCATOR_NOTHROW=1" ]
|
||||
|
||||
if (is_win && !use_custom_libcxx) {
|
||||
defines += [
|
||||
# See crbug.com/1101367: Acknowledge extended alignment when using
|
||||
# MSVC's standard library.
|
||||
"_ENABLE_EXTENDED_ALIGNED_STORAGE",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
config("absl_default_cflags_cc") {
|
||||
cflags_cc = []
|
||||
if (is_clang) {
|
||||
cflags_cc += [
|
||||
# TODO(crbug.com/588506): Explicitly enable conversion warnings.
|
||||
"-Wbool-conversion",
|
||||
"-Wconstant-conversion",
|
||||
"-Wenum-conversion",
|
||||
"-Wint-conversion",
|
||||
"-Wliteral-conversion",
|
||||
"-Wnon-literal-null-conversion",
|
||||
"-Wnull-conversion",
|
||||
"-Wobjc-literal-conversion",
|
||||
"-Wstring-conversion",
|
||||
]
|
||||
if (!is_nacl) {
|
||||
cflags_cc += [ "-Wbitfield-enum-conversion" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config("absl_public_cflags_cc") {
|
||||
# Non-chromium users of abseil have more lax style guides and use additional features in
|
||||
# abseil, Remove a few extra warnings for that use case.
|
||||
if (!build_with_chromium && is_clang) {
|
||||
cflags = [
|
||||
# Allow the use of enable_if()
|
||||
"-Wno-gcc-compat",
|
||||
"-Wno-unreachable-code-break",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
config("absl_test_cflags_cc") {
|
||||
cflags_cc = []
|
||||
if (is_clang || !is_win) {
|
||||
cflags_cc += [
|
||||
"-Wno-conversion-null",
|
||||
"-Wno-missing-declarations",
|
||||
"-Wno-unused-function",
|
||||
"-Wno-unused-parameter",
|
||||
"-Wno-unused-private-field",
|
||||
]
|
||||
}
|
||||
if (is_win) {
|
||||
cflags_cc += [
|
||||
"/wd4101", # unreferenced local variable
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
config("absl_test_config") {
|
||||
cflags_cc = []
|
||||
if (is_win && !is_clang) {
|
||||
cflags_cc += [ "/wd4996" ]
|
||||
}
|
||||
if (is_clang) {
|
||||
cflags_cc += [
|
||||
"-Wno-deprecated-declarations",
|
||||
"-Wno-implicit-const-int-float-conversion",
|
||||
"-Wno-unused-private-field",
|
||||
"-Wno-unused-function",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (absl_build_tests) {
|
||||
import("//testing/test.gni")
|
||||
|
||||
if (!is_component_build) {
|
||||
test("absl_tests") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"absl/algorithm:algorithm_test",
|
||||
"absl/algorithm:container_test",
|
||||
"absl/base:config_test",
|
||||
"absl/base:no_destructor_test",
|
||||
"absl/base:nullability_test",
|
||||
"absl/base:prefetch_test",
|
||||
"absl/cleanup:cleanup_test",
|
||||
|
||||
# TODO(mbonadei): Fix issue with EXPECT_DEATH and uncomment.
|
||||
# "absl/container:btree_test",
|
||||
"absl/container:common_policy_traits_test",
|
||||
"absl/container:fixed_array_test",
|
||||
"absl/container:flat_hash_map_test",
|
||||
"absl/container:flat_hash_set_test",
|
||||
"absl/container:hash_function_defaults_test",
|
||||
"absl/container:inlined_vector_test",
|
||||
"absl/container:node_slot_policy_test",
|
||||
"absl/container:raw_hash_set_allocator_test",
|
||||
"absl/container:raw_hash_set_test",
|
||||
"absl/container:sample_element_size_test",
|
||||
"absl/crc:crc32c_test",
|
||||
"absl/crc:crc_cord_state_test",
|
||||
"absl/crc:crc_memcpy_test",
|
||||
"absl/crc:non_temporal_memcpy_test",
|
||||
"absl/debugging:demangle_rust_test",
|
||||
"absl/debugging:stacktrace_test",
|
||||
"absl/flags:flag_test",
|
||||
"absl/functional:any_invocable_test",
|
||||
"absl/functional:function_ref_test",
|
||||
"absl/functional:overload_test",
|
||||
"absl/hash:hash_instantiated_test",
|
||||
"absl/hash:hash_test",
|
||||
"absl/hash:low_level_hash_test",
|
||||
"absl/log:absl_check_test",
|
||||
"absl/log:absl_log_basic_test",
|
||||
"absl/log:die_if_null_test",
|
||||
"absl/log:flags_test",
|
||||
"absl/log:globals_test",
|
||||
"absl/log:log_entry_test",
|
||||
"absl/log:log_format_test",
|
||||
"absl/log:log_macro_hygiene_test",
|
||||
"absl/log:log_modifier_methods_test",
|
||||
"absl/log:log_sink_test",
|
||||
"absl/log:log_streamer_test",
|
||||
"absl/log:scoped_mock_log_test",
|
||||
"absl/log:stripping_test",
|
||||
"absl/log:vlog_is_on_test",
|
||||
"absl/log/internal:fnmatch_test",
|
||||
"absl/log/internal:stderr_log_sink_test",
|
||||
"absl/memory:memory_test",
|
||||
"absl/meta:type_traits_test",
|
||||
"absl/numeric:int128_test",
|
||||
"absl/profiling:exponential_biased_test",
|
||||
"absl/profiling:periodic_sampler_test",
|
||||
"absl/status:status_matchers_test",
|
||||
"absl/status:status_test",
|
||||
"absl/status:statusor_test",
|
||||
"absl/strings:ascii_test",
|
||||
"absl/strings:char_formatting_test",
|
||||
"absl/strings:charset_test",
|
||||
"absl/strings:cord_buffer_test",
|
||||
"absl/strings:cord_data_edge_test",
|
||||
"absl/strings:cord_rep_btree_navigator_test",
|
||||
"absl/strings:cord_rep_btree_reader_test",
|
||||
"absl/strings:cord_rep_btree_test",
|
||||
"absl/strings:cord_rep_crc_test",
|
||||
"absl/strings:cordz_functions_test",
|
||||
"absl/strings:cordz_info_statistics_test",
|
||||
"absl/strings:cordz_info_test",
|
||||
"absl/strings:cordz_test",
|
||||
"absl/strings:cordz_update_scope_test",
|
||||
"absl/strings:cordz_update_tracker_test",
|
||||
"absl/strings:damerau_levenshtein_distance_test",
|
||||
"absl/strings:has_absl_stringify_test",
|
||||
"absl/strings:has_ostream_operator_test",
|
||||
"absl/strings:match_test",
|
||||
"absl/strings:str_format_arg_test",
|
||||
"absl/strings:str_format_bind_test",
|
||||
"absl/strings:str_format_checker_test",
|
||||
"absl/strings:str_format_convert_test",
|
||||
"absl/strings:str_format_extension_test",
|
||||
"absl/strings:str_format_output_test",
|
||||
"absl/strings:str_format_parser_test",
|
||||
"absl/strings:str_format_test",
|
||||
"absl/strings:str_replace_test",
|
||||
"absl/strings:string_view_test",
|
||||
"absl/synchronization:kernel_timeout_internal_test",
|
||||
"absl/synchronization:waiter_test",
|
||||
"absl/time:time_test",
|
||||
"absl/types:optional_test",
|
||||
"absl/types:span_test",
|
||||
"absl/types:variant_test",
|
||||
"absl/utility:if_constexpr_test",
|
||||
"//third_party/googletest:gtest_main",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
test("absl_hardening_tests") {
|
||||
testonly = true
|
||||
sources = [ "absl_hardening_test.cc" ]
|
||||
deps = [
|
||||
":absl",
|
||||
"//base/test:run_all_unittests",
|
||||
"//third_party/googletest:gtest",
|
||||
]
|
||||
}
|
||||
}
|
||||
+15
-87
@@ -15,7 +15,6 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"base/internal/cycleclock.h"
|
||||
"base/internal/cycleclock_config.h"
|
||||
"base/internal/direct_mmap.h"
|
||||
"base/internal/dynamic_annotations.h"
|
||||
"base/internal/endian.h"
|
||||
"base/internal/errno_saver.h"
|
||||
"base/internal/fast_type_id.h"
|
||||
@@ -28,8 +27,6 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"base/internal/low_level_scheduling.h"
|
||||
"base/internal/nullability_impl.h"
|
||||
"base/internal/per_thread_tls.h"
|
||||
"base/internal/poison.cc"
|
||||
"base/internal/poison.h"
|
||||
"base/prefetch.h"
|
||||
"base/internal/pretty_function.h"
|
||||
"base/internal/raw_logging.cc"
|
||||
@@ -49,8 +46,6 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"base/internal/thread_identity.h"
|
||||
"base/internal/throw_delegate.cc"
|
||||
"base/internal/throw_delegate.h"
|
||||
"base/internal/tracing.cc"
|
||||
"base/internal/tracing.h"
|
||||
"base/internal/tsan_mutex_interface.h"
|
||||
"base/internal/unaligned_access.h"
|
||||
"base/internal/unscaledcycleclock.cc"
|
||||
@@ -126,9 +121,6 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"debugging/symbolize.h"
|
||||
"debugging/internal/address_is_readable.cc"
|
||||
"debugging/internal/address_is_readable.h"
|
||||
"debugging/internal/bounded_utf8_length_sequence.h"
|
||||
"debugging/internal/decode_rust_punycode.cc"
|
||||
"debugging/internal/decode_rust_punycode.h"
|
||||
"debugging/internal/demangle.cc"
|
||||
"debugging/internal/demangle.h"
|
||||
"debugging/internal/demangle_rust.cc"
|
||||
@@ -141,8 +133,6 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"debugging/internal/stack_consumption.h"
|
||||
"debugging/internal/stacktrace_config.h"
|
||||
"debugging/internal/symbolize.h"
|
||||
"debugging/internal/utf8_for_code_point.cc"
|
||||
"debugging/internal/utf8_for_code_point.h"
|
||||
"debugging/internal/vdso_support.cc"
|
||||
"debugging/internal/vdso_support.h"
|
||||
"functional/any_invocable.h"
|
||||
@@ -193,8 +183,6 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"log/internal/proto.cc"
|
||||
"log/internal/strip.h"
|
||||
"log/internal/structured.h"
|
||||
"log/internal/structured_proto.cc"
|
||||
"log/internal/structured_proto.h"
|
||||
"log/internal/vlog_config.cc"
|
||||
"log/internal/vlog_config.h"
|
||||
"log/internal/voidify.h"
|
||||
@@ -325,6 +313,7 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"strings/internal/string_constant.h"
|
||||
"strings/internal/stringify_sink.h"
|
||||
"strings/internal/stringify_sink.cc"
|
||||
"strings/internal/has_absl_stringify.h"
|
||||
"strings/has_absl_stringify.h"
|
||||
"strings/has_ostream_operator.h"
|
||||
"strings/match.cc"
|
||||
@@ -450,47 +439,9 @@ set(ABSL_INTERNAL_DLL_FILES
|
||||
"debugging/leak_check.cc"
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
list(APPEND ABSL_INTERNAL_DLL_FILES
|
||||
"flags/commandlineflag.cc"
|
||||
"flags/commandlineflag.h"
|
||||
"flags/config.h"
|
||||
"flags/declare.h"
|
||||
"flags/flag.h"
|
||||
"flags/internal/commandlineflag.cc"
|
||||
"flags/internal/commandlineflag.h"
|
||||
"flags/internal/flag.cc"
|
||||
"flags/internal/flag.h"
|
||||
"flags/internal/parse.h"
|
||||
"flags/internal/path_util.h"
|
||||
"flags/internal/private_handle_accessor.cc"
|
||||
"flags/internal/private_handle_accessor.h"
|
||||
"flags/internal/program_name.cc"
|
||||
"flags/internal/program_name.h"
|
||||
"flags/internal/registry.h"
|
||||
"flags/internal/sequence_lock.h"
|
||||
"flags/internal/usage.cc"
|
||||
"flags/internal/usage.h"
|
||||
"flags/marshalling.cc"
|
||||
"flags/marshalling.h"
|
||||
"flags/parse.cc"
|
||||
"flags/parse.h"
|
||||
"flags/reflection.cc"
|
||||
"flags/reflection.h"
|
||||
"flags/usage.cc"
|
||||
"flags/usage.h"
|
||||
"flags/usage_config.cc"
|
||||
"flags/usage_config.h"
|
||||
"log/flags.cc"
|
||||
"log/flags.h"
|
||||
"log/internal/flags.h"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(ABSL_INTERNAL_DLL_TARGETS
|
||||
"absl_check"
|
||||
"absl_log"
|
||||
"absl_vlog_is_on"
|
||||
"algorithm"
|
||||
"algorithm_container"
|
||||
"any"
|
||||
@@ -556,7 +507,6 @@ set(ABSL_INTERNAL_DLL_TARGETS
|
||||
"log_internal_check_op"
|
||||
"log_internal_conditions"
|
||||
"log_internal_config"
|
||||
"log_internal_fnmatch"
|
||||
"log_internal_format"
|
||||
"log_internal_globals"
|
||||
"log_internal_log_impl"
|
||||
@@ -636,7 +586,6 @@ set(ABSL_INTERNAL_DLL_TARGETS
|
||||
"strerror"
|
||||
"strings"
|
||||
"strings_internal"
|
||||
"string_view"
|
||||
"symbolize"
|
||||
"synchronization"
|
||||
"thread_pool"
|
||||
@@ -647,30 +596,8 @@ set(ABSL_INTERNAL_DLL_TARGETS
|
||||
"type_traits"
|
||||
"utility"
|
||||
"variant"
|
||||
"vlog_config_internal"
|
||||
"vlog_is_on"
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
list(APPEND ABSL_INTERNAL_DLL_TARGETS
|
||||
"flags"
|
||||
"flags_commandlineflag"
|
||||
"flags_commandlineflag_internal"
|
||||
"flags_config"
|
||||
"flags_internal"
|
||||
"flags_marshalling"
|
||||
"flags_parse"
|
||||
"flags_path_util"
|
||||
"flags_private_handle_accessor"
|
||||
"flags_program_name"
|
||||
"flags_reflection"
|
||||
"flags_usage"
|
||||
"flags_usage_internal"
|
||||
"log_internal_flags"
|
||||
"log_flags"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(ABSL_INTERNAL_TEST_DLL_FILES
|
||||
"hash/hash_testing.h"
|
||||
"log/scoped_mock_log.cc"
|
||||
@@ -747,7 +674,12 @@ function(absl_internal_dll_contains)
|
||||
|
||||
STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_DLL_TARGET})
|
||||
|
||||
if (_target IN_LIST ABSL_INTERNAL_DLL_TARGETS)
|
||||
list(FIND
|
||||
ABSL_INTERNAL_DLL_TARGETS
|
||||
"${_target}"
|
||||
_index)
|
||||
|
||||
if (${_index} GREATER -1)
|
||||
set(${ABSL_INTERNAL_DLL_OUTPUT} 1 PARENT_SCOPE)
|
||||
else()
|
||||
set(${ABSL_INTERNAL_DLL_OUTPUT} 0 PARENT_SCOPE)
|
||||
@@ -764,7 +696,12 @@ function(absl_internal_test_dll_contains)
|
||||
|
||||
STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_TEST_DLL_TARGET})
|
||||
|
||||
if (_target IN_LIST ABSL_INTERNAL_TEST_DLL_TARGETS)
|
||||
list(FIND
|
||||
ABSL_INTERNAL_TEST_DLL_TARGETS
|
||||
"${_target}"
|
||||
_index)
|
||||
|
||||
if (${_index} GREATER -1)
|
||||
set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 1 PARENT_SCOPE)
|
||||
else()
|
||||
set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 0 PARENT_SCOPE)
|
||||
@@ -816,12 +753,7 @@ function(absl_make_dll)
|
||||
else()
|
||||
set(_dll "abseil_dll")
|
||||
set(_dll_files ${ABSL_INTERNAL_DLL_FILES})
|
||||
set(_dll_libs
|
||||
Threads::Threads
|
||||
# TODO(#1495): Use $<LINK_LIBRARY:FRAMEWORK,CoreFoundation> once our
|
||||
# minimum CMake version >= 3.24
|
||||
$<$<PLATFORM_ID:Darwin>:-Wl,-framework,CoreFoundation>
|
||||
)
|
||||
set(_dll_libs "")
|
||||
set(_dll_compile_definitions "")
|
||||
set(_dll_includes "")
|
||||
set(_dll_consume "ABSL_CONSUME_DLL")
|
||||
@@ -838,12 +770,8 @@ function(absl_make_dll)
|
||||
PRIVATE
|
||||
${_dll_libs}
|
||||
${ABSL_DEFAULT_LINKOPTS}
|
||||
$<$<BOOL:${ANDROID}>:-llog>
|
||||
)
|
||||
set_target_properties(${_dll} PROPERTIES
|
||||
LINKER_LANGUAGE "CXX"
|
||||
SOVERSION ${ABSL_SOVERSION}
|
||||
)
|
||||
set_property(TARGET ${_dll} PROPERTY LINKER_LANGUAGE "CXX")
|
||||
target_include_directories(
|
||||
${_dll}
|
||||
PUBLIC
|
||||
|
||||
+9
-16
@@ -186,16 +186,16 @@ function(absl_cc_library)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
set(skip_next_cflag OFF)
|
||||
foreach(cflag ${ABSL_CC_LIB_COPTS})
|
||||
# Strip out the CMake-specific `SHELL:` prefix, which is used to construct
|
||||
# a group of space-separated options.
|
||||
# https://cmake.org/cmake/help/v3.30/command/target_compile_options.html#option-de-duplication
|
||||
string(REGEX REPLACE "^SHELL:" "" cflag "${cflag}")
|
||||
if(${cflag} MATCHES "^-Xarch_")
|
||||
if(skip_next_cflag)
|
||||
set(skip_next_cflag OFF)
|
||||
elseif(${cflag} MATCHES "^-Xarch_")
|
||||
# An -Xarch_ flag implies that its successor only applies to the
|
||||
# specified platform. Such option groups are each specified in a single
|
||||
# `SHELL:`-prefixed string in the COPTS list, which we simply ignore.
|
||||
elseif(${cflag} MATCHES "^(-Wno-|/wd)")
|
||||
# specified platform. Filter both of them out before the successor
|
||||
# reaches the "^-m" filter.
|
||||
set(skip_next_cflag ON)
|
||||
elseif(${cflag} MATCHES "^(-Wno|/wd)")
|
||||
# These flags are needed to suppress warnings that might fire in our headers.
|
||||
set(PC_CFLAGS "${PC_CFLAGS} ${cflag}")
|
||||
elseif(${cflag} MATCHES "^(-W|/w[1234eo])")
|
||||
@@ -258,13 +258,6 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
|
||||
elseif(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
|
||||
add_library(${_NAME} "")
|
||||
target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS})
|
||||
if(APPLE)
|
||||
set_target_properties(${_NAME} PROPERTIES
|
||||
INSTALL_RPATH "@loader_path")
|
||||
elseif(UNIX)
|
||||
set_target_properties(${_NAME} PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN")
|
||||
endif()
|
||||
target_link_libraries(${_NAME}
|
||||
PUBLIC ${ABSL_CC_LIB_DEPS}
|
||||
PRIVATE
|
||||
@@ -313,7 +306,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
|
||||
if(ABSL_ENABLE_INSTALL)
|
||||
set_target_properties(${_NAME} PROPERTIES
|
||||
OUTPUT_NAME "absl_${_NAME}"
|
||||
SOVERSION "${ABSL_SOVERSION}"
|
||||
SOVERSION 0
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user