Compare commits
33
Commits
v1.56.1
...
ma/rainbowgen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
107f25ca75 | ||
|
|
46806735e4 | ||
|
|
c410acb69f | ||
|
|
cb9eba3b3e | ||
|
|
dcf51ba20e | ||
|
|
73e6ccad96 | ||
|
|
ad5c409317 | ||
|
|
db597af272 | ||
|
|
4a46955bfb | ||
|
|
864bc3c1c1 | ||
|
|
2da395caf8 | ||
|
|
64eb27af8a | ||
|
|
98585bdc30 | ||
|
|
0803987df3 | ||
|
|
ffd32e9309 | ||
|
|
b587028aa5 | ||
|
|
43b9c06f79 | ||
|
|
05ffc76032 | ||
|
|
8e5ba51718 | ||
|
|
8aa9c4d004 | ||
|
|
d075a877f2 | ||
|
|
5dfd285105 | ||
|
|
51392c77aa | ||
|
|
e78691b12b | ||
|
|
bc65135c51 | ||
|
|
0108d66aa1 | ||
|
|
96443fa1fd | ||
|
|
61c4df9503 | ||
|
|
b990652702 | ||
|
|
7f7d4d89ea | ||
|
|
2b7f325cc7 | ||
|
|
b9768394a1 | ||
|
|
3e02d5016e |
@@ -822,6 +822,7 @@ if (IS_HOST_PLATFORM)
|
||||
endif()
|
||||
add_subdirectory(${TOOLS}/mipgen)
|
||||
add_subdirectory(${TOOLS}/normal-blending)
|
||||
add_subdirectory(${TOOLS}/rainbowgen)
|
||||
add_subdirectory(${TOOLS}/resgen)
|
||||
add_subdirectory(${TOOLS}/rgb-to-lmsr)
|
||||
add_subdirectory(${TOOLS}/roughness-prefilter)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.56.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.56.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.56.0'
|
||||
pod 'Filament', '~> 1.56.1'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,6 +7,10 @@ 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.56.2
|
||||
|
||||
- vk: fix stage pool gc logic
|
||||
|
||||
## v1.56.1
|
||||
|
||||
## v1.56.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.56.0
|
||||
VERSION_NAME=1.56.1
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+10
-2
@@ -112,7 +112,13 @@ class MainActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun setupFilament() {
|
||||
engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build()
|
||||
val config = Engine.Config()
|
||||
//config.forceGLES2Context = true
|
||||
|
||||
engine = Engine.Builder()
|
||||
.config(config)
|
||||
.featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0)
|
||||
.build()
|
||||
renderer = engine.createRenderer()
|
||||
scene = engine.createScene()
|
||||
view = engine.createView()
|
||||
@@ -123,7 +129,9 @@ class MainActivity : Activity() {
|
||||
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
|
||||
|
||||
// post-processing is not supported at feature level 0
|
||||
view.isPostProcessingEnabled = false
|
||||
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
|
||||
view.isPostProcessingEnabled = false
|
||||
}
|
||||
|
||||
// Tell the view which camera we want to use
|
||||
view.camera = camera
|
||||
|
||||
@@ -308,6 +308,11 @@ if (FILAMENT_ENABLE_MULTIVIEW)
|
||||
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
|
||||
endif()
|
||||
|
||||
# Whether to force the profiling mode.
|
||||
if (FILAMENT_FORCE_PROFILING_MODE)
|
||||
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Definitions
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -221,9 +221,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanSwapChain.h
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanResourceAllocator.h
|
||||
src/vulkan/VulkanResources.cpp
|
||||
src/vulkan/VulkanResources.h
|
||||
src/vulkan/VulkanTexture.cpp
|
||||
src/vulkan/VulkanTexture.h
|
||||
src/vulkan/VulkanUtility.cpp
|
||||
@@ -378,10 +375,6 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS
|
||||
if (LINUX AND FILAMENT_SUPPORTS_OSMESA)
|
||||
set(OSMESA_COMPILE_FLAGS
|
||||
-I${FILAMENT_OSMESA_PATH}/include/GL)
|
||||
set(OSMESA_LINKER_FLAGS
|
||||
-Wl,-L${FILAMENT_OSMESA_PATH}/lib/x86_64-linux-gnu/
|
||||
-lOSMesa
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "bluegl/BlueGL.h"
|
||||
|
||||
#include "osmesa.h"
|
||||
#include <osmesa.h>
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -56,6 +56,7 @@ protected:
|
||||
|
||||
private:
|
||||
OSMesaContext mContext;
|
||||
void* mOsMesaApi = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -797,9 +797,20 @@ void MetalDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void MetalDriver::destroyProgram(Handle<HwProgram> ph) {
|
||||
if (ph) {
|
||||
destruct_handle<MetalProgram>(ph);
|
||||
if (UTILS_UNLIKELY(!ph)) {
|
||||
return;
|
||||
}
|
||||
// Remove any cached pipeline states that refer to these programs.
|
||||
auto* metalProgram = handle_cast<MetalProgram>(ph);
|
||||
const auto& functions = metalProgram->getFunctionsIfPresent();
|
||||
if (UTILS_LIKELY(functions.isRaster())) { // only raster pipelines are cached
|
||||
const auto& raster = functions.getRasterFunctions();
|
||||
mContext->pipelineStateCache.removeIf([&](const MetalPipelineState& state) {
|
||||
const auto& [fragment, vertex] = raster;
|
||||
return state.fragmentFunction == fragment || state.vertexFunction == vertex;
|
||||
});
|
||||
}
|
||||
destruct_handle<MetalProgram>(ph);
|
||||
}
|
||||
|
||||
void MetalDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
@@ -861,10 +872,20 @@ void MetalDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh)
|
||||
|
||||
void MetalDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||
DEBUG_LOG("destroyDescriptorSet(dsh = %d)\n", dsh.getId());
|
||||
if (dsh) {
|
||||
executeAfterCurrentCommandBufferCompletes(
|
||||
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
|
||||
if (!dsh) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unbind this descriptor set.
|
||||
auto* descriptorSet = handle_cast<MetalDescriptorSet>(dsh);
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||
if (UTILS_UNLIKELY(mContext->currentDescriptorSets[i] == descriptorSet)) {
|
||||
mContext->currentDescriptorSets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
executeAfterCurrentCommandBufferCompletes(
|
||||
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
|
||||
}
|
||||
|
||||
void MetalDriver::terminate() {
|
||||
|
||||
@@ -202,6 +202,7 @@ public:
|
||||
MetalProgram(MetalContext& context, Program&& program) noexcept;
|
||||
|
||||
const MetalShaderCompiler::MetalFunctionBundle& getFunctions();
|
||||
const MetalShaderCompiler::MetalFunctionBundle& getFunctionsIfPresent() const;
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
@@ -437,7 +438,7 @@ struct MetalTimerQuery : public HwTimerQuery {
|
||||
|
||||
struct Status {
|
||||
std::atomic<bool> available {false};
|
||||
uint64_t elapsed {0}; // only valid if available is true
|
||||
std::atomic<uint64_t> elapsed {0}; // only valid if available is true
|
||||
};
|
||||
|
||||
std::shared_ptr<Status> status;
|
||||
|
||||
@@ -495,6 +495,10 @@ const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctions() {
|
||||
return mFunctionBundle;
|
||||
}
|
||||
|
||||
const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctionsIfPresent() const {
|
||||
return mFunctionBundle;
|
||||
}
|
||||
|
||||
void MetalProgram::initialize() {
|
||||
if (!mToken) {
|
||||
return;
|
||||
|
||||
@@ -83,6 +83,10 @@ public:
|
||||
return std::get<Compute>(mPrograms);
|
||||
}
|
||||
|
||||
bool isRaster() const { return std::holds_alternative<Raster>(mPrograms); }
|
||||
|
||||
bool isCompute() const { return std::holds_alternative<Compute>(mPrograms); }
|
||||
|
||||
static MetalFunctionBundle none() {
|
||||
return MetalFunctionBundle(None{});
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
@@ -160,6 +162,8 @@ template<typename StateType,
|
||||
typename HashFn = utils::hash::MurmurHashFn<StateType>>
|
||||
class StateCache {
|
||||
|
||||
using MapType = tsl::robin_map<StateType, MetalType, HashFn>;
|
||||
|
||||
public:
|
||||
|
||||
StateCache() = default;
|
||||
@@ -169,6 +173,18 @@ public:
|
||||
|
||||
void setDevice(id<MTLDevice> device) noexcept { mDevice = device; }
|
||||
|
||||
void removeIf(utils::Invocable<bool(const StateType&)> fn) noexcept {
|
||||
typename MapType::const_iterator it = mStateCache.begin();
|
||||
while (it != mStateCache.end()) {
|
||||
const auto& [key, _] = *it;
|
||||
if (UTILS_UNLIKELY(fn(key))) {
|
||||
it = mStateCache.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MetalType getOrCreateState(const StateType& state) noexcept {
|
||||
assert_invariant(mDevice);
|
||||
|
||||
@@ -196,7 +212,7 @@ private:
|
||||
StateCreator creator;
|
||||
id<MTLDevice> mDevice = nil;
|
||||
|
||||
tsl::robin_map<StateType, MetalType, HashFn> mStateCache;
|
||||
MapType mStateCache;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -614,7 +614,7 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
|
||||
featureLevel = FeatureLevel::FEATURE_LEVEL_2;
|
||||
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
|
||||
gets.max_combined_texture_image_units >=
|
||||
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
|
||||
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
|
||||
featureLevel = FeatureLevel::FEATURE_LEVEL_3;
|
||||
}
|
||||
}
|
||||
@@ -623,15 +623,13 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
|
||||
# ifndef IOS // IOS is guaranteed to have ES3.x
|
||||
else if (UTILS_UNLIKELY(major == 2)) {
|
||||
// Runtime OpenGL version is ES 2.x
|
||||
# if defined(BACKEND_OPENGL_LEVEL_GLES30)
|
||||
// mandatory extensions (all supported by Mali-400 and Adreno 304)
|
||||
assert_invariant(exts.OES_depth_texture);
|
||||
assert_invariant(exts.OES_depth24);
|
||||
assert_invariant(exts.OES_packed_depth_stencil);
|
||||
assert_invariant(exts.OES_rgb8_rgba8);
|
||||
assert_invariant(exts.OES_standard_derivatives);
|
||||
assert_invariant(exts.OES_texture_npot);
|
||||
# endif
|
||||
// note: mandatory extensions (all supported by Mali-400 and Adreno 304)
|
||||
// OES_depth_texture
|
||||
// OES_depth24
|
||||
// OES_packed_depth_stencil
|
||||
// OES_rgb8_rgba8
|
||||
// OES_standard_derivatives
|
||||
// OES_texture_npot
|
||||
featureLevel = FeatureLevel::FEATURE_LEVEL_0;
|
||||
}
|
||||
# endif // IOS
|
||||
|
||||
@@ -106,17 +106,29 @@
|
||||
// set to the desired debug level (for internal debugging [Default: None])
|
||||
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
|
||||
|
||||
// Override the debug markers if we are forcing profiling mode
|
||||
#if defined(FILAMENT_FORCE_PROFILING_MODE)
|
||||
# undef DEBUG_GROUP_MARKER_LEVEL
|
||||
# undef DEBUG_MARKER_LEVEL
|
||||
|
||||
# define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_NONE
|
||||
# define DEBUG_MARKER_LEVEL DEBUG_MARKER_PROFILE
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE
|
||||
# define DEBUG_MARKER()
|
||||
# define PROFILE_MARKER(marker) PROFILE_SCOPE(marker);
|
||||
# if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE
|
||||
# error PROFILING is exclusive; group markers must be disabled.
|
||||
# error PROFILING is exclusive; group markers must be disabled.
|
||||
# endif
|
||||
# ifndef NDEBUG
|
||||
# error PROFILING is meaningless in DEBUG mode.
|
||||
# endif
|
||||
#elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
# define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__);
|
||||
# define PROFILE_MARKER(marker) DEBUG_MARKER()
|
||||
# if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE
|
||||
# error PROFILING is exclusive; all other debug features must be disabled.
|
||||
# error PROFILING is exclusive; all other debug features must be disabled.
|
||||
# endif
|
||||
#else
|
||||
# define DEBUG_MARKER()
|
||||
@@ -394,24 +406,21 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
|
||||
}
|
||||
|
||||
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
|
||||
if (UTILS_UNLIKELY(mBoundProgram == p)) {
|
||||
// program didn't change, don't do anything.
|
||||
return true;
|
||||
}
|
||||
bool success = true;
|
||||
if (mBoundProgram != p) {
|
||||
// compile/link the program if needed and call glUseProgram
|
||||
success = p->use(this, mContext);
|
||||
assert_invariant(success == p->isValid());
|
||||
if (success) {
|
||||
// TODO: we could even improve this if the program could tell us which of the descriptors
|
||||
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
|
||||
// change often.
|
||||
decltype(mInvalidDescriptorSetBindings) changed;
|
||||
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
|
||||
mInvalidDescriptorSetBindings |= changed;
|
||||
|
||||
// compile/link the program if needed and call glUseProgram
|
||||
bool const success = p->use(this, mContext);
|
||||
assert_invariant(success == p->isValid());
|
||||
|
||||
if (success) {
|
||||
// TODO: we could even improve this if the program could tell us which of the descriptors
|
||||
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
|
||||
// change often.
|
||||
decltype(mInvalidDescriptorSetBindings) changed;
|
||||
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
|
||||
mInvalidDescriptorSetBindings |= changed;
|
||||
|
||||
mBoundProgram = p;
|
||||
mBoundProgram = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(mContext.isES2() && success)) {
|
||||
@@ -1673,7 +1682,7 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
|
||||
sc->rec709 = ((flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) &&
|
||||
!mPlatform.isSRGBSwapChainSupported());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +255,9 @@ void OpenGLProgram::updateUniforms(
|
||||
for (size_t i = 0, c = records.uniforms.size(); i < c; i++) {
|
||||
Program::Uniform const& u = records.uniforms[i];
|
||||
GLint const loc = records.locations[i];
|
||||
if (loc < 0) {
|
||||
// mRec709Location is special, it is handled by setRec709ColorSpace() and the corresponding
|
||||
// entry in `buffer` is typically not initialized, so we skip it.
|
||||
if (loc < 0 || loc == mRec709Location) {
|
||||
continue;
|
||||
}
|
||||
// u.offset is in 'uint32_t' units
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <memory>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -41,13 +42,56 @@ struct OSMesaSwapchain {
|
||||
std::unique_ptr<uint8_t[]> buffer;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
struct OSMesaAPI {
|
||||
private:
|
||||
using CreateContextFunc = OSMesaContext (*)(GLenum format, OSMesaContext);
|
||||
using DestroyContextFunc = GLboolean (*)(OSMesaContext);
|
||||
using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type,
|
||||
GLsizei width, GLsizei height);
|
||||
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
|
||||
|
||||
public:
|
||||
CreateContextFunc OSMesaCreateContext;
|
||||
DestroyContextFunc OSMesaDestroyContext;
|
||||
MakeCurrentFunc OSMesaMakeCurrent;
|
||||
GetProcAddressFunc OSMesaGetProcAddress;
|
||||
|
||||
OSMesaAPI() {
|
||||
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
|
||||
for (char const* libName: libraryNames) {
|
||||
mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (mLib) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(mLib)
|
||||
<< "Unable to dlopen libOSMesa to create a software GL context";
|
||||
|
||||
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
|
||||
|
||||
OSMesaCreateContext = (CreateContextFunc) OSMesaGetProcAddress("OSMesaCreateContext");
|
||||
OSMesaDestroyContext =
|
||||
(DestroyContextFunc) OSMesaGetProcAddress("OSMesaDestroyContext");
|
||||
OSMesaMakeCurrent = (MakeCurrentFunc) OSMesaGetProcAddress("OSMesaMakeCurrent");
|
||||
}
|
||||
|
||||
~OSMesaAPI() {
|
||||
dlclose(mLib);
|
||||
}
|
||||
private:
|
||||
void* mLib = nullptr;
|
||||
};
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
|
||||
const DriverConfig& driverConfig) noexcept {
|
||||
OSMesaAPI* api = new OSMesaAPI();
|
||||
mOsMesaApi = api;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
|
||||
<< "shared GL context is not supported with PlatformOSMesa";
|
||||
mContext = OSMesaCreateContext(GL_RGBA, NULL);
|
||||
mContext = api->OSMesaCreateContext(GL_RGBA, NULL);
|
||||
|
||||
// We need to do a no-op makecurrent here so that the context will be in a correct state before
|
||||
// any GL calls.
|
||||
@@ -62,7 +106,11 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
|
||||
}
|
||||
|
||||
void PlatformOSMesa::terminate() noexcept {
|
||||
OSMesaDestroyContext(mContext);
|
||||
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
||||
api->OSMesaDestroyContext(mContext);
|
||||
delete api;
|
||||
mOsMesaApi = nullptr;
|
||||
|
||||
bluegl::unbind();
|
||||
}
|
||||
|
||||
@@ -84,11 +132,12 @@ void PlatformOSMesa::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
||||
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
|
||||
|
||||
auto result = OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(), BACKING_GL_TYPE,
|
||||
impl->width, impl->height);
|
||||
FILAMENT_CHECK_POSTCONDITION(result) << "OSMesaMakeCurrent failed!";
|
||||
auto result = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
|
||||
BACKING_GL_TYPE, impl->width, impl->height);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
|
||||
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
|
||||
mStartingQueryIndex(std::get<0>(indices)),
|
||||
: mStartingQueryIndex(std::get<0>(indices)),
|
||||
mStoppingQueryIndex(std::get<1>(indices)) {}
|
||||
|
||||
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanResources.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/Condition.h>
|
||||
@@ -47,17 +47,12 @@ private:
|
||||
std::atomic<VkResult> status;
|
||||
};
|
||||
|
||||
struct VulkanFence : public HwFence, VulkanResource {
|
||||
VulkanFence() : VulkanResource(VulkanResourceType::FENCE) {}
|
||||
|
||||
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
|
||||
: VulkanResource(VulkanResourceType::FENCE),
|
||||
fence(fence) {}
|
||||
|
||||
struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
|
||||
VulkanFence() {}
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
};
|
||||
|
||||
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
|
||||
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
|
||||
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
|
||||
~VulkanTimerQuery();
|
||||
|
||||
|
||||
@@ -126,8 +126,7 @@ struct BlitterUniforms {
|
||||
}// anonymous namespace
|
||||
|
||||
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
|
||||
: mPhysicalDevice(physicalDevice),
|
||||
mCommands(commands) {}
|
||||
: mPhysicalDevice(physicalDevice), mCommands(commands) {}
|
||||
|
||||
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
||||
|
||||
@@ -151,7 +150,8 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
||||
}
|
||||
#endif
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = dst.texture->getIsProtected() ?
|
||||
mCommands->getProtected() : mCommands->get();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
resolveFast(&commands, aspect, src, dst);
|
||||
@@ -176,7 +176,8 @@ void VulkanBlitter::blit(VkFilter filter,
|
||||
#endif
|
||||
// src and dst should have the same aspect here
|
||||
VkImageAspectFlags const aspect = src.texture->getImageAspect();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = dst.texture->getIsProtected() ?
|
||||
mCommands->getProtected() : mCommands->get();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair);
|
||||
|
||||
@@ -58,66 +58,41 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
|
||||
mMarkers.push_back(marker);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
mTimestamps.push_back(start.time_since_epoch().count() > 0.0
|
||||
? start
|
||||
: std::chrono::high_resolution_clock::now());
|
||||
#endif
|
||||
mMarkers.push_back({marker,
|
||||
start.time_since_epoch().count() > 0.0
|
||||
? start
|
||||
: std::chrono::high_resolution_clock::now()});
|
||||
}
|
||||
|
||||
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop() noexcept {
|
||||
auto const marker = mMarkers.back();
|
||||
auto ret = mMarkers.back();
|
||||
mMarkers.pop_back();
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
auto const timestamp = mTimestamps.back();
|
||||
mTimestamps.pop_back();
|
||||
return std::make_pair(marker, timestamp);
|
||||
#else
|
||||
return std::make_pair(marker, Timestamp{});
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
|
||||
auto const marker = mMarkers.front();
|
||||
auto ret = mMarkers.front();
|
||||
mMarkers.pop_front();
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
auto const timestamp = mTimestamps.front();
|
||||
mTimestamps.pop_front();
|
||||
return std::make_pair(marker, timestamp);
|
||||
#else
|
||||
return std::make_pair(marker, Timestamp{});
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::pair<std::string, Timestamp> VulkanGroupMarkers::top() const {
|
||||
std::pair<std::string, Timestamp> const& VulkanGroupMarkers::top() const {
|
||||
assert_invariant(!empty());
|
||||
auto const marker = mMarkers.back();
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
auto const topTimestamp = mTimestamps.front();
|
||||
return std::make_pair(marker, topTimestamp);
|
||||
#else
|
||||
return std::make_pair(marker, Timestamp{});
|
||||
#endif
|
||||
return mMarkers.back();
|
||||
}
|
||||
|
||||
bool VulkanGroupMarkers::empty() const noexcept {
|
||||
return mMarkers.empty();
|
||||
}
|
||||
|
||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||
|
||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator,
|
||||
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected)
|
||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VkDevice device, VkQueue queue,
|
||||
VkCommandPool pool, bool isProtected)
|
||||
: mContext(context),
|
||||
mMarkerCount(0),
|
||||
isProtected(isProtected),
|
||||
mDevice(device),
|
||||
mQueue(queue),
|
||||
mResourceManager(allocator),
|
||||
mBuffer(createCommandBuffer(device, pool)),
|
||||
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
|
||||
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||
@@ -134,7 +109,7 @@ VulkanCommandBuffer::~VulkanCommandBuffer() {
|
||||
|
||||
void VulkanCommandBuffer::reset() noexcept {
|
||||
mMarkerCount = 0;
|
||||
mResourceManager.clear();
|
||||
mResources.clear();
|
||||
mWaitSemaphores.clear();
|
||||
|
||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||
@@ -257,8 +232,8 @@ VkSemaphore VulkanCommandBuffer::submit() {
|
||||
return mSubmission;
|
||||
}
|
||||
|
||||
CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator,
|
||||
VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected)
|
||||
CommandBufferPool::CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue,
|
||||
uint8_t queueFamilyIndex, bool isProtected)
|
||||
: mDevice(device),
|
||||
mRecording(INVALID) {
|
||||
VkCommandPoolCreateInfo createInfo = {
|
||||
@@ -271,8 +246,8 @@ CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAlloc
|
||||
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
|
||||
|
||||
for (size_t i = 0; i < CAPACITY; ++i) {
|
||||
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, allocator, device,
|
||||
queue, mPool, isProtected));
|
||||
mBuffers.emplace_back(
|
||||
std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool, isProtected));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +278,19 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() {
|
||||
|
||||
auto& recording = *mBuffers[mRecording];
|
||||
recording.begin();
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
if (mGroupMarkers) {
|
||||
std::unique_ptr<VulkanGroupMarkers> markers = std::make_unique<VulkanGroupMarkers>();
|
||||
while (!mGroupMarkers->empty()) {
|
||||
auto [marker, timestamp] = mGroupMarkers->pop_bottom();
|
||||
recording.pushMarker(marker.c_str());
|
||||
markers->push(marker, timestamp);
|
||||
}
|
||||
std::swap(mGroupMarkers, markers);
|
||||
}
|
||||
#endif
|
||||
|
||||
return recording;
|
||||
}
|
||||
|
||||
@@ -357,29 +345,46 @@ void CommandBufferPool::waitFor(VkSemaphore previousAction) {
|
||||
recording->insertWait(previousAction);
|
||||
}
|
||||
|
||||
void CommandBufferPool::pushMarker(char const* marker) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::string CommandBufferPool::topMarker() const {
|
||||
if (!mGroupMarkers || mGroupMarkers->empty()) {
|
||||
return "";
|
||||
}
|
||||
return std::get<0>(mGroupMarkers->top());
|
||||
}
|
||||
|
||||
void CommandBufferPool::pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp) {
|
||||
if (!mGroupMarkers) {
|
||||
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
|
||||
}
|
||||
mGroupMarkers->push(marker, timestamp);
|
||||
getRecording().pushMarker(marker);
|
||||
}
|
||||
|
||||
void CommandBufferPool::popMarker() {
|
||||
getRecording().popMarker();
|
||||
std::pair<std::string, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
|
||||
assert_invariant(mGroupMarkers && !mGroupMarkers->empty());
|
||||
auto ret = mGroupMarkers->pop();
|
||||
|
||||
// Note that if we're popping a marker while not recording, we would just pop the conceptual
|
||||
// stack of marker (i.e. mGroupMarkers) and not carry out the pop on the command buffer.
|
||||
if (isRecording()) {
|
||||
getRecording().popMarker();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CommandBufferPool::insertEvent(char const* marker) {
|
||||
getRecording().insertEvent(marker);
|
||||
}
|
||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||
|
||||
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
|
||||
VulkanResourceAllocator* allocator)
|
||||
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context)
|
||||
: mDevice(device),
|
||||
mProtectedQueue(protectedQueue),
|
||||
mProtectedQueueFamilyIndex(protectedQueueFamilyIndex),
|
||||
mAllocator(allocator),
|
||||
mContext(context),
|
||||
mPool(std::make_unique<CommandBufferPool>(context, allocator, device, queue, queueFamilyIndex,
|
||||
false)) {
|
||||
}
|
||||
mPool(std::make_unique<CommandBufferPool>(context, device, queue, queueFamilyIndex, false)) {}
|
||||
|
||||
void VulkanCommands::terminate() {
|
||||
mPool.reset();
|
||||
@@ -395,14 +400,19 @@ VulkanCommandBuffer& VulkanCommands::getProtected() {
|
||||
assert_invariant(mProtectedQueue != VK_NULL_HANDLE);
|
||||
|
||||
if (!mProtectedPool) {
|
||||
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mAllocator, mDevice,
|
||||
mProtectedQueue, mProtectedQueueFamilyIndex, true);
|
||||
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mDevice, mProtectedQueue,
|
||||
mProtectedQueueFamilyIndex, true);
|
||||
}
|
||||
auto& ret = mProtectedPool->getRecording();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VulkanCommands::flush() {
|
||||
// It's possible to call flush and wait at "terminate", in which case, we'll just return.
|
||||
if (!mPool && !mProtectedPool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkSemaphore dependency = mInjectedDependency;
|
||||
VkSemaphore lastSubmit = mLastSubmit;
|
||||
bool hasFlushed = false;
|
||||
@@ -434,6 +444,11 @@ bool VulkanCommands::flush() {
|
||||
}
|
||||
|
||||
void VulkanCommands::wait() {
|
||||
// It's possible to call flush and wait at "terminate", in which case, we'll just return.
|
||||
if (!mPool && !mProtectedPool) {
|
||||
return;
|
||||
}
|
||||
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("commands::wait");
|
||||
|
||||
@@ -465,47 +480,31 @@ void VulkanCommands::updateFences() {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
|
||||
void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
// If the timestamp is not 0, then we are carrying over a marker across buffer submits.
|
||||
// If it is 0, then this is a normal marker push and we should just print debug line as usual.
|
||||
if (timestamp.time_since_epoch().count() == 0.0) {
|
||||
FVK_LOGD << "----> " << str << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
if (!mGroupMarkers) {
|
||||
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
|
||||
}
|
||||
mGroupMarkers->push(str, timestamp);
|
||||
|
||||
mPool->pushMarker(str);
|
||||
mPool->pushMarker(str, timestamp);
|
||||
if (mProtectedPool) {
|
||||
mProtectedPool->pushMarker(str);
|
||||
mProtectedPool->pushMarker(str, timestamp);
|
||||
}
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
FVK_LOGD << "----> " << str << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanCommands::popGroupMarker() {
|
||||
assert_invariant(mGroupMarkers);
|
||||
|
||||
if (!mGroupMarkers->empty()) {
|
||||
VkCommandBuffer const cmdbuffer = get().buffer();
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
auto const [marker, startTime] = mGroupMarkers->pop();
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
|
||||
<< utils::io::endl;
|
||||
auto ret = mPool->popMarker();
|
||||
auto const& marker = ret.first;
|
||||
auto const& startTime = ret.second;
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
|
||||
<< utils::io::endl;
|
||||
#else
|
||||
mGroupMarkers->pop();
|
||||
#endif
|
||||
mPool->popMarker();
|
||||
if (mProtectedPool) {
|
||||
mProtectedPool->popMarker();
|
||||
}
|
||||
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
|
||||
// It could be that pop is called between flush() and get() (new command buffer), in which
|
||||
// case the marker is in "carried over" state, we'd just remove that. Since the
|
||||
// mCarriedOverMarkers is in the opposite order, we pop the bottom instead of the top.
|
||||
mCarriedOverMarkers->pop_bottom();
|
||||
mPool->popMarker();
|
||||
#endif // FVK_DEBUG_PRINT_GROUP_MARKERS
|
||||
|
||||
if (mProtectedPool) {
|
||||
mProtectedPool->popMarker();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,10 +516,10 @@ void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
|
||||
}
|
||||
|
||||
std::string VulkanCommands::getTopGroupMarker() const {
|
||||
if (!mGroupMarkers || mGroupMarkers->empty()) {
|
||||
return "";
|
||||
if (mProtectedPool) {
|
||||
return mProtectedPool->topMarker();
|
||||
}
|
||||
return std::get<0>(mGroupMarkers->top());
|
||||
return mPool->topMarker();
|
||||
}
|
||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
#include "VulkanAsyncHandles.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace fvkmemory;
|
||||
|
||||
struct VulkanContext;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
@@ -49,14 +51,11 @@ public:
|
||||
void push(std::string const& marker, Timestamp start = {}) noexcept;
|
||||
std::pair<std::string, Timestamp> pop() noexcept;
|
||||
std::pair<std::string, Timestamp> pop_bottom() noexcept;
|
||||
std::pair<std::string, Timestamp> top() const;
|
||||
std::pair<std::string, Timestamp> const& top() const;
|
||||
bool empty() const noexcept;
|
||||
|
||||
private:
|
||||
std::list<std::string> mMarkers;
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
std::list<Timestamp> mTimestamps;
|
||||
#endif
|
||||
std::list<std::pair<std::string, Timestamp>> mMarkers;
|
||||
};
|
||||
|
||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||
@@ -65,7 +64,7 @@ private:
|
||||
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
|
||||
// we're done waiting on the most recent submission of the given command buffer.
|
||||
struct VulkanCommandBuffer {
|
||||
VulkanCommandBuffer(VulkanContext* mContext, VulkanResourceAllocator* allocator,
|
||||
VulkanCommandBuffer(VulkanContext* mContext,
|
||||
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected);
|
||||
|
||||
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
|
||||
@@ -73,13 +72,10 @@ struct VulkanCommandBuffer {
|
||||
|
||||
~VulkanCommandBuffer();
|
||||
|
||||
inline void acquire(VulkanResource* resource) {
|
||||
mResourceManager.acquire(resource);
|
||||
inline void acquire(fvkmemory::resource_ptr<fvkmemory::Resource> resource) {
|
||||
mResources.push_back(resource);
|
||||
}
|
||||
|
||||
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
|
||||
mResourceManager.acquireAll(srcResources);
|
||||
}
|
||||
void reset() noexcept;
|
||||
|
||||
inline void insertWait(VkSemaphore sem) {
|
||||
@@ -119,21 +115,20 @@ private:
|
||||
bool const isProtected;
|
||||
VkDevice mDevice;
|
||||
VkQueue mQueue;
|
||||
VulkanAcquireOnlyResourceManager mResourceManager;
|
||||
CappedArray<VkSemaphore, 2> mWaitSemaphores;
|
||||
VkCommandBuffer mBuffer;
|
||||
VkSemaphore mSubmission;
|
||||
VkFence mFence;
|
||||
std::shared_ptr<VulkanCmdFence> mFenceStatus;
|
||||
|
||||
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
|
||||
};
|
||||
|
||||
struct CommandBufferPool {
|
||||
using ActiveBuffers = utils::bitset32;
|
||||
using ActiveBuffers = utils::bitset64;
|
||||
static constexpr int8_t INVALID = -1;
|
||||
|
||||
CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, VkDevice device,
|
||||
VkQueue queue, uint8_t queueFamilyIndex, bool isProtected);
|
||||
CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue,
|
||||
uint8_t queueFamilyIndex, bool isProtected);
|
||||
~CommandBufferPool();
|
||||
|
||||
VulkanCommandBuffer& getRecording();
|
||||
@@ -142,12 +137,14 @@ struct CommandBufferPool {
|
||||
void update();
|
||||
VkSemaphore flush();
|
||||
void wait();
|
||||
|
||||
void waitFor(VkSemaphore previousAction);
|
||||
|
||||
void pushMarker(char const* marker);
|
||||
void popMarker();
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::string topMarker() const;
|
||||
void pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp);
|
||||
std::pair<std::string, VulkanGroupMarkers::Timestamp> popMarker();
|
||||
void insertEvent(char const* marker);
|
||||
#endif
|
||||
|
||||
inline bool isRecording() const { return mRecording != INVALID; }
|
||||
|
||||
@@ -155,12 +152,21 @@ private:
|
||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||
static_assert(CAPACITY < 128);
|
||||
|
||||
// The number of bits in ActiveBuffers describe the usage of the buffers in the pool, so must be
|
||||
// larger than the size of the pool.
|
||||
static_assert(sizeof(ActiveBuffers) * 8 >= CAPACITY);
|
||||
|
||||
using BufferList = utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>>;
|
||||
VkDevice mDevice;
|
||||
VkCommandPool mPool;
|
||||
ActiveBuffers mSubmitted;
|
||||
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
|
||||
int8_t mRecording;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Manages a set of command buffers and semaphores, exposing an API that is significantly simpler
|
||||
@@ -189,8 +195,7 @@ private:
|
||||
class VulkanCommands {
|
||||
public:
|
||||
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
|
||||
VulkanResourceAllocator* allocator);
|
||||
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context);
|
||||
|
||||
void terminate();
|
||||
|
||||
@@ -241,7 +246,6 @@ private:
|
||||
VkQueue const mProtectedQueue;
|
||||
// For defered initialization if/when we need protected content
|
||||
uint32_t const mProtectedQueueFamilyIndex;
|
||||
VulkanResourceAllocator* mAllocator;
|
||||
VulkanContext* mContext;
|
||||
|
||||
std::unique_ptr<CommandBufferPool> mPool;
|
||||
@@ -249,11 +253,6 @@ private:
|
||||
|
||||
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
|
||||
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* 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
|
||||
*
|
||||
@@ -63,7 +63,7 @@
|
||||
#define FVK_DEBUG_SHADER_MODULE 0x00000800
|
||||
#define FVK_DEBUG_READ_PIXELS 0x00001000
|
||||
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
|
||||
#define FVK_DEBUG_ALLOCATION 0x00004000
|
||||
#define FVK_DEBUG_STAGING_ALLOCATION 0x00004000
|
||||
|
||||
// Enable the debug utils extension if it is available.
|
||||
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
|
||||
@@ -98,6 +98,12 @@
|
||||
#define FVK_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
// Override the debug flags if we are forcing profiling mode
|
||||
#if defined(FILAMENT_FORCE_PROFILING_MODE)
|
||||
#undef FVK_DEBUG_FLAGS
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PROFILING)
|
||||
#endif
|
||||
|
||||
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
// Group marker only works only if validation or debug utils is enabled since it uses
|
||||
@@ -133,6 +139,10 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
|
||||
#if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING
|
||||
|
||||
#ifndef NDEBUG
|
||||
#error PROFILING is meaningless in DEBUG mode.
|
||||
#endif
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker)
|
||||
#define FVK_SYSTRACE_END()
|
||||
@@ -186,14 +196,16 @@ constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1;
|
||||
// buffers that have been submitted but have not yet finished rendering. Note that Filament can
|
||||
// issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on
|
||||
// some platforms.
|
||||
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 10;
|
||||
//
|
||||
// Heuristic: Triple Buffering (3) multiplied by maximum number of renderpasses (15).
|
||||
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 3 * 15;
|
||||
|
||||
// Number of command buffer submissions that should occur before an unused pipeline is removed
|
||||
// from the cache.
|
||||
//
|
||||
// If this number is low, VkPipeline construction will occur frequently, which can
|
||||
// be extremely slow. If this number is high, the memory footprint will be large.
|
||||
constexpr static const int FVK_MAX_PIPELINE_AGE = 10;
|
||||
constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
|
||||
|
||||
// VulkanPipelineCache does not track which command buffers contain references to which pipelines,
|
||||
// instead it simply waits for at least FVK_MAX_COMMAND_BUFFERS submissions to occur before
|
||||
|
||||
@@ -25,15 +25,11 @@
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <algorithm> // for std::max
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
using utils::FixedCapacityVector;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
} // end anonymous namespace
|
||||
@@ -113,7 +109,7 @@ void VulkanTimestamps::clearQuery(uint32_t queryIndex) {
|
||||
}
|
||||
|
||||
void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
|
||||
VulkanTimerQuery* query) {
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
uint32_t const index = query->getStartingQueryIndex();
|
||||
|
||||
auto const cmdbuffer = commands->buffer();
|
||||
@@ -125,19 +121,20 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
|
||||
}
|
||||
|
||||
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
|
||||
VulkanTimerQuery const* query) {
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
uint32_t const index = query->getStoppingQueryIndex();
|
||||
vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
|
||||
}
|
||||
|
||||
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) {
|
||||
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
|
||||
uint32_t const index = query->getStartingQueryIndex();
|
||||
QueryResult result;
|
||||
size_t const dataSize = result.size() * sizeof(uint64_t);
|
||||
VkDeviceSize const stride = sizeof(uint64_t) * 2;
|
||||
VkResult vkresult =
|
||||
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(),
|
||||
stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(), stride,
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
|
||||
<< "vkGetQueryPoolResults error: " << static_cast<int32_t>(vkresult);
|
||||
if (vkresult == VK_NOT_READY) {
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Mutex.h>
|
||||
@@ -41,10 +43,10 @@ struct VulkanTimerQuery;
|
||||
struct VulkanCommandBuffer;
|
||||
|
||||
struct VulkanAttachment {
|
||||
VulkanTexture* texture = nullptr;
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture;
|
||||
uint8_t level = 0;
|
||||
uint8_t layerCount = 1;
|
||||
uint16_t layer = 0;
|
||||
uint8_t layer = 0;
|
||||
|
||||
bool isDepth() const;
|
||||
VkImage getImage() const;
|
||||
@@ -70,9 +72,11 @@ public:
|
||||
std::tuple<uint32_t, uint32_t> getNextQuery();
|
||||
void clearQuery(uint32_t queryIndex);
|
||||
|
||||
void beginQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery* query);
|
||||
void endQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery const* query);
|
||||
QueryResult getResult(VulkanTimerQuery const* query);
|
||||
void beginQuery(VulkanCommandBuffer const* commands,
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
void endQuery(VulkanCommandBuffer const* commands,
|
||||
fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
QueryResult getResult(fvkmemory::resource_ptr<VulkanTimerQuery> query);
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
@@ -82,7 +86,9 @@ private:
|
||||
};
|
||||
|
||||
struct VulkanRenderPass {
|
||||
VulkanRenderTarget* renderTarget;
|
||||
// Between the begin and end command render pass we cache the command buffer
|
||||
VulkanCommandBuffer* commandBuffer;
|
||||
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
|
||||
VkRenderPass renderPass;
|
||||
RenderPassParams params;
|
||||
int currentSubpass;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,13 +24,14 @@
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanPipelineCache.h"
|
||||
#include "VulkanReadPixels.h"
|
||||
#include "VulkanResourceAllocator.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "backend/DriverEnums.h"
|
||||
#include "caching/VulkanDescriptorSetManager.h"
|
||||
#include "caching/VulkanPipelineLayoutCache.h"
|
||||
#include "memory/ResourceManager.h"
|
||||
#include "memory/ResourcePointer.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Driver.h"
|
||||
@@ -77,6 +78,9 @@ public:
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
private:
|
||||
template<typename D>
|
||||
using resource_ptr = fvkmemory::resource_ptr<D>;
|
||||
|
||||
static constexpr uint8_t MAX_SAMPLER_BINDING_COUNT = Program::SAMPLER_BINDING_COUNT;
|
||||
|
||||
void debugCommandBegin(CommandStream* cmds, bool synchronous,
|
||||
@@ -113,21 +117,16 @@ private:
|
||||
void collectGarbage();
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
fvkmemory::ResourceManager mResourceManager;
|
||||
std::unique_ptr<VulkanTimestamps> mTimestamps;
|
||||
|
||||
VulkanSwapChain* mCurrentSwapChain = nullptr;
|
||||
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
|
||||
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
|
||||
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
|
||||
VulkanRenderPass mCurrentRenderPass = {};
|
||||
VmaAllocator mAllocator = VK_NULL_HANDLE;
|
||||
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
||||
|
||||
VulkanContext mContext = {};
|
||||
VulkanResourceAllocator mResourceAllocator;
|
||||
VulkanResourceManager mResourceManager;
|
||||
|
||||
// Used for resources that are created synchronously and used and destroyed on the backend
|
||||
// thread.
|
||||
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
|
||||
|
||||
VulkanCommands mCommands;
|
||||
VulkanPipelineLayoutCache mPipelineLayoutCache;
|
||||
@@ -142,7 +141,7 @@ private:
|
||||
|
||||
// This is necessary for us to write to push constants after binding a pipeline.
|
||||
struct {
|
||||
VulkanProgram* program;
|
||||
resource_ptr<VulkanProgram> program;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
DescriptorSetMask descriptorSetMask;
|
||||
} mBoundPipeline = {};
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
#include "VulkanDriver.h"
|
||||
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanResourceAllocator.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "spirv/VulkanSpirvUtils.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
@@ -105,9 +105,10 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
|
||||
fvkmemory::resource_ptr<VulkanTexture> initMsaaTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkDevice device,
|
||||
VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, uint8_t levels,
|
||||
VulkanCommands* commands, fvkmemory::ResourceManager* resManager, uint8_t levels,
|
||||
uint8_t samples, VulkanStagePool& stagePool) {
|
||||
assert_invariant(texture);
|
||||
auto msTexture = texture->getSidecar();
|
||||
@@ -117,10 +118,9 @@ VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
|
||||
const TextureUsage usage = texture->usage & TextureUsage::ALL_ATTACHMENTS;
|
||||
assert_invariant(static_cast<uint16_t>(usage) != 0U);
|
||||
|
||||
msTexture = new VulkanTexture(device, physicalDevice, context, allocator, commands,
|
||||
handleAllocator, texture->target, levels,
|
||||
texture->format, samples, texture->width, texture->height, texture->depth, usage,
|
||||
stagePool, true /* heap allocated */);
|
||||
msTexture = resource_ptr<VulkanTexture>::construct(resManager, device, physicalDevice,
|
||||
context, allocator, resManager, commands, texture->target, levels, texture->format,
|
||||
samples, texture->width, texture->height, texture->depth, usage, stagePool);
|
||||
texture->setSidecar(msTexture);
|
||||
}
|
||||
return msTexture;
|
||||
@@ -128,19 +128,18 @@ VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanTexture> texture) {
|
||||
mResources.push_back(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> obj) {
|
||||
mResources.push_back(obj);
|
||||
}
|
||||
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
|
||||
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT),
|
||||
bitmask(fromBackendLayout(layout)),
|
||||
: bitmask(fromBackendLayout(layout)),
|
||||
count(Count::fromLayoutBitmask(bitmask)) {}
|
||||
|
||||
void VulkanDescriptorSet::acquire(VulkanTexture* texture) {
|
||||
mResources.acquire(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSet::acquire(VulkanBufferObject* bufferObject) {
|
||||
mResources.acquire(bufferObject);
|
||||
}
|
||||
|
||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
|
||||
mRangeCount = 0;
|
||||
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
|
||||
@@ -164,9 +163,9 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program
|
||||
}
|
||||
}
|
||||
|
||||
void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout,
|
||||
void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout layout,
|
||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||
VulkanCommandBuffer* cmdbuf = &(commands->get());
|
||||
|
||||
uint32_t binaryValue = 0;
|
||||
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
|
||||
if (std::holds_alternative<bool>(value)) {
|
||||
@@ -182,13 +181,12 @@ void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout l
|
||||
int const ival = std::get<int>(value);
|
||||
binaryValue = *reinterpret_cast<uint32_t const*>(&ival);
|
||||
}
|
||||
vkCmdPushConstants(cmdbuf->buffer(), layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
|
||||
vkCmdPushConstants(cmdbuf, layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
|
||||
&binaryValue);
|
||||
}
|
||||
|
||||
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
: HwProgram(builder.getName()),
|
||||
VulkanResource(VulkanResourceType::PROGRAM),
|
||||
mInfo(new(std::nothrow) PipelineInfo(builder)),
|
||||
mDevice(device) {
|
||||
|
||||
@@ -256,23 +254,24 @@ VulkanProgram::~VulkanProgram() {
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
VulkanRenderTarget::VulkanRenderTarget()
|
||||
: HwRenderTarget(0, 0),
|
||||
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
||||
mOffscreen(false),
|
||||
mProtected(false),
|
||||
mResources(nullptr),
|
||||
mInfo(std::make_unique<Auxiliary>()) {
|
||||
mInfo->rpkey.samples = mInfo->fbkey.samples = 1;
|
||||
}
|
||||
|
||||
void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
||||
VulkanRenderTarget::~VulkanRenderTarget() = default;
|
||||
|
||||
void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain) {
|
||||
assert_invariant(!mOffscreen);
|
||||
|
||||
VkExtent2D const extent = swapChain.getExtent();
|
||||
VkExtent2D const extent = swapchain->getExtent();
|
||||
width = extent.width;
|
||||
height = extent.height;
|
||||
mProtected = swapChain.isProtected();
|
||||
mProtected = swapchain->isProtected();
|
||||
|
||||
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
|
||||
VulkanAttachment color = {};
|
||||
color.texture = swapchain->getCurrentColor();
|
||||
mInfo->attachments = {color};
|
||||
|
||||
auto& fbkey = mInfo->fbkey;
|
||||
@@ -284,33 +283,30 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
||||
fbkey.color[0] = color.getImageView();
|
||||
fbkey.resolve[0] = VK_NULL_HANDLE;
|
||||
|
||||
VulkanAttachment depth = {};
|
||||
rpkey.depthFormat = depth.getFormat();
|
||||
fbkey.depth = VK_NULL_HANDLE;
|
||||
|
||||
if (swapChain.getDepth()) {
|
||||
depth = {.texture = swapChain.getDepth()};
|
||||
if (swapchain->getDepth()) {
|
||||
VulkanAttachment depth = {};
|
||||
depth.texture = swapchain->getDepth();
|
||||
mInfo->attachments.push_back(depth);
|
||||
mInfo->depthIndex = 1;
|
||||
|
||||
rpkey.depthFormat = depth.getFormat();
|
||||
fbkey.depth = depth.getImageView();
|
||||
} else {
|
||||
rpkey.depthFormat = VK_FORMAT_UNDEFINED;
|
||||
fbkey.depth = VK_NULL_HANDLE;
|
||||
}
|
||||
mInfo->colors.set(0);
|
||||
}
|
||||
|
||||
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator, uint32_t width, uint32_t height, uint8_t samples,
|
||||
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
|
||||
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
|
||||
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
|
||||
: HwRenderTarget(width, height),
|
||||
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
||||
mOffscreen(true),
|
||||
mProtected(false),
|
||||
mResources(handleAllocator),
|
||||
mInfo(std::make_unique<Auxiliary>()) {
|
||||
|
||||
auto& depth = depthStencil[0];
|
||||
|
||||
// Constrain the sample count according to both kinds of sample count masks obtained from
|
||||
@@ -340,6 +336,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
continue;
|
||||
}
|
||||
|
||||
mProtected |= texture->getIsProtected();
|
||||
|
||||
attachments.push_back(attachment);
|
||||
mInfo->colors.set(index);
|
||||
|
||||
@@ -347,14 +345,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
fbkey.color[index] = attachment.getImageView();
|
||||
fbkey.resolve[index] = VK_NULL_HANDLE;
|
||||
|
||||
mResources.acquire(attachment.texture);
|
||||
|
||||
if (samples > 1) {
|
||||
VulkanAttachment msaaAttachment = {};
|
||||
if (texture->samples == 1) {
|
||||
auto msaaTexture = initMsaaTexture(texture, device, physicalDevice, context,
|
||||
allocator, commands, handleAllocator,
|
||||
((VulkanTexture const*) texture)->levels, samples, stagePool);
|
||||
allocator, commands, resourceManager, texture->levels, samples, stagePool);
|
||||
if (msaaTexture && msaaTexture->isTransientAttachment()) {
|
||||
rpkey.usesLazilyAllocatedMemory |= (1 << index);
|
||||
}
|
||||
@@ -375,7 +370,6 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
}
|
||||
fbkey.color[index] = msaaAttachment.getImageView();
|
||||
msaa.push_back(msaaAttachment);
|
||||
mResources.acquire(msaaAttachment.texture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,19 +382,18 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
auto depthTexture = depth.texture;
|
||||
mInfo->depthIndex = (uint8_t) attachments.size();
|
||||
attachments.push_back(depth);
|
||||
mResources.acquire(depthTexture);
|
||||
fbkey.depth = depth.getImageView();
|
||||
if (samples > 1) {
|
||||
mInfo->msaaDepthIndex = mInfo->depthIndex;
|
||||
if (depthTexture->samples == 1) {
|
||||
// MSAA depth texture must have the mipmap count of 1
|
||||
uint8_t const msLevel = 1;
|
||||
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
|
||||
auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context, allocator,
|
||||
commands, handleAllocator, msLevel, samples, stagePool);
|
||||
// Create sidecar MSAA texture for the depth attachment if it does not already
|
||||
// exist.
|
||||
auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context,
|
||||
allocator, commands, resourceManager, msLevel, samples, stagePool);
|
||||
mInfo->msaaDepthIndex = (uint8_t) attachments.size();
|
||||
attachments.push_back({ .texture = msaa, .layerCount = layerCount });
|
||||
mResources.acquire(msaa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -491,9 +484,7 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands
|
||||
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
|
||||
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
|
||||
mInfo(attributes.size()) {
|
||||
|
||||
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
|
||||
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
|
||||
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
|
||||
@@ -531,19 +522,15 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
|
||||
}
|
||||
|
||||
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanResourceAllocator* allocator,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
|
||||
uint32_t vertexCount, fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi)
|
||||
: HwVertexBuffer(vertexCount),
|
||||
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
|
||||
vbih(vbih),
|
||||
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
|
||||
mResources(allocator) {
|
||||
vbi(vbi),
|
||||
// TODO: Seems a bit wasteful. can we do better here?
|
||||
mBuffers(MAX_VERTEX_BUFFER_COUNT) {
|
||||
}
|
||||
|
||||
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
|
||||
VulkanBufferObject* bufferObject, uint32_t index) {
|
||||
VulkanVertexBufferInfo const* const vbi =
|
||||
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
|
||||
void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
|
||||
uint32_t index) {
|
||||
size_t const count = vbi->getAttributeCount();
|
||||
VkBuffer* const vkbuffers = getVkBuffers();
|
||||
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
|
||||
@@ -552,25 +539,20 @@ void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
|
||||
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
|
||||
}
|
||||
}
|
||||
mResources.acquire(bufferObject);
|
||||
mResources.push_back(bufferObject);
|
||||
}
|
||||
|
||||
VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
uint32_t byteCount, BufferObjectBinding bindingType)
|
||||
: HwBufferObject(byteCount),
|
||||
VulkanResource(VulkanResourceType::BUFFER_OBJECT),
|
||||
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
|
||||
bindingType(bindingType) {}
|
||||
|
||||
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
|
||||
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
|
||||
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
|
||||
mResources(resourceAllocator) {
|
||||
type = pt;
|
||||
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
|
||||
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
|
||||
mResources.acquire(vertexBuffer);
|
||||
mResources.acquire(indexBuffer);
|
||||
}
|
||||
VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
|
||||
fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
|
||||
fvkmemory::resource_ptr<VulkanIndexBuffer> ib)
|
||||
: HwRenderPrimitive{.type = pt},
|
||||
vertexBuffer(vb),
|
||||
indexBuffer(ib) {}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
// This needs to be at the top
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanAsyncHandles.h"
|
||||
#include "VulkanBuffer.h"
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanSwapChain.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
|
||||
#include <private/backend/SamplerGroup.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/StructureOfArrays.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
@@ -55,7 +57,7 @@ inline uint8_t collapsedCount(Bitmask const& mask) {
|
||||
class VulkanTimestamps;
|
||||
struct VulkanBufferObject;
|
||||
|
||||
struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout {
|
||||
struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource {
|
||||
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 4;
|
||||
static constexpr uint8_t MAX_BINDINGS = 25;
|
||||
|
||||
@@ -129,21 +131,19 @@ private:
|
||||
VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet {
|
||||
struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource {
|
||||
public:
|
||||
// Because we need to recycle descriptor sets not used, we allow for a callback that the "Pool"
|
||||
// can use to repackage the vk handle.
|
||||
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
||||
|
||||
VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
|
||||
VulkanDescriptorSet(VkDescriptorSet rawSet,
|
||||
UniformBufferBitmask const& dynamicUboMask,
|
||||
uint8_t uniqueDynamicUboCount,
|
||||
OnRecycle&& onRecycleFn)
|
||||
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
|
||||
vkSet(rawSet),
|
||||
: vkSet(rawSet),
|
||||
dynamicUboMask(dynamicUboMask),
|
||||
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
||||
mResources(allocator),
|
||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||
|
||||
~VulkanDescriptorSet() {
|
||||
@@ -160,9 +160,8 @@ public:
|
||||
return &mOffsets;
|
||||
}
|
||||
|
||||
void acquire(VulkanTexture* texture);
|
||||
|
||||
void acquire(VulkanBufferObject* texture);
|
||||
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
||||
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
||||
|
||||
VkDescriptorSet const vkSet;
|
||||
UniformBufferBitmask const dynamicUboMask;
|
||||
@@ -170,7 +169,7 @@ public:
|
||||
|
||||
private:
|
||||
backend::DescriptorSetOffsetArray mOffsets;
|
||||
VulkanAcquireOnlyResourceManager mResources;
|
||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||
OnRecycle mOnRecycleFn;
|
||||
};
|
||||
|
||||
@@ -178,14 +177,11 @@ using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
|
||||
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
struct PushConstantDescription {
|
||||
|
||||
explicit PushConstantDescription(backend::Program const& program) noexcept;
|
||||
|
||||
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
|
||||
|
||||
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
|
||||
|
||||
void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage,
|
||||
void write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage,
|
||||
uint8_t index, backend::PushConstantVariant const& value);
|
||||
|
||||
private:
|
||||
@@ -196,12 +192,10 @@ private:
|
||||
uint32_t mRangeCount;
|
||||
};
|
||||
|
||||
struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
|
||||
struct VulkanProgram : public HwProgram, fvkmemory::Resource {
|
||||
using BindingList = CappedArray<uint16_t, MAX_SAMPLER_COUNT>;
|
||||
|
||||
VulkanProgram(VkDevice device, Program const& builder) noexcept;
|
||||
|
||||
~VulkanProgram();
|
||||
|
||||
inline VkShaderModule getVertexShader() const {
|
||||
@@ -218,9 +212,9 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
return mInfo->pushConstantDescription.getVkRanges();
|
||||
}
|
||||
|
||||
inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout,
|
||||
inline void writePushConstant(VkCommandBuffer cmdbuf, VkPipelineLayout layout,
|
||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
|
||||
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
|
||||
}
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
@@ -255,16 +249,16 @@ private:
|
||||
//
|
||||
// We use private inheritance to shield clients from the width / height fields in HwRenderTarget,
|
||||
// which are not representative when this is the default render target.
|
||||
struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
|
||||
// Creates an offscreen render target.
|
||||
VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
uint32_t width, uint32_t height,
|
||||
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
|
||||
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
|
||||
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
|
||||
|
||||
~VulkanRenderTarget();
|
||||
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
explicit VulkanRenderTarget();
|
||||
|
||||
@@ -276,12 +270,12 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
inline VulkanAttachment const& getColor0() const {
|
||||
inline VulkanAttachment& getColor0() const {
|
||||
assert_invariant(mInfo->colors[0]);
|
||||
return mInfo->attachments[0];
|
||||
}
|
||||
|
||||
inline VulkanAttachment const& getDepth() const {
|
||||
inline VulkanAttachment& getDepth() const {
|
||||
assert_invariant(hasDepth());
|
||||
if (mInfo->fbkey.samples == 1) {
|
||||
return mInfo->attachments[mInfo->depthIndex];
|
||||
@@ -308,7 +302,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
inline bool isSwapChain() const { return !mOffscreen; }
|
||||
inline bool isProtected() const { return mProtected; }
|
||||
|
||||
void bindToSwapChain(VulkanSwapChain& surf);
|
||||
void bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain);
|
||||
|
||||
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
|
||||
|
||||
@@ -331,13 +325,12 @@ private:
|
||||
bool const mOffscreen;
|
||||
bool mProtected;
|
||||
|
||||
VulkanAcquireOnlyResourceManager mResources;
|
||||
std::unique_ptr<Auxiliary> mInfo;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject;
|
||||
|
||||
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
|
||||
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, fvkmemory::Resource {
|
||||
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes);
|
||||
|
||||
@@ -384,33 +377,24 @@ private:
|
||||
PipelineInfo mInfo;
|
||||
};
|
||||
|
||||
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
|
||||
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanResourceAllocator* allocator,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
|
||||
struct VulkanVertexBuffer : public HwVertexBuffer, fvkmemory::Resource {
|
||||
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint32_t vertexCount,
|
||||
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi);
|
||||
void setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, uint32_t index);
|
||||
|
||||
void setBuffer(VulkanResourceAllocator const& allocator,
|
||||
VulkanBufferObject* bufferObject, uint32_t index);
|
||||
inline VkBuffer const* getVkBuffers() const { return mBuffers.data(); }
|
||||
inline VkBuffer* getVkBuffers() { return mBuffers.data(); }
|
||||
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi;
|
||||
|
||||
inline VkBuffer const* getVkBuffers() const {
|
||||
return mBuffers.data();
|
||||
}
|
||||
|
||||
inline VkBuffer* getVkBuffers() {
|
||||
return mBuffers.data();
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
private:
|
||||
utils::FixedCapacityVector<VkBuffer> mBuffers;
|
||||
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
|
||||
std::vector<fvkmemory::resource_ptr<VulkanBufferObject>> mResources;
|
||||
};
|
||||
|
||||
struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
|
||||
struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
|
||||
VulkanIndexBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, uint8_t elementSize,
|
||||
uint32_t indexCount)
|
||||
: HwIndexBuffer(elementSize, indexCount),
|
||||
VulkanResource(VulkanResourceType::INDEX_BUFFER),
|
||||
buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount),
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
|
||||
|
||||
@@ -418,7 +402,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
|
||||
const VkIndexType indexType;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject : public HwBufferObject, VulkanResource {
|
||||
struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
|
||||
VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType);
|
||||
|
||||
@@ -426,28 +410,13 @@ struct VulkanBufferObject : public HwBufferObject, VulkanResource {
|
||||
const BufferObjectBinding bindingType;
|
||||
};
|
||||
|
||||
struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
|
||||
// NOTE: we have to use out-of-line allocation here because the size of a Handle<> is limited
|
||||
std::unique_ptr<SamplerGroup> sb;// FIXME: this shouldn't depend on filament::SamplerGroup
|
||||
explicit VulkanSamplerGroup(size_t size) noexcept
|
||||
: VulkanResource(VulkanResourceType::SAMPLER_GROUP),
|
||||
sb(new SamplerGroup(size)) {}
|
||||
};
|
||||
struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
||||
VulkanRenderPrimitive(PrimitiveType pt, fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
|
||||
fvkmemory::resource_ptr<VulkanIndexBuffer> ib);
|
||||
~VulkanRenderPrimitive() = default;
|
||||
|
||||
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
|
||||
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
|
||||
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
|
||||
|
||||
~VulkanRenderPrimitive() {
|
||||
mResources.clear();
|
||||
}
|
||||
|
||||
VulkanVertexBuffer* vertexBuffer = nullptr;
|
||||
VulkanIndexBuffer* indexBuffer = nullptr;
|
||||
|
||||
private:
|
||||
// Keep references to the vertex buffer and the index buffer.
|
||||
FixedSizeVulkanResourceManager<2> mResources;
|
||||
fvkmemory::resource_ptr<VulkanVertexBuffer> vertexBuffer;
|
||||
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
|
||||
};
|
||||
|
||||
inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
|
||||
|
||||
@@ -241,7 +241,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
|
||||
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
|
||||
mPipelineRequirements.shaders[0] = program->getVertexShader();
|
||||
mPipelineRequirements.shaders[1] = program->getFragmentShader();
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -44,7 +43,6 @@ namespace filament::backend {
|
||||
struct VulkanProgram;
|
||||
struct VulkanBufferObject;
|
||||
struct VulkanTexture;
|
||||
class VulkanResourceAllocator;
|
||||
|
||||
// VulkanPipelineCache manages a cache of descriptor sets and pipelines.
|
||||
//
|
||||
@@ -122,7 +120,7 @@ public:
|
||||
void bindPipeline(VulkanCommandBuffer* commands);
|
||||
|
||||
// Each of the following methods are fast and do not make Vulkan calls.
|
||||
void bindProgram(VulkanProgram* program) noexcept;
|
||||
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
|
||||
void bindRasterState(const RasterState& rasterState) noexcept;
|
||||
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
|
||||
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
|
||||
|
||||
@@ -117,9 +117,10 @@ void VulkanReadPixels::terminate() noexcept {
|
||||
VulkanReadPixels::VulkanReadPixels(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint32_t const y,
|
||||
uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex,
|
||||
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
|
||||
void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget, uint32_t const x,
|
||||
uint32_t const y, uint32_t const width, uint32_t const height,
|
||||
uint32_t const graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
|
||||
SelecteMemoryFunction const& selectMemoryFunc,
|
||||
OnReadCompleteFunction const& readCompleteFunc) {
|
||||
assert_invariant(mDevice != VK_NULL_HANDLE);
|
||||
|
||||
@@ -143,7 +144,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
|
||||
|
||||
VkCommandPool& cmdpool = mCommandPool;
|
||||
|
||||
VulkanTexture* srcTexture = srcTarget->getColor0().texture;
|
||||
fvkmemory::resource_ptr<VulkanTexture> srcTexture = srcTarget->getColor0().texture;
|
||||
assert_invariant(srcTexture);
|
||||
VkFormat const srcFormat = srcTexture->getVkFormat();
|
||||
bool const swizzle
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
|
||||
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "private/backend/Driver.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
@@ -72,9 +73,9 @@ public:
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void run(VulkanRenderTarget* srcTarget, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
|
||||
SelecteMemoryFunction const& selectMemoryFunc,
|
||||
void run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget, uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height, uint32_t graphicsQueueFamilyIndex,
|
||||
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
|
||||
OnReadCompleteFunction const& readCompleteFunc);
|
||||
|
||||
// This method will block until all of the in-flight requests are complete.
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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_VULKANRESOURCEALLOCATOR_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
|
||||
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
#define TRACK_INCREMENT() \
|
||||
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
|
||||
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
|
||||
}
|
||||
#define TRACK_DECREMENT() \
|
||||
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
|
||||
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
|
||||
}
|
||||
#else
|
||||
// No-op
|
||||
#define TRACK_INCREMENT()
|
||||
#define TRACK_DECREMENT()
|
||||
#endif
|
||||
|
||||
class VulkanResourceAllocator {
|
||||
public:
|
||||
using AllocatorImpl = HandleAllocatorVK;
|
||||
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
|
||||
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
|
||||
}
|
||||
#else
|
||||
{}
|
||||
#endif
|
||||
|
||||
template<typename D, typename... ARGS>
|
||||
inline Handle<D> initHandle(ARGS&&... args) noexcept {
|
||||
auto handle = mHandleAllocatorImpl.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
|
||||
auto obj = handle_cast<D*>(handle);
|
||||
obj->initResource(handle.getId());
|
||||
TRACK_INCREMENT();
|
||||
return handle;
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
inline Handle<D> allocHandle() noexcept {
|
||||
return mHandleAllocatorImpl.allocate<D>();
|
||||
}
|
||||
|
||||
template<typename D, typename B, typename... ARGS>
|
||||
inline typename std::enable_if<std::is_base_of<B, D>::value, D>::type* construct(
|
||||
Handle<B> const& handle, ARGS&&... args) noexcept {
|
||||
auto obj = mHandleAllocatorImpl.construct<D, B>(handle, std::forward<ARGS>(args)...);
|
||||
obj->initResource(handle.getId());
|
||||
TRACK_INCREMENT();
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename Dp, typename B>
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B>& handle) noexcept {
|
||||
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
|
||||
}
|
||||
|
||||
template<typename Dp, typename B>
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B> const& handle) noexcept {
|
||||
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
|
||||
}
|
||||
|
||||
template<typename D, typename B>
|
||||
inline void destruct(Handle<B> handle) noexcept {
|
||||
auto obj = handle_cast<D*>(handle);
|
||||
TRACK_DECREMENT();
|
||||
mHandleAllocatorImpl.deallocate(handle, obj);
|
||||
}
|
||||
|
||||
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
|
||||
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
|
||||
}
|
||||
|
||||
private:
|
||||
AllocatorImpl mHandleAllocatorImpl;
|
||||
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
public:
|
||||
void print() {
|
||||
FVK_LOGD << "Resource Allocator state (debug only)" << utils::io::endl;
|
||||
for (size_t i = 0; i < RESOURCE_TYPE_COUNT; i++) {
|
||||
FVK_LOGD << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
|
||||
}
|
||||
FVK_LOGD << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
|
||||
}
|
||||
private:
|
||||
utils::FixedCapacityVector<size_t> mDebugOnlyResourceCount;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#undef TRACK_INCREMENT
|
||||
#undef TRACK_DECREMENT
|
||||
#undef DEBUG_RESOURCE_LEAKS
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 "VulkanResources.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanResourceAllocator.h"
|
||||
#include "VulkanPipelineCache.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type,
|
||||
HandleBase::HandleId id) {
|
||||
|
||||
if (IS_HEAP_ALLOC_TYPE(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case VulkanResourceType::BUFFER_OBJECT:
|
||||
allocator->destruct<VulkanBufferObject>(Handle<HwBufferObject>(id));
|
||||
break;
|
||||
case VulkanResourceType::INDEX_BUFFER:
|
||||
allocator->destruct<VulkanIndexBuffer>(Handle<HwIndexBuffer>(id));
|
||||
break;
|
||||
case VulkanResourceType::PROGRAM:
|
||||
allocator->destruct<VulkanProgram>(Handle<HwProgram>(id));
|
||||
break;
|
||||
case VulkanResourceType::RENDER_TARGET:
|
||||
allocator->destruct<VulkanRenderTarget>(Handle<HwRenderTarget>(id));
|
||||
break;
|
||||
case VulkanResourceType::SAMPLER_GROUP:
|
||||
allocator->destruct<VulkanSamplerGroup>(Handle<HwSamplerGroup>(id));
|
||||
break;
|
||||
case VulkanResourceType::SWAP_CHAIN:
|
||||
allocator->destruct<VulkanSwapChain>(Handle<HwSwapChain>(id));
|
||||
break;
|
||||
case VulkanResourceType::TEXTURE:
|
||||
allocator->destruct<VulkanTexture>(Handle<HwTexture>(id));
|
||||
break;
|
||||
case VulkanResourceType::TIMER_QUERY:
|
||||
allocator->destruct<VulkanTimerQuery>(Handle<HwTimerQuery>(id));
|
||||
break;
|
||||
case VulkanResourceType::VERTEX_BUFFER_INFO:
|
||||
allocator->destruct<VulkanVertexBufferInfo>(Handle<HwVertexBufferInfo>(id));
|
||||
break;
|
||||
case VulkanResourceType::VERTEX_BUFFER:
|
||||
allocator->destruct<VulkanVertexBuffer>(Handle<HwVertexBuffer>(id));
|
||||
break;
|
||||
case VulkanResourceType::RENDER_PRIMITIVE:
|
||||
allocator->destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
|
||||
break;
|
||||
case VulkanResourceType::DESCRIPTOR_SET_LAYOUT:
|
||||
allocator->destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
|
||||
break;
|
||||
case VulkanResourceType::DESCRIPTOR_SET:
|
||||
allocator->destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
|
||||
break;
|
||||
|
||||
// If the resource is heap allocated, then the resource manager just skip refcounted
|
||||
// destruction.
|
||||
case VulkanResourceType::FENCE:
|
||||
case VulkanResourceType::HEAP_ALLOCATED:
|
||||
case VulkanResourceType::END_TYPE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,313 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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_VULKANRESOURCES_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
|
||||
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <tsl/robin_set.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanResourceAllocator;
|
||||
struct VulkanThreadSafeResource;
|
||||
|
||||
// Subclasses of VulkanResource must provide this enum in their construction.
|
||||
enum class VulkanResourceType : uint8_t {
|
||||
BUFFER_OBJECT = 0,
|
||||
INDEX_BUFFER = 1,
|
||||
PROGRAM = 2,
|
||||
RENDER_TARGET = 3,
|
||||
SAMPLER_GROUP = 4,
|
||||
SWAP_CHAIN = 5,
|
||||
RENDER_PRIMITIVE = 6,
|
||||
TEXTURE = 7,
|
||||
TIMER_QUERY = 8,
|
||||
VERTEX_BUFFER = 9,
|
||||
VERTEX_BUFFER_INFO = 10,
|
||||
DESCRIPTOR_SET_LAYOUT = 11,
|
||||
DESCRIPTOR_SET = 12,
|
||||
|
||||
// Below are resources that are managed manually (i.e. not ref counted).
|
||||
FENCE = 13,
|
||||
HEAP_ALLOCATED = 14,
|
||||
|
||||
END_TYPE = 15, // A placeholder
|
||||
};
|
||||
|
||||
#define IS_HEAP_ALLOC_TYPE(f) \
|
||||
(f == VulkanResourceType::FENCE || f == VulkanResourceType::HEAP_ALLOCATED)
|
||||
|
||||
|
||||
// This is a ref-counting base class that tracks how many references of this resource exist. This
|
||||
// class is paired with VulkanResourceManagerImpl which is responsible for incrementing or
|
||||
// decrementing the count. Once mRefCount == 0, VulkanResourceManagerImpl will also call the
|
||||
// appropriate destructor. VulkanCommandBuffer, VulkanDriver, and composite structure like
|
||||
// VulkanRenderPrimitive are owners of VulkanResourceManagerImpl instances.
|
||||
struct VulkanResourceBase {
|
||||
protected:
|
||||
explicit VulkanResourceBase(VulkanResourceType type)
|
||||
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
|
||||
mType(uint32_t(type)),
|
||||
mHandleId(0) {
|
||||
}
|
||||
|
||||
private:
|
||||
inline VulkanResourceType getType() const noexcept {
|
||||
return VulkanResourceType(mType);
|
||||
}
|
||||
|
||||
inline HandleBase::HandleId getId() const noexcept {
|
||||
return mHandleId;
|
||||
}
|
||||
|
||||
inline void initResource(HandleBase::HandleId id) noexcept {
|
||||
mHandleId = id;
|
||||
}
|
||||
|
||||
inline void ref() noexcept {
|
||||
if (IS_HEAP_ALLOC_TYPE(getType())) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mRefCount < ((1<<24) - 1));
|
||||
++mRefCount;
|
||||
}
|
||||
|
||||
inline void deref() noexcept {
|
||||
if (IS_HEAP_ALLOC_TYPE(getType())) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mRefCount > 0);
|
||||
--mRefCount;
|
||||
}
|
||||
|
||||
inline size_t refcount() const noexcept {
|
||||
return mRefCount;
|
||||
}
|
||||
|
||||
uint32_t mRefCount : 24; // 16M is enough for the refcount
|
||||
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
|
||||
HandleBase::HandleId mHandleId;
|
||||
|
||||
friend struct VulkanThreadSafeResource;
|
||||
friend class VulkanResourceAllocator;
|
||||
|
||||
template<typename RT, typename ST>
|
||||
friend class VulkanResourceManagerImpl;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
|
||||
|
||||
struct VulkanThreadSafeResource {
|
||||
protected:
|
||||
explicit VulkanThreadSafeResource(VulkanResourceType type)
|
||||
: mImpl(type) {}
|
||||
|
||||
private:
|
||||
inline VulkanResourceType getType() {
|
||||
return mImpl.getType();
|
||||
}
|
||||
|
||||
inline HandleBase::HandleId getId() {
|
||||
return mImpl.getId();
|
||||
}
|
||||
|
||||
inline void initResource(HandleBase::HandleId id) noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
mImpl.initResource(id);
|
||||
}
|
||||
|
||||
inline void ref() noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
mImpl.ref();
|
||||
}
|
||||
|
||||
inline void deref() noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
mImpl.deref();
|
||||
}
|
||||
|
||||
inline size_t refcount() noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
return mImpl.refcount();
|
||||
}
|
||||
|
||||
utils::Mutex mMutex;
|
||||
VulkanResourceBase mImpl;
|
||||
|
||||
friend class VulkanResourceAllocator;
|
||||
template<typename RT, typename ST>
|
||||
friend class VulkanResourceManagerImpl;
|
||||
};
|
||||
|
||||
using VulkanResource = VulkanResourceBase;
|
||||
|
||||
namespace {
|
||||
|
||||
// When the size of the resource set is known to be small, (for example for VulkanRenderPrimitive),
|
||||
// we just use a std::array to back the set.
|
||||
template<std::size_t SIZE>
|
||||
using FixedCapacityResourceSet = CappedArray<VulkanResource*, SIZE>;
|
||||
|
||||
// robin_set/map are useful for sets that are acquire only and the set will be iterated when the set
|
||||
// is cleared.
|
||||
using FastIterationResourceSet = tsl::robin_set<VulkanResource*>;
|
||||
|
||||
// unoredered_set is used in the general case where insert/erase can occur at will. This is useful
|
||||
// for the basic object ownership count - i.e. VulkanDriver.
|
||||
using ResourceSet = std::unordered_set<VulkanResource*>;
|
||||
|
||||
using ThreadSafeResourceSet = std::unordered_set<VulkanThreadSafeResource*>;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class VulkanResourceAllocator;
|
||||
|
||||
#define LOCK_IF_NEEDED() \
|
||||
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) { \
|
||||
mMutex->lock(); \
|
||||
}
|
||||
|
||||
#define UNLOCK_IF_NEEDED() \
|
||||
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) { \
|
||||
mMutex->unlock(); \
|
||||
}
|
||||
|
||||
void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type,
|
||||
HandleBase::HandleId id);
|
||||
|
||||
template<typename ResourceType, typename SetType>
|
||||
class VulkanResourceManagerImpl {
|
||||
public:
|
||||
explicit VulkanResourceManagerImpl(VulkanResourceAllocator* allocator)
|
||||
: mAllocator(allocator) {
|
||||
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) {
|
||||
mMutex = std::make_unique<utils::Mutex>();
|
||||
}
|
||||
}
|
||||
|
||||
VulkanResourceManagerImpl(const VulkanResourceManagerImpl& other) = delete;
|
||||
void operator=(const VulkanResourceManagerImpl& other) = delete;
|
||||
VulkanResourceManagerImpl(const VulkanResourceManagerImpl&& other) = delete;
|
||||
void operator=(const VulkanResourceManagerImpl&& other) = delete;
|
||||
|
||||
~VulkanResourceManagerImpl() {
|
||||
clear();
|
||||
}
|
||||
|
||||
inline void acquire(ResourceType* resource) {
|
||||
if (IS_HEAP_ALLOC_TYPE(resource->getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK_IF_NEEDED();
|
||||
if (mResources.find(resource) != mResources.end()) {
|
||||
UNLOCK_IF_NEEDED();
|
||||
return;
|
||||
}
|
||||
mResources.insert(resource);
|
||||
UNLOCK_IF_NEEDED();
|
||||
resource->ref();
|
||||
}
|
||||
|
||||
// Transfers ownership from one resource set to another
|
||||
template <typename tSetType>
|
||||
inline void acquireAll(VulkanResourceManagerImpl<ResourceType, tSetType>* srcResources) {
|
||||
copyAll(srcResources);
|
||||
srcResources->clear();
|
||||
}
|
||||
|
||||
// Transfers ownership from one resource set to another
|
||||
template <typename tSetType>
|
||||
inline void copyAll(VulkanResourceManagerImpl<ResourceType, tSetType>* srcResources) {
|
||||
LOCK_IF_NEEDED();
|
||||
for (auto iter = srcResources->mResources.begin(); iter != srcResources->mResources.end();
|
||||
iter++) {
|
||||
acquire(*iter);
|
||||
}
|
||||
UNLOCK_IF_NEEDED();
|
||||
}
|
||||
|
||||
inline void release(ResourceType* resource) {
|
||||
if (IS_HEAP_ALLOC_TYPE(resource->getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK_IF_NEEDED();
|
||||
auto resItr = mResources.find(resource);
|
||||
if (resItr == mResources.end()) {
|
||||
UNLOCK_IF_NEEDED();
|
||||
return;
|
||||
}
|
||||
mResources.erase(resItr);
|
||||
UNLOCK_IF_NEEDED();
|
||||
derefImpl(resource);
|
||||
}
|
||||
|
||||
inline void clear() {
|
||||
LOCK_IF_NEEDED();
|
||||
for (auto iter = mResources.begin(); iter != mResources.end(); iter++) {
|
||||
derefImpl(*iter);
|
||||
}
|
||||
mResources.clear();
|
||||
UNLOCK_IF_NEEDED();
|
||||
}
|
||||
|
||||
inline size_t size() {
|
||||
return mResources.size();
|
||||
}
|
||||
|
||||
private:
|
||||
inline void derefImpl(ResourceType* resource) {
|
||||
resource->deref();
|
||||
if (resource->refcount() != 0) {
|
||||
return;
|
||||
}
|
||||
deallocateResource(mAllocator, resource->getType(), resource->getId());
|
||||
}
|
||||
|
||||
VulkanResourceAllocator* mAllocator;
|
||||
SetType mResources;
|
||||
std::unique_ptr<utils::Mutex> mMutex;
|
||||
|
||||
template <typename, typename> friend class VulkanResourceManagerImpl;
|
||||
};
|
||||
|
||||
using VulkanAcquireOnlyResourceManager
|
||||
= VulkanResourceManagerImpl<VulkanResource, FastIterationResourceSet>;
|
||||
using VulkanResourceManager = VulkanResourceManagerImpl<VulkanResource, ResourceSet>;
|
||||
|
||||
template<std::size_t SIZE>
|
||||
using FixedSizeVulkanResourceManager =
|
||||
VulkanResourceManagerImpl<VulkanResource, FixedCapacityResourceSet<SIZE>>;
|
||||
|
||||
using VulkanThreadSafeResourceManager
|
||||
= VulkanResourceManagerImpl<VulkanThreadSafeResource, ThreadSafeResourceSet>;
|
||||
|
||||
#undef LOCK_IF_NEEDED
|
||||
#undef UNLOCK_IF_NEEDED
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "VulkanStagePool.h"
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanMemory.h"
|
||||
@@ -37,6 +38,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
if (iter != mFreeStages.end()) {
|
||||
auto stage = iter->second;
|
||||
mFreeStages.erase(iter);
|
||||
stage->lastAccessed = mCurrentFrame;
|
||||
mUsedStages.insert(stage);
|
||||
return stage;
|
||||
}
|
||||
@@ -59,7 +61,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
UTILS_UNUSED_IN_RELEASE VkResult result = vmaCreateBuffer(mAllocator, &bufferInfo,
|
||||
&allocInfo, &stage->buffer, &stage->memory, nullptr);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
#if FVK_ENABLED(FVK_DEBUG_STAGING_ALLOCATION)
|
||||
if (result != VK_SUCCESS) {
|
||||
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
|
||||
}
|
||||
@@ -74,6 +76,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
|
||||
for (auto image : mFreeImages) {
|
||||
if (image->format == vkformat && image->width == width && image->height == height) {
|
||||
mFreeImages.erase(image);
|
||||
image->lastAccessed = mCurrentFrame;
|
||||
mUsedImages.insert(image);
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "backend/DriverEnums.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
#include <map>
|
||||
@@ -26,6 +25,8 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanCommands;
|
||||
|
||||
// Immutable POD representing a shared CPU-GPU staging area.
|
||||
struct VulkanStage {
|
||||
VmaAllocation memory;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
#include "VulkanSwapChain.h"
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
@@ -26,19 +28,18 @@ using namespace utils;
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
|
||||
VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator,
|
||||
VulkanStagePool& stagePool,
|
||||
void* nativeWindow, uint64_t flags, VkExtent2D extent)
|
||||
: VulkanResource(VulkanResourceType::SWAP_CHAIN),
|
||||
mPlatform(platform),
|
||||
fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
|
||||
VkExtent2D extent)
|
||||
: mPlatform(platform),
|
||||
mResourceManager(resourceManager),
|
||||
mCommands(commands),
|
||||
mAllocator(allocator),
|
||||
mHandleAllocator(handleAllocator),
|
||||
mStagePool(stagePool),
|
||||
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
|
||||
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
|
||||
mTransitionSwapChainImageLayoutForPresent(
|
||||
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
|
||||
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
|
||||
mAcquired(false),
|
||||
mIsFirstRenderPass(true) {
|
||||
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
|
||||
@@ -53,6 +54,9 @@ VulkanSwapChain::~VulkanSwapChain() {
|
||||
mCommands->flush();
|
||||
mCommands->wait();
|
||||
|
||||
mColors = {};
|
||||
mDepth = {};
|
||||
|
||||
mPlatform->destroy(swapChain);
|
||||
}
|
||||
|
||||
@@ -70,13 +74,16 @@ void VulkanSwapChain::update() {
|
||||
colorUsage |= TextureUsage::PROTECTED;
|
||||
}
|
||||
for (auto const color: bundle.colors) {
|
||||
mColors.push_back(std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
|
||||
color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height,
|
||||
colorUsage, mStagePool, true /* heap allocated */));
|
||||
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
|
||||
device, mAllocator, mResourceManager, mCommands, color, bundle.colorFormat, 1,
|
||||
bundle.extent.width, bundle.extent.height, TextureUsage::COLOR_ATTACHMENT,
|
||||
mStagePool);
|
||||
mColors.push_back(colorTexture);
|
||||
}
|
||||
mDepth = std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
|
||||
bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height,
|
||||
depthUsage, mStagePool, true /* heap allocated */);
|
||||
|
||||
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device, mAllocator,
|
||||
mResourceManager, mCommands, bundle.depth, bundle.depthFormat, 1, bundle.extent.width,
|
||||
bundle.extent.height, TextureUsage::DEPTH_ATTACHMENT, mStagePool);
|
||||
|
||||
mExtent = bundle.extent;
|
||||
}
|
||||
@@ -95,7 +102,7 @@ void VulkanSwapChain::present() {
|
||||
}
|
||||
|
||||
mCommands->flush();
|
||||
|
||||
|
||||
// call the image ready wait function
|
||||
if (mExplicitImageReadyWait != nullptr) {
|
||||
mExplicitImageReadyWait(swapChain);
|
||||
@@ -135,7 +142,7 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
||||
mCurrentSwapIndex = imageSyncData.imageIndex;
|
||||
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
<< "Cannot acquire in swapchain.";
|
||||
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
||||
mCommands->injectDependency(imageSyncData.imageReadySemaphore);
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
@@ -36,14 +36,14 @@ namespace filament::backend {
|
||||
|
||||
struct VulkanHeadlessSwapChain;
|
||||
struct VulkanSurfaceSwapChain;
|
||||
class VulkanResourceAllocator;
|
||||
class VulkanCommands;
|
||||
|
||||
// A wrapper around the platform implementation of swapchain.
|
||||
struct VulkanSwapChain : public HwSwapChain, VulkanResource {
|
||||
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator,
|
||||
VulkanStagePool& stagePool,
|
||||
void* nativeWindow, uint64_t flags, VkExtent2D extent = {0, 0});
|
||||
struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
|
||||
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
|
||||
fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow,
|
||||
uint64_t flags, VkExtent2D extent = {0, 0});
|
||||
|
||||
~VulkanSwapChain();
|
||||
|
||||
@@ -51,15 +51,15 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
|
||||
|
||||
void acquire(bool& reized);
|
||||
|
||||
inline VulkanTexture* getCurrentColor() const noexcept {
|
||||
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
|
||||
uint32_t const imageIndex = mCurrentSwapIndex;
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
imageIndex != VulkanPlatform::ImageSyncData::INVALID_IMAGE_INDEX);
|
||||
return mColors[imageIndex].get();
|
||||
return mColors[imageIndex];
|
||||
}
|
||||
|
||||
inline VulkanTexture* getDepth() const noexcept {
|
||||
return mDepth.get();
|
||||
inline fvkmemory::resource_ptr<VulkanTexture> getDepth() const noexcept {
|
||||
return mDepth;
|
||||
}
|
||||
|
||||
inline bool isFirstRenderPass() const noexcept {
|
||||
@@ -83,9 +83,9 @@ private:
|
||||
void update();
|
||||
|
||||
VulkanPlatform* mPlatform;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
VulkanCommands* mCommands;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanResourceAllocator* const mHandleAllocator;
|
||||
VulkanStagePool& mStagePool;
|
||||
bool const mHeadless;
|
||||
bool const mFlushAndWaitOnResize;
|
||||
@@ -93,8 +93,8 @@ private:
|
||||
|
||||
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
|
||||
// transitions, which are useful here.
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
|
||||
std::unique_ptr<VulkanTexture> mDepth;
|
||||
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
|
||||
fvkmemory::resource_ptr<VulkanTexture> mDepth;
|
||||
VkExtent2D mExtent;
|
||||
uint32_t mCurrentSwapIndex;
|
||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanResourceAllocator.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <DataReshaper.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -149,8 +150,7 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
|
||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
|
||||
bool isProtected)
|
||||
: VulkanResource(VulkanResourceType::HEAP_ALLOCATED),
|
||||
mVkFormat(format),
|
||||
: mVkFormat(format),
|
||||
mViewType(viewType),
|
||||
mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount},
|
||||
mDefaultLayout(defaultLayout),
|
||||
@@ -161,61 +161,42 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
||||
mCommands(commands),
|
||||
mIsTransientAttachment(false) {}
|
||||
|
||||
VulkanTextureState* VulkanTexture::getSharedState() {
|
||||
VulkanTextureState* state = mAllocator->handle_cast<VulkanTextureState*>(mState);
|
||||
return state;
|
||||
}
|
||||
|
||||
VulkanTextureState const* VulkanTexture::getSharedState() const {
|
||||
VulkanTextureState const* state = mAllocator->handle_cast<VulkanTextureState const*>(mState);
|
||||
return state;
|
||||
}
|
||||
|
||||
// Constructor for internally passed VkImage
|
||||
VulkanTexture::VulkanTexture(
|
||||
VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
|
||||
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
|
||||
tusage),
|
||||
VulkanResource(
|
||||
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
|
||||
mAllocator(handleAllocator),
|
||||
mState(handleAllocator->initHandle<VulkanTextureState>(
|
||||
device, allocator, commands, stagePool,
|
||||
format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1,
|
||||
getDefaultLayoutImpl(tusage),
|
||||
any(usage& TextureUsage::PROTECTED))) {
|
||||
auto* const state = getSharedState();
|
||||
state->mTextureImage = image;
|
||||
mPrimaryViewRange = state->mFullViewRange;
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
|
||||
VulkanStagePool& stagePool)
|
||||
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
|
||||
tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
||||
allocator, commands, stagePool, format, imgutil::getViewType(SamplerType::SAMPLER_2D),
|
||||
1, 1, getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
|
||||
mState->mTextureImage = image;
|
||||
mPrimaryViewRange = mState->mFullViewRange;
|
||||
}
|
||||
|
||||
// Constructor for user facing texture
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels,
|
||||
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
|
||||
VulkanContext const& context, VmaAllocator allocator,
|
||||
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),
|
||||
VulkanResource(
|
||||
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
|
||||
mAllocator(handleAllocator),
|
||||
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
|
||||
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
|
||||
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) {
|
||||
auto* const state = getSharedState();
|
||||
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
||||
allocator, commands, stagePool, backend::getVkFormat(tformat),
|
||||
imgutil::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.
|
||||
VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
|
||||
.format = state->mVkFormat,
|
||||
.extent = {w, h, depth},
|
||||
.mipLevels = levels,
|
||||
.arrayLayers = 1,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = 0};
|
||||
VkImageCreateInfo imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
|
||||
.format = mState->mVkFormat,
|
||||
.extent = {w, h, depth},
|
||||
.mipLevels = levels,
|
||||
.arrayLayers = 1,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = 0,
|
||||
};
|
||||
if (target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
imageInfo.arrayLayers = 6;
|
||||
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
@@ -254,7 +235,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(tusage & TextureUsage::ALL_ATTACHMENTS);
|
||||
state->mIsTransientAttachment = useTransientAttachment;
|
||||
mState->mIsTransientAttachment = useTransientAttachment;
|
||||
|
||||
const VkImageUsageFlags transientFlag =
|
||||
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
|
||||
@@ -299,7 +280,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
// any kind of attachment (color or depth).
|
||||
const auto& limits = context.getPhysicalDeviceLimits();
|
||||
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
samples = reduceSampleCount(samples, isVkDepthFormat(state->mVkFormat)
|
||||
samples = reduceSampleCount(samples, isVkDepthFormat(mState->mVkFormat)
|
||||
? limits.sampledImageDepthSampleCounts
|
||||
: limits.sampledImageColorSampleCounts);
|
||||
}
|
||||
@@ -313,12 +294,12 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
this->samples = samples;
|
||||
imageInfo.samples = (VkSampleCountFlagBits) samples;
|
||||
|
||||
VkResult error = vkCreateImage(state->mDevice, &imageInfo, VKALLOC, &state->mTextureImage);
|
||||
VkResult error = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
|
||||
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
|
||||
FVK_LOGD << "vkCreateImage: "
|
||||
<< "image = " << state->mTextureImage << ", "
|
||||
<< "image = " << mState->mTextureImage << ", "
|
||||
<< "result = " << error << ", "
|
||||
<< "handle = " << utils::io::hex << state->mTextureImage << 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>(usage) << ", "
|
||||
@@ -327,18 +308,18 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
<< "type = " << imageInfo.imageType << ", "
|
||||
<< "flags = " << imageInfo.flags << ", "
|
||||
<< "target = " << static_cast<int>(target) <<", "
|
||||
<< "format = " << state->mVkFormat << utils::io::endl;
|
||||
<< "format = " << mState->mVkFormat << utils::io::endl;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image.";
|
||||
|
||||
// Allocate memory for the VkImage and bind it.
|
||||
VkMemoryRequirements memReqs = {};
|
||||
vkGetImageMemoryRequirements(state->mDevice, state->mTextureImage, &memReqs);
|
||||
vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
|
||||
|
||||
const VkFlags requiredMemoryFlags =
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
|
||||
(state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
@@ -350,37 +331,29 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
error = vkAllocateMemory(state->mDevice, &allocInfo, nullptr, &state->mTextureImageMemory);
|
||||
error = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory.";
|
||||
error = vkBindImageMemory(state->mDevice, state->mTextureImage, state->mTextureImageMemory, 0);
|
||||
error = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
|
||||
0);
|
||||
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image.";
|
||||
|
||||
// Spec out the "primary" VkImageView that shaders use to sample from the image.
|
||||
mPrimaryViewRange = state->mFullViewRange;
|
||||
mPrimaryViewRange = mState->mFullViewRange;
|
||||
|
||||
// Go ahead and create the primary image view.
|
||||
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
|
||||
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
|
||||
|
||||
VulkanCommandBuffer& commandsBuf = state->mCommands->get();
|
||||
commandsBuf.acquire(this);
|
||||
|
||||
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
||||
transitionLayout(&commandsBuf, mPrimaryViewRange, defaultLayout);
|
||||
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
||||
}
|
||||
|
||||
// Constructor for creating a texture view
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator, VulkanTexture const* src, uint8_t baseLevel,
|
||||
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),
|
||||
VulkanResource(VulkanResourceType::TEXTURE),
|
||||
mAllocator(handleAllocator) {
|
||||
src->format, src->usage) {
|
||||
mState = src->mState;
|
||||
auto* state = getSharedState();
|
||||
|
||||
state->refs++;
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
|
||||
mPrimaryViewRange.levelCount = levelCount;
|
||||
@@ -389,31 +362,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
// Constructor for creating a texture view with swizzle
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator, VulkanTexture const* src,
|
||||
VkComponentMapping swizzle)
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
VulkanResource(VulkanResourceType::TEXTURE),
|
||||
mAllocator(handleAllocator) {
|
||||
src->format, src->usage) {
|
||||
mState = src->mState;
|
||||
auto* state = getSharedState();
|
||||
state->refs++;
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
|
||||
}
|
||||
|
||||
VulkanTexture::~VulkanTexture() {
|
||||
auto* const state = getSharedState();
|
||||
state->refs--;
|
||||
if (state->refs == 0) {
|
||||
if (state->mTextureImageMemory != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(state->mDevice, state->mTextureImage, VKALLOC);
|
||||
vkFreeMemory(state->mDevice, state->mTextureImageMemory, VKALLOC);
|
||||
}
|
||||
for (auto entry: state->mCachedImageViews) {
|
||||
vkDestroyImageView(state->mDevice, entry.second, VKALLOC);
|
||||
}
|
||||
mAllocator->destruct<VulkanTextureState>(mState);
|
||||
VulkanTextureState::~VulkanTextureState() {
|
||||
if (mTextureImageMemory != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
|
||||
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
|
||||
}
|
||||
for (auto entry: mCachedImageViews) {
|
||||
vkDestroyImageView(mDevice, entry.second, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,8 +385,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
assert_invariant(width <= this->width && height <= this->height);
|
||||
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
|
||||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
|
||||
auto* const state = getSharedState();
|
||||
assert_invariant(state->mIsProtected == false);
|
||||
assert_invariant(!mState->mIsProtected);
|
||||
const PixelBufferDescriptor* hostData = &data;
|
||||
PixelBufferDescriptor reshapedData;
|
||||
|
||||
@@ -436,7 +398,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
|
||||
// If format conversion is both required and supported, use vkCmdBlitImage.
|
||||
const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type);
|
||||
const VkFormat deviceFormat = getVkFormatLinear(state->mVkFormat);
|
||||
const VkFormat deviceFormat = getVkFormatLinear(mState->mVkFormat);
|
||||
if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) {
|
||||
assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 &&
|
||||
"Offsets not yet supported when format conversion is required.");
|
||||
@@ -448,16 +410,16 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
|
||||
// Otherwise, use vkCmdCopyBufferToImage.
|
||||
void* mapped = nullptr;
|
||||
VulkanStage const* stage = state->mStagePool.acquireStage(hostData->size);
|
||||
VulkanStage const* stage = mState->mStagePool.acquireStage(hostData->size);
|
||||
assert_invariant(stage->memory);
|
||||
vmaMapMemory(state->mAllocator, stage->memory, &mapped);
|
||||
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
|
||||
memcpy(mapped, hostData->buffer, hostData->size);
|
||||
vmaUnmapMemory(state->mAllocator, stage->memory);
|
||||
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
|
||||
vmaUnmapMemory(mState->mAllocator, stage->memory);
|
||||
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData->size);
|
||||
|
||||
VulkanCommandBuffer& commands = state->mCommands->get();
|
||||
VulkanCommandBuffer& commands = mState->mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
commands.acquire(this);
|
||||
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
|
||||
|
||||
VkBufferImageCopy copyRegion = {
|
||||
.bufferOffset = {},
|
||||
@@ -503,25 +465,24 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
|
||||
transitionLayout(&commands, transitionRange, newLayout);
|
||||
|
||||
vkCmdCopyBufferToImage(cmdbuf, stage->buffer, state->mTextureImage, newVkLayout, 1, ©Region);
|
||||
vkCmdCopyBufferToImage(cmdbuf, stage->buffer, mState->mTextureImage, newVkLayout, 1, ©Region);
|
||||
|
||||
transitionLayout(&commands, transitionRange, nextLayout);
|
||||
}
|
||||
|
||||
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
|
||||
uint32_t height, uint32_t depth, uint32_t miplevel) {
|
||||
auto* const state = getSharedState();
|
||||
void* mapped = nullptr;
|
||||
VulkanStageImage const* stage
|
||||
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
|
||||
vmaMapMemory(state->mAllocator, stage->memory, &mapped);
|
||||
= mState->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
|
||||
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
|
||||
memcpy(mapped, hostData.buffer, hostData.size);
|
||||
vmaUnmapMemory(state->mAllocator, stage->memory);
|
||||
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData.size);
|
||||
vmaUnmapMemory(mState->mAllocator, stage->memory);
|
||||
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData.size);
|
||||
|
||||
VulkanCommandBuffer& commands = state->mCommands->get();
|
||||
VulkanCommandBuffer& commands = mState->mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
commands.acquire(this);
|
||||
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
|
||||
|
||||
// TODO: support blit-based format conversion for 3D images and cubemaps.
|
||||
const int layer = 0;
|
||||
@@ -544,14 +505,13 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
|
||||
transitionLayout(&commands, range, newLayout);
|
||||
|
||||
vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
state->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
|
||||
mState->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
|
||||
|
||||
transitionLayout(&commands, range, oldLayout);
|
||||
}
|
||||
|
||||
VulkanLayout VulkanTexture::getDefaultLayout() const {
|
||||
auto* const state = getSharedState();
|
||||
return state->mDefaultLayout;
|
||||
return mState->mDefaultLayout;
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
||||
@@ -570,42 +530,43 @@ VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range,
|
||||
|
||||
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle) {
|
||||
auto* const state = getSharedState();
|
||||
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
|
||||
auto iter = state->mCachedImageViews.find(key);
|
||||
if (iter != state->mCachedImageViews.end()) {
|
||||
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 = state->mTextureImage,
|
||||
.image = mState->mTextureImage,
|
||||
.viewType = viewType,
|
||||
.format = state->mVkFormat,
|
||||
.format = mState->mVkFormat,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
vkCreateImageView(state->mDevice, &viewInfo, VKALLOC, &imageView);
|
||||
state->mCachedImageViews.emplace(key, imageView);
|
||||
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
|
||||
mState->mCachedImageViews.emplace(key, imageView);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkImageAspectFlags VulkanTexture::getImageAspect() const {
|
||||
// Helper function in VulkanUtility
|
||||
auto* const state = getSharedState();
|
||||
return filament::backend::getImageAspect(state->mVkFormat);
|
||||
return filament::backend::getImageAspect(mState->mVkFormat);
|
||||
}
|
||||
|
||||
bool VulkanTexture::transitionLayout(VulkanCommandBuffer* commands,
|
||||
VkImageSubresourceRange const& range, VulkanLayout newLayout) {
|
||||
return transitionLayout(commands->buffer(), range, newLayout);
|
||||
if (transitionLayout(commands->buffer(), range, newLayout)) {
|
||||
commands->acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceRange const& range,
|
||||
VulkanLayout newLayout) {
|
||||
auto* const state = getSharedState();
|
||||
VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel);
|
||||
|
||||
uint32_t const firstLayer = range.baseArrayLayer;
|
||||
@@ -636,7 +597,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
|
||||
continue;
|
||||
}
|
||||
hasTransitions = hasTransitions || imgutil::transitionLayout(cmdbuf, {
|
||||
.image = state->mTextureImage,
|
||||
.image = mState->mTextureImage,
|
||||
.oldLayout = layout,
|
||||
.newLayout = newLayout,
|
||||
.subresources = {
|
||||
@@ -651,7 +612,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
|
||||
}
|
||||
} else if (newLayout != oldLayout) {
|
||||
hasTransitions = imgutil::transitionLayout(cmdbuf, {
|
||||
.image = state->mTextureImage,
|
||||
.image = mState->mTextureImage,
|
||||
.oldLayout = oldLayout,
|
||||
.newLayout = newLayout,
|
||||
.subresources = range,
|
||||
@@ -684,7 +645,6 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
|
||||
void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
|
||||
VkImageSubresourceRange const& range) {
|
||||
VkCommandBuffer const cmdbuf = commands->buffer();
|
||||
auto* const state = getSharedState();
|
||||
VkImageLayout const layout =
|
||||
imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
|
||||
VkImageMemoryBarrier barrier = {
|
||||
@@ -696,7 +656,7 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
|
||||
.newLayout = layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = state->mTextureImage,
|
||||
.image = mState->mTextureImage,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
@@ -708,7 +668,6 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
|
||||
void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
|
||||
VkImageSubresourceRange const& range) {
|
||||
VkCommandBuffer const cmdbuf = commands->buffer();
|
||||
auto* const state = getSharedState();
|
||||
VkImageLayout const layout
|
||||
= imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
|
||||
VkImageMemoryBarrier barrier = {
|
||||
@@ -719,7 +678,7 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
|
||||
.newLayout = layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = state->mTextureImage,
|
||||
.image = mState->mTextureImage,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
@@ -727,7 +686,6 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
|
||||
}
|
||||
|
||||
void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout) {
|
||||
auto* const state = getSharedState();
|
||||
uint32_t const firstLayer = range.baseArrayLayer;
|
||||
uint32_t const lastLayer = firstLayer + range.layerCount;
|
||||
uint32_t const firstLevel = range.baseMipLevel;
|
||||
@@ -740,34 +698,32 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
|
||||
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
|
||||
uint32_t const first = (layer << 16) | firstLevel;
|
||||
uint32_t const last = (layer << 16) | lastLevel;
|
||||
state->mSubresourceLayouts.clear(first, last);
|
||||
mState->mSubresourceLayouts.clear(first, last);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
|
||||
uint32_t const first = (layer << 16) | firstLevel;
|
||||
uint32_t const last = (layer << 16) | lastLevel;
|
||||
state->mSubresourceLayouts.add(first, last, newLayout);
|
||||
mState->mSubresourceLayouts.add(first, last, newLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
|
||||
assert_invariant(level <= 0xffff && layer <= 0xffff);
|
||||
auto* const state = getSharedState();
|
||||
const uint32_t key = (layer << 16) | level;
|
||||
if (!state->mSubresourceLayouts.has(key)) {
|
||||
if (!mState->mSubresourceLayouts.has(key)) {
|
||||
return VulkanLayout::UNDEFINED;
|
||||
}
|
||||
return state->mSubresourceLayouts.get(key);
|
||||
return mState->mSubresourceLayouts.get(key);
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
void VulkanTexture::print() const {
|
||||
auto* const state = getSharedState();
|
||||
uint32_t const firstLayer = 0;
|
||||
uint32_t const lastLayer = firstLayer + state->mFullViewRange.layerCount;
|
||||
uint32_t const lastLayer = firstLayer + mState->mFullViewRange.layerCount;
|
||||
uint32_t const firstLevel = 0;
|
||||
uint32_t const lastLevel = firstLevel + state->mFullViewRange.levelCount;
|
||||
uint32_t const lastLevel = firstLevel + mState->mFullViewRange.levelCount;
|
||||
|
||||
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
|
||||
for (uint32_t level = firstLevel; level < lastLevel; ++level) {
|
||||
@@ -776,16 +732,16 @@ void VulkanTexture::print() const {
|
||||
layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) &&
|
||||
level >= mPrimaryViewRange.baseMipLevel &&
|
||||
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
|
||||
FVK_LOGD << "[" << state->mTextureImage << "]: (" << layer << "," << level
|
||||
FVK_LOGD << "[" << mState->mTextureImage << "]: (" << layer << "," << level
|
||||
<< ")=" << getLayout(layer, level)
|
||||
<< " primary=" << primary
|
||||
<< utils::io::endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto view: state->mCachedImageViews) {
|
||||
for (auto view: mState->mCachedImageViews) {
|
||||
auto& range = view.first.range;
|
||||
FVK_LOGD << "[" << state->mTextureImage << ", imageView=" << view.second << "]=>"
|
||||
FVK_LOGD << "[" << mState->mTextureImage << ", imageView=" << view.second << "]=>"
|
||||
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
|
||||
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
|
||||
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanBuffer.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/RangeMap.h>
|
||||
@@ -30,13 +31,13 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanResourceAllocator;
|
||||
|
||||
struct VulkanTextureState : public VulkanResource {
|
||||
struct VulkanTextureState : public fvkmemory::Resource {
|
||||
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
||||
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
|
||||
|
||||
~VulkanTextureState();
|
||||
|
||||
struct ImageViewKey {
|
||||
VkImageSubresourceRange range; // 4 * 5 bytes
|
||||
VkImageViewType type; // 4 bytes
|
||||
@@ -58,10 +59,8 @@ struct VulkanTextureState : public VulkanResource {
|
||||
|
||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
||||
|
||||
uint32_t refs = 1;
|
||||
|
||||
// The texture with the sidecar owns the sidecar.
|
||||
std::unique_ptr<VulkanTexture> mSidecarMSAA;
|
||||
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
|
||||
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkFormat const mVkFormat;
|
||||
@@ -83,37 +82,32 @@ struct VulkanTextureState : public VulkanResource {
|
||||
bool mIsTransientAttachment;
|
||||
};
|
||||
|
||||
|
||||
struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
// Standard constructor for user-facing textures.
|
||||
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
|
||||
VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
SamplerType target, uint8_t levels,
|
||||
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false);
|
||||
VmaAllocator allocator, 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);
|
||||
|
||||
// Specialized constructor for internally created textures (e.g. from a swap chain)
|
||||
// The texture will never destroy the given VkImage, but it does manages its subresources.
|
||||
VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
VkImage image,
|
||||
VulkanTexture(VkDevice device, VmaAllocator allocator,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
|
||||
VulkanStagePool& stagePool, bool heapAllocated = false);
|
||||
VulkanStagePool& stagePool);
|
||||
|
||||
// Constructor for creating a texture view for wrt specific mip range
|
||||
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
|
||||
VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount);
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel, uint8_t levelCount);
|
||||
|
||||
// Constructor for creating a texture view for swizzle.
|
||||
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
|
||||
VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
VulkanTexture const* src, VkComponentMapping swizzle);
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle);
|
||||
|
||||
~VulkanTexture();
|
||||
~VulkanTexture() = default;
|
||||
|
||||
// Uploads data into a subregion of a 2D or 3D texture.
|
||||
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
|
||||
@@ -121,16 +115,14 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
|
||||
// Returns the primary image view, which is used for shader sampling.
|
||||
VkImageView getPrimaryImageView() {
|
||||
VulkanTextureState* state = getSharedState();
|
||||
return getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
|
||||
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
|
||||
}
|
||||
|
||||
VkImageViewType getViewType() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mViewType;
|
||||
return mState->mViewType;
|
||||
}
|
||||
|
||||
VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; }
|
||||
VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; }
|
||||
|
||||
VulkanLayout getPrimaryImageLayout() const {
|
||||
return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel);
|
||||
@@ -156,34 +148,28 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
|
||||
|
||||
VkFormat getVkFormat() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mVkFormat;
|
||||
return mState->mVkFormat;
|
||||
}
|
||||
VkImage getVkImage() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mTextureImage;
|
||||
return mState->mTextureImage;
|
||||
}
|
||||
|
||||
VulkanLayout getLayout(uint32_t layer, uint32_t level) const;
|
||||
|
||||
void setSidecar(VulkanTexture* sidecar) {
|
||||
VulkanTextureState* state = getSharedState();
|
||||
state->mSidecarMSAA.reset(sidecar);
|
||||
void setSidecar(fvkmemory::resource_ptr<VulkanTexture> sidecar) {
|
||||
mState->mSidecarMSAA = sidecar;
|
||||
}
|
||||
|
||||
VulkanTexture* getSidecar() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mSidecarMSAA.get();
|
||||
fvkmemory::resource_ptr<VulkanTexture> getSidecar() const {
|
||||
return mState->mSidecarMSAA;
|
||||
}
|
||||
|
||||
bool isTransientAttachment() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mIsTransientAttachment;
|
||||
return mState->mIsTransientAttachment;
|
||||
}
|
||||
|
||||
bool getIsProtected() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mIsProtected;
|
||||
return mState->mIsProtected;
|
||||
}
|
||||
|
||||
bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range,
|
||||
@@ -211,9 +197,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
#endif
|
||||
|
||||
private:
|
||||
VulkanTextureState* getSharedState();
|
||||
VulkanTextureState const* getSharedState() const;
|
||||
|
||||
// Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and
|
||||
// swizzle (or not).
|
||||
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
@@ -222,9 +205,7 @@ private:
|
||||
void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t miplevel);
|
||||
|
||||
VulkanResourceAllocator* const mAllocator;
|
||||
|
||||
Handle<VulkanTextureState> mState;
|
||||
fvkmemory::resource_ptr<VulkanTextureState> mState;
|
||||
|
||||
// Track the range of subresources that define the "primary" image view, which is the special
|
||||
// image view that gets bound to an actual texture sampler.
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
#include "VulkanDescriptorSetManager.h"
|
||||
|
||||
#include <vulkan/VulkanCommands.h>
|
||||
#include <vulkan/VulkanHandles.h>
|
||||
#include <vulkan/VulkanUtility.h>
|
||||
#include <vulkan/VulkanConstants.h>
|
||||
#include <vulkan/VulkanImageUtility.h>
|
||||
#include <vulkan/VulkanResources.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -274,7 +274,7 @@ public:
|
||||
DescriptorInfinitePool(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkDescriptorSet obtainSet(VulkanDescriptorSetLayout* layout) {
|
||||
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto const vklayout = layout->getVkLayout();
|
||||
DescriptorPool* sameTypePool = nullptr;
|
||||
for (auto& pool: mPools) {
|
||||
@@ -325,13 +325,12 @@ public:
|
||||
DescriptorSetLayoutManager(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout* layout) {
|
||||
auto const& bitmasks = layout->bitmask;
|
||||
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
|
||||
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
auto vklayout = createLayout(mDevice, layout->bitmask);
|
||||
mVkLayouts[layout->bitmask] = vklayout;
|
||||
auto vklayout = createLayout(mDevice, bitmasks);
|
||||
mVkLayouts[bitmasks] = vklayout;
|
||||
return vklayout;
|
||||
}
|
||||
|
||||
@@ -347,10 +346,11 @@ private:
|
||||
mVkLayouts;
|
||||
};
|
||||
|
||||
|
||||
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
|
||||
VulkanResourceAllocator* resourceAllocator)
|
||||
fvkmemory::ResourceManager* resourceManager)
|
||||
: mDevice(device),
|
||||
mResourceAllocator(resourceAllocator),
|
||||
mResourceManager(resourceManager),
|
||||
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
|
||||
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
|
||||
|
||||
@@ -359,18 +359,20 @@ VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
|
||||
void VulkanDescriptorSetManager::terminate() noexcept{
|
||||
mLayoutManager.reset();
|
||||
mDescriptorPool.reset();
|
||||
clearHistory();
|
||||
}
|
||||
|
||||
// bind() is not really binding the set but just stashing until we have all the info
|
||||
// (pipelinelayout).
|
||||
void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set,
|
||||
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
set->setOffsets(std::move(offsets));
|
||||
mStashedSets[setIndex] = set;
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
|
||||
mStashedSets[setIndex] = nullptr;
|
||||
mStashedSets[setIndex] = {};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
|
||||
@@ -412,14 +414,14 @@ void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
|
||||
};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
|
||||
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
VkDescriptorBufferInfo const info = {
|
||||
.buffer = bufferObject->buffer.getGpuBuffer(),
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
};
|
||||
|
||||
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
|
||||
if (set->dynamicUboMask.test(binding)) {
|
||||
@@ -438,8 +440,9 @@ void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t
|
||||
set->acquire(bufferObject);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t binding,
|
||||
VulkanTexture* texture, VkSampler sampler) noexcept {
|
||||
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||
VkSampler sampler) noexcept {
|
||||
VkDescriptorImageInfo info{
|
||||
.sampler = sampler,
|
||||
};
|
||||
@@ -469,38 +472,35 @@ void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t
|
||||
set->acquire(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::updateInputAttachment(VulkanDescriptorSet* set,
|
||||
VulkanAttachment attachment) noexcept {
|
||||
void VulkanDescriptorSetManager::updateInputAttachment(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
VulkanAttachment const& attachment) noexcept {
|
||||
// TOOD: fill-in this region
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::createSet(Handle<HwDescriptorSet> handle,
|
||||
VulkanDescriptorSetLayout* layout) {
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
|
||||
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto const vkSet = mDescriptorPool->obtainSet(layout);
|
||||
auto const& count = layout->count;
|
||||
auto const vklayout = layout->getVkLayout();
|
||||
mResourceAllocator->construct<VulkanDescriptorSet>(handle, mResourceAllocator, vkSet,
|
||||
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
|
||||
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
||||
[vkSet, count, vklayout, this](VulkanDescriptorSet* set) {
|
||||
eraseSetFromHistory(set);
|
||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
||||
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
||||
// down).
|
||||
if (mDescriptorPool) {
|
||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::destroySet(Handle<HwDescriptorSet> handle) {
|
||||
void VulkanDescriptorSetManager::initVkLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::initVkLayout(VulkanDescriptorSetLayout* layout) {
|
||||
layout->setVkLayout(mLayoutManager->getVkLayout(layout));
|
||||
void VulkanDescriptorSetManager::clearHistory() {
|
||||
mStashedSets = {};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::eraseSetFromHistory(VulkanDescriptorSet* set) {
|
||||
for (uint8_t i = 0; i < mStashedSets.size(); ++i) {
|
||||
if (mStashedSets[i] == set) {
|
||||
mStashedSets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
|
||||
#include <vulkan/VulkanResourceAllocator.h>
|
||||
#include <vulkan/VulkanTexture.h>
|
||||
#include <vulkan/VulkanUtility.h>
|
||||
#include "vulkan/VulkanHandles.h"
|
||||
#include "vulkan/VulkanTexture.h"
|
||||
#include "vulkan/VulkanUtility.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
@@ -46,46 +47,45 @@ public:
|
||||
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
|
||||
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
|
||||
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||
~VulkanDescriptorSetManager();
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
|
||||
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept;
|
||||
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
|
||||
VkDeviceSize size) noexcept;
|
||||
|
||||
void updateSampler(VulkanDescriptorSet* set, uint8_t binding,
|
||||
VulkanTexture* texture, VkSampler sampler) noexcept;
|
||||
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
|
||||
|
||||
void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept;
|
||||
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
VulkanAttachment const& attachment) noexcept;
|
||||
|
||||
void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets);
|
||||
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
backend::DescriptorSetOffsetArray&& offsets);
|
||||
|
||||
void unbind(uint8_t setIndex);
|
||||
|
||||
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
|
||||
DescriptorSetMask const& setMask);
|
||||
|
||||
void setPlaceHolders(VkSampler sampler, VulkanTexture* texture,
|
||||
VulkanBufferObject* bufferObject) noexcept;
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void createSet(Handle<HwDescriptorSet> handle, VulkanDescriptorSetLayout* layout);
|
||||
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void destroySet(Handle<HwDescriptorSet> handle);
|
||||
|
||||
void initVkLayout(VulkanDescriptorSetLayout* layout);
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
class DescriptorSetLayoutManager;
|
||||
class DescriptorInfinitePool;
|
||||
|
||||
void eraseSetFromHistory(VulkanDescriptorSet* set);
|
||||
|
||||
using DescriptorSetArray =
|
||||
std::array<VulkanDescriptorSet*, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
VkDevice mDevice;
|
||||
VulkanResourceAllocator* mResourceAllocator;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
|
||||
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
||||
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
|
||||
@@ -94,7 +94,7 @@ private:
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
DescriptorSetMask setMask;
|
||||
DescriptorSetArray boundSets;
|
||||
DescriptorSetArray boundSets = {};
|
||||
} mLastBoundInfo;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
#include "VulkanPipelineLayoutCache.h"
|
||||
|
||||
#include <vulkan/VulkanResourceAllocator.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
DescriptorSetLayoutArray const& descriptorSetLayouts, VulkanProgram* program) {
|
||||
DescriptorSetLayoutArray const& descriptorSetLayouts,
|
||||
fvkmemory::resource_ptr<VulkanProgram> program) {
|
||||
PipelineLayoutKey key = {};
|
||||
uint8_t descSetLayoutCount = 0;
|
||||
key.descSetLayouts = descriptorSetLayouts;
|
||||
@@ -40,13 +39,13 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
|
||||
auto const& range = pushConstantRanges[i];
|
||||
auto& pushConstant = key.pushConstant[i];
|
||||
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
|
||||
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
|
||||
}
|
||||
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
|
||||
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::FRAGMENT);
|
||||
}
|
||||
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
|
||||
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::COMPUTE);
|
||||
}
|
||||
pushConstant.size = range.size;
|
||||
|
||||
@@ -30,7 +30,7 @@ class VulkanPipelineLayoutCache {
|
||||
public:
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
|
||||
VulkanPipelineLayoutCache(VkDevice device, VulkanResourceAllocator* allocator)
|
||||
VulkanPipelineLayoutCache(VkDevice device)
|
||||
: mDevice(device),
|
||||
mTimestamp(0) {}
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
// A pipeline layout depends on the descriptor set layout and the push constant ranges, which
|
||||
// are described in the program.
|
||||
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
|
||||
VulkanProgram* program);
|
||||
fvkmemory::resource_ptr<VulkanProgram> program);
|
||||
|
||||
private:
|
||||
using Timestamp = uint64_t;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
namespace filament::backend::fvkmemory {
|
||||
|
||||
namespace {
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
uint32_t COUNTER[(size_t) ResourceType::UNDEFINED_TYPE] = {};
|
||||
#endif
|
||||
}
|
||||
@@ -47,8 +47,7 @@ void ResourceManager::gc() noexcept {
|
||||
}
|
||||
|
||||
GcList gcs;
|
||||
gcs.insert(gcs.end(), mGcList.begin(), mGcList.end());
|
||||
mGcList.clear();
|
||||
std::swap(gcs, mGcList);
|
||||
destroyAll(gcs);
|
||||
}
|
||||
|
||||
@@ -105,20 +104,20 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
break;
|
||||
}
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
COUNTER[(size_t) type]--;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
assert_invariant(type != ResourceType::UNDEFINED_TYPE);
|
||||
COUNTER[(size_t) type]++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResourceManager::print() const noexcept {
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
utils::slog.e << "-------------------" << utils::io::endl;
|
||||
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
|
||||
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;
|
||||
|
||||
@@ -95,7 +95,6 @@ private:
|
||||
AllocatorImpl mHandleAllocatorImpl;
|
||||
|
||||
using GcList = std::vector<std::pair<ResourceType, HandleId>>;
|
||||
|
||||
utils::Mutex mThreadSafeGcListMutex;
|
||||
GcList mThreadSafeGcList;
|
||||
GcList mGcList;
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace filament::backend {
|
||||
namespace {
|
||||
|
||||
std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& context,
|
||||
VkDevice device, VkExtent2D extent, VkFormat format) {
|
||||
VkDevice device, VkExtent2D extent, VkFormat format,
|
||||
bool isProtected) {
|
||||
bool const isDepth = isVkDepthFormat(format);
|
||||
// Filament expects blit() to work with any texture, so we almost always set these usage flags
|
||||
// (see copyFrame() and readPixels()).
|
||||
@@ -38,6 +39,7 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
||||
|
||||
VkImageCreateInfo imageInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.flags = isProtected ? VK_IMAGE_CREATE_PROTECTED_BIT : VkImageCreateFlags(0),
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = {extent.width, extent.height, 1},
|
||||
@@ -58,8 +60,11 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device, image, &memReqs);
|
||||
|
||||
const VkFlags requiredMemoryFlags =
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
|
||||
@@ -110,8 +115,9 @@ void VulkanPlatformSwapChainImpl::destroy() {
|
||||
mSwapChainBundle.colors.clear();
|
||||
}
|
||||
|
||||
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format) {
|
||||
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format);
|
||||
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
|
||||
bool isProtected) {
|
||||
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected);
|
||||
mMemory.insert({image, memory});
|
||||
return image;
|
||||
}
|
||||
@@ -248,7 +254,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
mSwapChainBundle.colorFormat = surfaceFormat.format;
|
||||
mSwapChainBundle.depthFormat =
|
||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
|
||||
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
|
||||
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent,
|
||||
mSwapChainBundle.depthFormat, mIsProtected);
|
||||
mSwapChainBundle.isProtected = mIsProtected;
|
||||
|
||||
FVK_LOGI << "vkCreateSwapchain"
|
||||
@@ -356,13 +363,13 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
|
||||
images.reserve(HEADLESS_SWAPCHAIN_SIZE);
|
||||
images.resize(HEADLESS_SWAPCHAIN_SIZE);
|
||||
for (size_t i = 0; i < HEADLESS_SWAPCHAIN_SIZE; ++i) {
|
||||
images[i] = createImage(extent, mSwapChainBundle.colorFormat);
|
||||
images[i] = createImage(extent, mSwapChainBundle.colorFormat, false);
|
||||
}
|
||||
|
||||
bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
||||
mSwapChainBundle.depthFormat =
|
||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
|
||||
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat);
|
||||
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat, false);
|
||||
}
|
||||
|
||||
VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() {
|
||||
|
||||
@@ -73,7 +73,7 @@ protected:
|
||||
// Non-virtual override-able method
|
||||
void destroy();
|
||||
|
||||
VkImage createImage(VkExtent2D extent, VkFormat format);
|
||||
VkImage createImage(VkExtent2D extent, VkFormat format, bool isProtected);
|
||||
|
||||
VulkanContext const& mContext;
|
||||
VkDevice mDevice;
|
||||
|
||||
@@ -529,19 +529,31 @@ void FEngine::shutdown() {
|
||||
mPerViewDescriptorSetLayoutSsrVariant.terminate(mHwDescriptorSetLayoutFactory, driver);
|
||||
mPerRenderableDescriptorSetLayout.terminate(mHwDescriptorSetLayoutFactory, driver);
|
||||
|
||||
driver.destroyRenderPrimitive(mFullScreenTriangleRph);
|
||||
driver.destroyRenderPrimitive(std::move(mFullScreenTriangleRph));
|
||||
|
||||
destroy(mFullScreenTriangleIb);
|
||||
mFullScreenTriangleIb = nullptr;
|
||||
|
||||
destroy(mFullScreenTriangleVb);
|
||||
mFullScreenTriangleVb = nullptr;
|
||||
|
||||
destroy(mDummyMorphTargetBuffer);
|
||||
mDummyMorphTargetBuffer = nullptr;
|
||||
|
||||
destroy(mDefaultIblTexture);
|
||||
mDefaultIblTexture = nullptr;
|
||||
|
||||
destroy(mDefaultIbl);
|
||||
mDefaultIbl = nullptr;
|
||||
|
||||
destroy(mDefaultColorGrading);
|
||||
mDefaultColorGrading = nullptr;
|
||||
|
||||
destroy(mDefaultMaterial);
|
||||
mDefaultMaterial = nullptr;
|
||||
|
||||
destroy(mUnprotectedDummySwapchain);
|
||||
mUnprotectedDummySwapchain = nullptr;
|
||||
|
||||
/*
|
||||
* clean-up after the user -- we call terminate on each "leaked" object and clear each list.
|
||||
@@ -558,6 +570,7 @@ void FEngine::shutdown() {
|
||||
|
||||
// this must be done after Skyboxes and before materials
|
||||
destroy(mSkyboxMaterial);
|
||||
mSkyboxMaterial = nullptr;
|
||||
|
||||
cleanupResourceList(std::move(mBufferObjects));
|
||||
cleanupResourceList(std::move(mIndexBuffers));
|
||||
@@ -574,12 +587,12 @@ void FEngine::shutdown() {
|
||||
|
||||
cleanupResourceListLocked(mFenceListLock, std::move(mFences));
|
||||
|
||||
driver.destroyTexture(mDummyOneTexture);
|
||||
driver.destroyTexture(mDummyOneTextureArray);
|
||||
driver.destroyTexture(mDummyZeroTexture);
|
||||
driver.destroyTexture(mDummyZeroTextureArray);
|
||||
driver.destroyTexture(std::move(mDummyOneTexture));
|
||||
driver.destroyTexture(std::move(mDummyOneTextureArray));
|
||||
driver.destroyTexture(std::move(mDummyZeroTexture));
|
||||
driver.destroyTexture(std::move(mDummyZeroTextureArray));
|
||||
|
||||
driver.destroyRenderTarget(mDefaultRenderTarget);
|
||||
driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
|
||||
|
||||
/*
|
||||
* Shutdown the backend...
|
||||
|
||||
@@ -321,11 +321,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
parser->getSpecularAntiAliasingThreshold(&mSpecularAntiAliasingThreshold);
|
||||
}
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
processPushConstants(engine, parser);
|
||||
processDepthVariants(engine, parser);
|
||||
processDescriptorSets(engine, parser);
|
||||
parser->hasCustomDepthShader(&mHasCustomDepthShader);
|
||||
|
||||
mPerViewLayoutIndex = ColorPassDescriptorSet::getIndex(
|
||||
mIsVariantLit,
|
||||
@@ -333,6 +329,12 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
mRefractionMode == RefractionMode::SCREEN_SPACE,
|
||||
!(mVariantFilterMask & +UserVariantFilterBit::FOG));
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
processPushConstants(engine, parser);
|
||||
processDescriptorSets(engine, parser);
|
||||
precacheDepthVariants(engine);
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
// Register the material with matdbg.
|
||||
matdbg::DebugServer* server = downcast(engine).debug.server;
|
||||
@@ -346,47 +348,21 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
FMaterial::~FMaterial() noexcept = default;
|
||||
|
||||
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
|
||||
DriverApi& driverApi = mEngine.getDriverApi();
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE) {
|
||||
auto& cachedPrograms = mCachedPrograms;
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
Variant const variant(k);
|
||||
if ((k & variantMask) == variantValue) {
|
||||
if (UTILS_LIKELY(!mIsDefaultMaterial)) {
|
||||
// The depth variants may be shared with the default material, in which case
|
||||
// we should not free it now.
|
||||
bool const isSharedVariant =
|
||||
Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
|
||||
if (isSharedVariant) {
|
||||
// we don't own this variant, skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
driverApi.destroyProgram(cachedPrograms[k]);
|
||||
cachedPrograms[k].clear();
|
||||
}
|
||||
// Note: This API is not public at the moment, so it's okay to have some debugging logs
|
||||
// and extra checks.
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader) {
|
||||
// it would be unsafe to invalidate any of the cached depth variant
|
||||
if (UTILS_UNLIKELY(!((variantMask & Variant::DEP) && !(variantValue & Variant::DEP)))) {
|
||||
slog.w << io::hex << "FMaterial::invalidate("
|
||||
<< +variantMask << ", " << +variantValue
|
||||
<< ") would corrupt the depth variant cache" << io::endl;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
|
||||
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
|
||||
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
|
||||
pDefaultMaterial->prepareProgram(variant);
|
||||
if (!cachedPrograms[variant.key]) {
|
||||
cachedPrograms[variant.key] = pDefaultMaterial->getProgram(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mMaterialDomain == MaterialDomain::POST_PROCESS) {
|
||||
auto& cachedPrograms = mCachedPrograms;
|
||||
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
driverApi.destroyProgram(cachedPrograms[k]);
|
||||
cachedPrograms[k].clear();
|
||||
}
|
||||
}
|
||||
} else if (mMaterialDomain == MaterialDomain::COMPUTE) {
|
||||
// TODO: handle compute variants if any
|
||||
variantMask |= Variant::DEP;
|
||||
variantValue &= ~Variant::DEP;
|
||||
}
|
||||
destroyPrograms(mEngine, variantMask, variantValue);
|
||||
}
|
||||
|
||||
void FMaterial::terminate(FEngine& engine) {
|
||||
@@ -626,10 +602,41 @@ Program FMaterial::getProgramWithVariants(
|
||||
}
|
||||
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
|
||||
auto program = mEngine.getDriverApi().createProgram(std::move(p));
|
||||
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
|
||||
FEngine const& engine = mEngine;
|
||||
DriverApi& driverApi = mEngine.getDriverApi();
|
||||
|
||||
// Check if the default material has this program cached
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial && !mHasCustomDepthShader &&
|
||||
Variant::isValidDepthVariant(variant)) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial) {
|
||||
auto program = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
if (program) {
|
||||
mCachedPrograms[variant.key] = program;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto program = driverApi.createProgram(std::move(p));
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[variant.key] = program;
|
||||
|
||||
// If the default material doesn't already have this program cached, and all caching conditions
|
||||
// are met (Surface Domain and no custom depth shader), cache it now.
|
||||
// New Materials will inherit these program automatically.
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial && !mHasCustomDepthShader &&
|
||||
Variant::isValidDepthVariant(variant)) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
// set the tag to the default material name
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
pDefaultMaterial->mCachedPrograms[variant.key] = program;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
|
||||
@@ -734,22 +741,76 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pVariants) {
|
||||
#endif // FILAMENT_ENABLE_MATDBG
|
||||
|
||||
|
||||
void FMaterial::destroyPrograms(FEngine& engine) {
|
||||
void FMaterial::destroyPrograms(FEngine& engine,
|
||||
Variant::type_t const variantMask, Variant::type_t const variantValue) {
|
||||
|
||||
DriverApi& driverApi = engine.getDriverApi();
|
||||
auto& cachedPrograms = mCachedPrograms;
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
const Variant variant(k);
|
||||
if (!mIsDefaultMaterial) {
|
||||
// The depth variants may be shared with the default material, in which case
|
||||
// we should not free it now.
|
||||
bool const isSharedVariant = Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
|
||||
if (isSharedVariant) {
|
||||
// we don't own this variant, skip.
|
||||
continue;
|
||||
|
||||
switch (mMaterialDomain) {
|
||||
case MaterialDomain::SURFACE: {
|
||||
if (mIsDefaultMaterial || mHasCustomDepthShader) {
|
||||
// default material or we have custom depth shaders, we destroy all variants
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variants, and this generates traffic in the command queue.
|
||||
if (cachedPrograms[k]) {
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The depth variants may be shared with the default material, in which case
|
||||
// we should not free them now.
|
||||
|
||||
// During Engine::shutdown(), auto-cleanup destroys the default material first,
|
||||
// so this can be null, but this is only used for debugging.
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
auto UTILS_NULLABLE pDefaultMaterial = engine.getDefaultMaterial();
|
||||
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
if (cachedPrograms[k]) {
|
||||
if (Variant::isValidDepthVariant(Variant(k))) {
|
||||
// By construction this should always be true, because this
|
||||
// field is populated only when a material creates the program
|
||||
// for this variant.
|
||||
// During Engine::shutdown, auto-cleanup destroys the
|
||||
// default material first
|
||||
assert_invariant(!pDefaultMaterial ||
|
||||
pDefaultMaterial->mCachedPrograms[k]);
|
||||
// we don't own this variant, skip, but clear the entry.
|
||||
cachedPrograms[k].clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::POST_PROCESS: {
|
||||
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
if (cachedPrograms[k]) {
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::COMPUTE: {
|
||||
// Compute programs don't have variants
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[0]));
|
||||
break;
|
||||
}
|
||||
driverApi.destroyProgram(cachedPrograms[k]);
|
||||
cachedPrograms[k].clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,34 +1075,17 @@ void FMaterial::processPushConstants(FEngine& engine, MaterialParser const* pars
|
||||
});
|
||||
}
|
||||
|
||||
void FMaterial::processDepthVariants(FEngine& engine, MaterialParser const* const parser) {
|
||||
parser->hasCustomDepthShader(&mHasCustomDepthShader);
|
||||
|
||||
if (UTILS_UNLIKELY(mIsDefaultMaterial)) {
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
filaflat::MaterialChunk const& materialChunk{ parser->getMaterialChunk() };
|
||||
auto variants = FixedCapacityVector<Variant>::with_capacity(materialChunk.getShaderCount());
|
||||
materialChunk.visitShaders([&variants](
|
||||
ShaderModel, Variant variant, ShaderStage) {
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
variants.push_back(variant);
|
||||
}
|
||||
});
|
||||
std::sort(variants.begin(), variants.end(),
|
||||
[](Variant lhs, Variant rhs) { return lhs.key < rhs.key; });
|
||||
auto pos = std::unique(variants.begin(), variants.end());
|
||||
variants.resize(std::distance(variants.begin(), pos));
|
||||
std::swap(mDepthVariants, variants);
|
||||
}
|
||||
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE) {
|
||||
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
auto& cachedPrograms = mCachedPrograms;
|
||||
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
|
||||
pDefaultMaterial->prepareProgram(variant);
|
||||
cachedPrograms[variant.key] = pDefaultMaterial->getProgram(variant);
|
||||
}
|
||||
void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
// if possible pre-cache all depth variants from the default material
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
assert_invariant(pDefaultMaterial);
|
||||
auto const allDepthVariants = VariantUtils::getDepthVariants();
|
||||
for (auto const variant: allDepthVariants) {
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
mCachedPrograms[variant.key] = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,9 @@ public:
|
||||
|
||||
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
|
||||
|
||||
void destroyPrograms(FEngine& engine);
|
||||
void destroyPrograms(FEngine& engine,
|
||||
Variant::type_t variantMask = 0,
|
||||
Variant::type_t variantValue = 0);
|
||||
|
||||
// return the id of a specialization constant specified by name for this material
|
||||
std::optional<uint32_t> getSpecializationConstantId(std::string_view name) const noexcept ;
|
||||
@@ -282,7 +284,7 @@ private:
|
||||
|
||||
void processPushConstants(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void processDepthVariants(FEngine& engine, MaterialParser const* parser);
|
||||
void precacheDepthVariants(FEngine& engine);
|
||||
|
||||
void processDescriptorSets(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
@@ -331,7 +333,6 @@ private:
|
||||
SamplerInterfaceBlock mSamplerInterfaceBlock;
|
||||
BufferInterfaceBlock mUniformInterfaceBlock;
|
||||
SubpassInfo mSubpassInfo;
|
||||
utils::FixedCapacityVector<Variant> mDepthVariants; // only populated with default material
|
||||
|
||||
using BindingUniformInfoContainer = utils::FixedCapacityVector<std::tuple<
|
||||
uint8_t, utils::CString, backend::Program::UniformInfo>>;
|
||||
|
||||
@@ -584,6 +584,10 @@ void FRenderer::render(FView const* view) {
|
||||
void FRenderer::renderInternal(FView const* view) {
|
||||
FEngine& engine = mEngine;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!view->hasPostProcessPass() ||
|
||||
engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1))
|
||||
<< "post-processing is not supported at FEATURE_LEVEL_0";
|
||||
|
||||
// per-renderpass data
|
||||
RootArenaScope rootArenaScope(engine.getPerRenderPassArena());
|
||||
|
||||
|
||||
@@ -534,7 +534,7 @@ private:
|
||||
Viewport mViewport;
|
||||
bool mCulling = true;
|
||||
bool mFrontFaceWindingInverted = false;
|
||||
bool mIsTransparentPickingEnabled = true;
|
||||
bool mIsTransparentPickingEnabled = false;
|
||||
|
||||
FRenderTarget* mRenderTarget = nullptr;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.56.0"
|
||||
spec.version = "1.56.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.56.0/filament-v1.56.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.1/filament-v1.56.1-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -36,6 +36,8 @@ elseif (APPLE AND NOT IOS)
|
||||
elseif(LINUX)
|
||||
if(FILAMENT_SUPPORTS_EGL_ON_LINUX)
|
||||
set(SRCS ${SRCS} src/BlueGLLinuxEGL.cpp)
|
||||
elseif(FILAMENT_SUPPORTS_OSMESA)
|
||||
set(SRCS ${SRCS} src/BlueGLLinuxOSMesa.cpp)
|
||||
else()
|
||||
set(SRCS ${SRCS} src/BlueGLLinux.cpp)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace bluegl {
|
||||
|
||||
namespace {
|
||||
using ProcAddressFunc = void*(*)(char const* funcName);
|
||||
}
|
||||
|
||||
struct Driver {
|
||||
ProcAddressFunc OSMesaGetProcAddress;
|
||||
void* library;
|
||||
} g_driver = {nullptr, nullptr};
|
||||
|
||||
bool initBinder() {
|
||||
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
|
||||
for (char const* name : libraryNames) {
|
||||
g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (g_driver.library) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!g_driver.library) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g_driver.OSMesaGetProcAddress = (ProcAddressFunc)
|
||||
dlsym(g_driver.library, "OSMesaGetProcAddress");
|
||||
|
||||
return g_driver.OSMesaGetProcAddress;
|
||||
}
|
||||
|
||||
void* loadFunction(const char* name) {
|
||||
return (void*) g_driver.OSMesaGetProcAddress(name);
|
||||
}
|
||||
|
||||
void shutdownBinder() {
|
||||
dlclose(g_driver.library);
|
||||
memset(&g_driver, 0, sizeof(g_driver));
|
||||
}
|
||||
|
||||
} // namespace bluegl
|
||||
@@ -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 = 55;
|
||||
static constexpr size_t MATERIAL_VERSION = 56;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/Slice.h>
|
||||
|
||||
@@ -271,6 +272,8 @@ namespace VariantUtils {
|
||||
utils::Slice<Variant> getLitVariants() noexcept UTILS_PURE;
|
||||
// list of unlit variants
|
||||
utils::Slice<Variant> getUnlitVariants() noexcept UTILS_PURE;
|
||||
// list of depth variants
|
||||
utils::Slice<Variant> getDepthVariants() noexcept UTILS_PURE;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -16,8 +16,15 @@
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
Variant Variant::filterUserVariant(
|
||||
@@ -65,6 +72,8 @@ Variant Variant::filterUserVariant(
|
||||
|
||||
namespace details {
|
||||
|
||||
namespace {
|
||||
|
||||
// Compile-time variant count for lit and unlit
|
||||
constexpr inline size_t variant_count(bool lit) noexcept {
|
||||
size_t count = 0;
|
||||
@@ -81,6 +90,17 @@ constexpr inline size_t variant_count(bool lit) noexcept {
|
||||
return count;
|
||||
}
|
||||
|
||||
constexpr inline size_t depth_variant_count() noexcept {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < VARIANT_COUNT; i++) {
|
||||
Variant const variant(i);
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Compile-time variant list for lit and unlit
|
||||
template<bool LIT>
|
||||
constexpr auto get_variants() noexcept {
|
||||
@@ -98,9 +118,18 @@ constexpr auto get_variants() noexcept {
|
||||
}
|
||||
return variants;
|
||||
}
|
||||
static auto const gLitVariants{ details::get_variants<true>() };
|
||||
static auto const gUnlitVariants{ details::get_variants<false>() };
|
||||
|
||||
constexpr auto get_depth_variants() noexcept {
|
||||
std::array<Variant, depth_variant_count()> variants;
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < VARIANT_COUNT; i++) {
|
||||
Variant const variant(i);
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
variants[count++] = variant;
|
||||
}
|
||||
}
|
||||
return variants;
|
||||
}
|
||||
|
||||
// Below are compile time sanity-check tests
|
||||
constexpr inline bool reserved_is_not_valid() noexcept {
|
||||
@@ -163,6 +192,13 @@ constexpr inline size_t fragment_variant_count() noexcept {
|
||||
return count;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
static auto const gLitVariants{ details::get_variants<true>() };
|
||||
static auto const gUnlitVariants{ details::get_variants<false>() };
|
||||
static auto const gDepthVariants{ details::get_depth_variants() };
|
||||
|
||||
static_assert(reserved_is_not_valid());
|
||||
static_assert(reserved_variant_count() == 160);
|
||||
static_assert(valid_variant_count() == 96);
|
||||
@@ -182,6 +218,10 @@ utils::Slice<Variant> getUnlitVariants() noexcept {
|
||||
return { details::gUnlitVariants.data(), details::gUnlitVariants.size() };
|
||||
}
|
||||
|
||||
utils::Slice<Variant> getDepthVariants() noexcept {
|
||||
return { details::gDepthVariants.data(), details::gDepthVariants.size() };
|
||||
}
|
||||
|
||||
}; // VariantUtils
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -278,18 +278,23 @@ public:
|
||||
void* pop() noexcept {
|
||||
Node* const pStorage = mStorage;
|
||||
|
||||
HeadPtr currentHead = mHead.load();
|
||||
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
|
||||
while (currentHead.offset >= 0) {
|
||||
// The value of "pNext" we load here might already contain application data if another
|
||||
// thread raced ahead of us. But in that case, the computed "newHead" will be discarded
|
||||
// since compare_exchange_weak fails. Then this thread will loop with the updated
|
||||
// since compare_exchange_weak() fails. Then this thread will loop with the updated
|
||||
// value of currentHead, and try again.
|
||||
Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed);
|
||||
// TSAN complains if we don't use a local variable here.
|
||||
Node const node = pStorage[currentHead.offset];
|
||||
Node const* const pNext = node.next;
|
||||
const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 };
|
||||
// In the rare case that the other thread that raced ahead of us already returned the
|
||||
// same mHead we just loaded, but it now has a different "next" value, the tag field will not
|
||||
// match, and compare_exchange_weak will fail and prevent that particular race condition.
|
||||
if (mHead.compare_exchange_weak(currentHead, newHead)) {
|
||||
// In the rare case that the other thread that raced ahead of us already returned the
|
||||
// same mHead we just loaded, but it now has a different "next" value, the tag field
|
||||
// will not match, and compare_exchange_weak() will fail and prevent that particular
|
||||
// race condition.
|
||||
// acquire: no read/write can be reordered before this
|
||||
if (mHead.compare_exchange_weak(currentHead, newHead,
|
||||
std::memory_order_acquire, std::memory_order_relaxed)) {
|
||||
// This assert needs to occur after we have validated that there was no race condition
|
||||
// Otherwise, next might already contain application data, if another thread
|
||||
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
|
||||
@@ -306,13 +311,15 @@ public:
|
||||
Node* const storage = mStorage;
|
||||
assert_invariant(p && p >= storage);
|
||||
Node* const node = static_cast<Node*>(p);
|
||||
HeadPtr currentHead = mHead.load();
|
||||
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
|
||||
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
|
||||
do {
|
||||
newHead.tag = currentHead.tag + 1;
|
||||
Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
|
||||
node->next.store(n, std::memory_order_relaxed);
|
||||
} while(!mHead.compare_exchange_weak(currentHead, newHead));
|
||||
Node* const pNext = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
|
||||
node->next = pNext; // could be a race with pop, corrected by CAS
|
||||
} while(!mHead.compare_exchange_weak(currentHead, newHead,
|
||||
std::memory_order_release, std::memory_order_relaxed));
|
||||
// release: no read/write can be reordered after this
|
||||
}
|
||||
|
||||
void* getFirst() noexcept {
|
||||
@@ -320,10 +327,7 @@ public:
|
||||
}
|
||||
|
||||
struct Node {
|
||||
// This should be a regular (non-atomic) pointer, but this causes TSAN to complain
|
||||
// about a data-race that exists but is benin. We always use this atomic<> in
|
||||
// relaxed mode.
|
||||
// The data race TSAN complains about is when a pop() is interrupted by a
|
||||
// There is a benign data race when a pop() is interrupted by a
|
||||
// pop() + push() just after mHead->next is read -- it appears as though it is written
|
||||
// without synchronization (by the push), however in that case, the pop's CAS will fail
|
||||
// and things will auto-correct.
|
||||
@@ -346,7 +350,7 @@ public:
|
||||
// |
|
||||
// CAS, tag++
|
||||
//
|
||||
std::atomic<Node*> next;
|
||||
Node* next = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@@ -446,7 +446,7 @@ private:
|
||||
Condition mWaiterCondition;
|
||||
|
||||
std::atomic<int32_t> mActiveJobs = { 0 };
|
||||
utils::Arena<utils::ThreadSafeObjectPoolAllocator<Job>, LockingPolicy::NoLock> mJobPool;
|
||||
utils::Arena<utils::ObjectPoolAllocator<Job>, LockingPolicy::Mutex> mJobPool;
|
||||
|
||||
template <typename T>
|
||||
using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
|
||||
|
||||
@@ -95,11 +95,11 @@ protected:
|
||||
size_t length() const noexcept;
|
||||
|
||||
private:
|
||||
void reserve(size_t newSize) noexcept;
|
||||
void reserve(size_t newCapacity) noexcept;
|
||||
|
||||
char* buffer = nullptr; // buffer address
|
||||
char* curr = nullptr; // current pointer
|
||||
size_t size = 0; // size remaining
|
||||
size_t sizeRemaining = 0; // size remaining
|
||||
size_t capacity = 0; // total capacity of the buffer
|
||||
};
|
||||
|
||||
|
||||
+14
-13
@@ -237,23 +237,23 @@ ostream::Buffer::~Buffer() noexcept {
|
||||
|
||||
void ostream::Buffer::advance(ssize_t n) noexcept {
|
||||
if (n > 0) {
|
||||
size_t const written = n < size ? size_t(n) : size;
|
||||
size_t const written = n < sizeRemaining ? size_t(n) : sizeRemaining;
|
||||
curr += written;
|
||||
size -= written;
|
||||
sizeRemaining -= written;
|
||||
}
|
||||
}
|
||||
|
||||
void ostream::Buffer::reserve(size_t newSize) noexcept {
|
||||
void ostream::Buffer::reserve(size_t newCapacity) noexcept {
|
||||
size_t const offset = curr - buffer;
|
||||
if (buffer == nullptr) {
|
||||
buffer = (char*)malloc(newSize);
|
||||
buffer = (char*)malloc(newCapacity);
|
||||
} else {
|
||||
buffer = (char*)realloc(buffer, newSize);
|
||||
buffer = (char*)realloc(buffer, newCapacity);
|
||||
}
|
||||
assert(buffer);
|
||||
capacity = newSize;
|
||||
capacity = newCapacity;
|
||||
curr = buffer + offset;
|
||||
size = capacity - offset;
|
||||
sizeRemaining = capacity - offset;
|
||||
}
|
||||
|
||||
void ostream::Buffer::reset() noexcept {
|
||||
@@ -264,7 +264,7 @@ void ostream::Buffer::reset() noexcept {
|
||||
capacity = 1024;
|
||||
}
|
||||
curr = buffer;
|
||||
size = capacity;
|
||||
sizeRemaining = capacity;
|
||||
}
|
||||
|
||||
size_t ostream::Buffer::length() const noexcept {
|
||||
@@ -272,13 +272,14 @@ size_t ostream::Buffer::length() const noexcept {
|
||||
}
|
||||
|
||||
std::pair<char*, size_t> ostream::Buffer::grow(size_t s) noexcept {
|
||||
if (UTILS_UNLIKELY(size < s)) {
|
||||
size_t const used = curr - buffer;
|
||||
size_t const newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
|
||||
if (UTILS_UNLIKELY(sizeRemaining < s)) {
|
||||
size_t const usedSize = curr - buffer;
|
||||
size_t const neededCapacity = usedSize + s;
|
||||
size_t const newCapacity = std::max(size_t(32), (neededCapacity * 3 + 1) / 2); // 32 bytes minimum
|
||||
reserve(newCapacity);
|
||||
assert(size >= s);
|
||||
assert(sizeRemaining >= s);
|
||||
}
|
||||
return { curr, size };
|
||||
return { curr, sizeRemaining };
|
||||
}
|
||||
|
||||
} // namespace utils::io
|
||||
|
||||
@@ -1043,6 +1043,8 @@ int main(int argc, char** argv) {
|
||||
delete app.resourceLoader;
|
||||
delete app.stbDecoder;
|
||||
delete app.ktxDecoder;
|
||||
delete app.automationSpec;
|
||||
delete app.automationEngine;
|
||||
|
||||
AssetLoader::destroy(&app.assetLoader);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
float sampleDepth(const mediump sampler2DArrayShadow map,
|
||||
const highp vec4 scissorNormalized,
|
||||
const uint layer, highp vec2 uv, float depth) {
|
||||
const uint layer, highp vec2 uv, highp float depth) {
|
||||
|
||||
// clamp needed for directional lights and/or large kernels
|
||||
uv = clamp(uv, scissorNormalized.xy, scissorNormalized.zw);
|
||||
@@ -51,7 +51,7 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map,
|
||||
highp vec2 texelSize = vec2(1.0) / size;
|
||||
|
||||
// Castaño, 2013, "Shadow Mapping Summary Part 1"
|
||||
float depth = position.z;
|
||||
highp float depth = position.z;
|
||||
|
||||
// clamp position to avoid overflows below, which cause some GPUs to abort
|
||||
position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0));
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(rainbowgen)
|
||||
|
||||
set(TARGET rainbowgen)
|
||||
|
||||
# ==================================================================================================
|
||||
# Source files
|
||||
# ==================================================================================================
|
||||
set(SRCS src/rainbowgen.cpp
|
||||
src/RainbowGenerator.cpp)
|
||||
|
||||
# ==================================================================================================
|
||||
# Target definitions
|
||||
# ==================================================================================================
|
||||
add_executable(${TARGET} ${SRCS})
|
||||
target_link_libraries(${TARGET} PRIVATE math utils getopt image imageio)
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Tools)
|
||||
|
||||
# =================================================================================================
|
||||
# Licenses
|
||||
# ==================================================================================================
|
||||
set(MODULE_LICENSES getopt)
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR}/generated)
|
||||
list_licenses(${GENERATION_ROOT}/licenses/licenses.inc ${MODULE_LICENSES})
|
||||
target_include_directories(${TARGET} PRIVATE ${GENERATION_ROOT})
|
||||
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
install(TARGETS ${TARGET} RUNTIME DESTINATION bin)
|
||||
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_CIE_H
|
||||
#define TNT_CIE_H
|
||||
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
// CIE 2006 10-deg color matching functions (CMFs), from 390nm to 830nm, at 1nm intervals
|
||||
//
|
||||
// Data source:
|
||||
// http://www.cvrl.org/ciexyzpr.htm
|
||||
const size_t CIE_XYZ_START = 390;
|
||||
const size_t CIE_XYZ_COUNT = 441;
|
||||
const float3 CIE_XYZ[CIE_XYZ_COUNT] = { // NOLINT
|
||||
{ 2.952420E-03f, 4.076779E-04f, 1.318752E-02f },
|
||||
{ 3.577275E-03f, 4.977769E-04f, 1.597879E-02f },
|
||||
{ 4.332146E-03f, 6.064754E-04f, 1.935758E-02f },
|
||||
{ 5.241609E-03f, 7.370040E-04f, 2.343758E-02f },
|
||||
{ 6.333902E-03f, 8.929388E-04f, 2.835021E-02f },
|
||||
{ 7.641137E-03f, 1.078166E-03f, 3.424588E-02f },
|
||||
{ 9.199401E-03f, 1.296816E-03f, 4.129467E-02f },
|
||||
{ 1.104869E-02f, 1.553159E-03f, 4.968641E-02f },
|
||||
{ 1.323262E-02f, 1.851463E-03f, 5.962964E-02f },
|
||||
{ 1.579791E-02f, 2.195795E-03f, 7.134926E-02f },
|
||||
{ 1.879338E-02f, 2.589775E-03f, 8.508254E-02f },
|
||||
{ 2.226949E-02f, 3.036799E-03f, 1.010753E-01f },
|
||||
{ 2.627978E-02f, 3.541926E-03f, 1.195838E-01f },
|
||||
{ 3.087862E-02f, 4.111422E-03f, 1.408647E-01f },
|
||||
{ 3.611890E-02f, 4.752618E-03f, 1.651644E-01f },
|
||||
{ 4.204986E-02f, 5.474207E-03f, 1.927065E-01f },
|
||||
{ 4.871256E-02f, 6.285034E-03f, 2.236782E-01f },
|
||||
{ 5.612868E-02f, 7.188068E-03f, 2.582109E-01f },
|
||||
{ 6.429866E-02f, 8.181786E-03f, 2.963632E-01f },
|
||||
{ 7.319818E-02f, 9.260417E-03f, 3.381018E-01f },
|
||||
{ 8.277331E-02f, 1.041303E-02f, 3.832822E-01f },
|
||||
{ 9.295327E-02f, 1.162642E-02f, 4.316884E-01f },
|
||||
{ 1.037137E-01f, 1.289884E-02f, 4.832440E-01f },
|
||||
{ 1.150520E-01f, 1.423442E-02f, 5.379345E-01f },
|
||||
{ 1.269771E-01f, 1.564080E-02f, 5.957740E-01f },
|
||||
{ 1.395127E-01f, 1.712968E-02f, 6.568187E-01f },
|
||||
{ 1.526661E-01f, 1.871265E-02f, 7.210459E-01f },
|
||||
{ 1.663054E-01f, 2.038394E-02f, 7.878635E-01f },
|
||||
{ 1.802197E-01f, 2.212935E-02f, 8.563391E-01f },
|
||||
{ 1.941448E-01f, 2.392985E-02f, 9.253017E-01f },
|
||||
{ 2.077647E-01f, 2.576133E-02f, 9.933444E-01f },
|
||||
{ 2.207911E-01f, 2.760156E-02f, 1.059178E+00f },
|
||||
{ 2.332355E-01f, 2.945513E-02f, 1.122832E+00f },
|
||||
{ 2.452462E-01f, 3.133884E-02f, 1.184947E+00f },
|
||||
{ 2.570397E-01f, 3.327575E-02f, 1.246476E+00f },
|
||||
{ 2.688989E-01f, 3.529554E-02f, 1.308674E+00f },
|
||||
{ 2.810677E-01f, 3.742705E-02f, 1.372628E+00f },
|
||||
{ 2.933967E-01f, 3.967137E-02f, 1.437661E+00f },
|
||||
{ 3.055933E-01f, 4.201998E-02f, 1.502449E+00f },
|
||||
{ 3.173165E-01f, 4.446166E-02f, 1.565456E+00f },
|
||||
{ 3.281798E-01f, 4.698226E-02f, 1.624940E+00f },
|
||||
{ 3.378678E-01f, 4.956742E-02f, 1.679488E+00f },
|
||||
{ 3.465097E-01f, 5.221219E-02f, 1.729668E+00f },
|
||||
{ 3.543953E-01f, 5.491387E-02f, 1.776755E+00f },
|
||||
{ 3.618655E-01f, 5.766919E-02f, 1.822228E+00f },
|
||||
{ 3.693084E-01f, 6.047429E-02f, 1.867751E+00f },
|
||||
{ 3.770107E-01f, 6.332195E-02f, 1.914504E+00f },
|
||||
{ 3.846850E-01f, 6.619271E-02f, 1.961055E+00f },
|
||||
{ 3.918591E-01f, 6.906185E-02f, 2.005136E+00f },
|
||||
{ 3.980192E-01f, 7.190190E-02f, 2.044296E+00f },
|
||||
{ 4.026189E-01f, 7.468288E-02f, 2.075946E+00f },
|
||||
{ 4.052637E-01f, 7.738452E-02f, 2.098231E+00f },
|
||||
{ 4.062482E-01f, 8.003601E-02f, 2.112591E+00f },
|
||||
{ 4.060660E-01f, 8.268524E-02f, 2.121427E+00f },
|
||||
{ 4.052283E-01f, 8.538745E-02f, 2.127239E+00f },
|
||||
{ 4.042529E-01f, 8.820537E-02f, 2.132574E+00f },
|
||||
{ 4.034808E-01f, 9.118925E-02f, 2.139093E+00f },
|
||||
{ 4.025362E-01f, 9.431041E-02f, 2.144815E+00f },
|
||||
{ 4.008675E-01f, 9.751346E-02f, 2.146832E+00f },
|
||||
{ 3.979327E-01f, 1.007349E-01f, 2.142250E+00f },
|
||||
{ 3.932139E-01f, 1.039030E-01f, 2.128264E+00f },
|
||||
{ 3.864108E-01f, 1.069639E-01f, 2.103205E+00f },
|
||||
{ 3.779513E-01f, 1.099676E-01f, 2.069388E+00f },
|
||||
{ 3.684176E-01f, 1.129992E-01f, 2.030030E+00f },
|
||||
{ 3.583473E-01f, 1.161541E-01f, 1.988178E+00f },
|
||||
{ 3.482214E-01f, 1.195389E-01f, 1.946651E+00f },
|
||||
{ 3.383830E-01f, 1.232503E-01f, 1.907521E+00f },
|
||||
{ 3.288309E-01f, 1.273047E-01f, 1.870689E+00f },
|
||||
{ 3.194977E-01f, 1.316964E-01f, 1.835578E+00f },
|
||||
{ 3.103345E-01f, 1.364178E-01f, 1.801657E+00f },
|
||||
{ 3.013112E-01f, 1.414586E-01f, 1.768440E+00f },
|
||||
{ 2.923754E-01f, 1.468003E-01f, 1.735338E+00f },
|
||||
{ 2.833273E-01f, 1.524002E-01f, 1.701254E+00f },
|
||||
{ 2.739463E-01f, 1.582021E-01f, 1.665053E+00f },
|
||||
{ 2.640352E-01f, 1.641400E-01f, 1.625712E+00f },
|
||||
{ 2.534221E-01f, 1.701373E-01f, 1.582342E+00f },
|
||||
{ 2.420135E-01f, 1.761233E-01f, 1.534439E+00f },
|
||||
{ 2.299346E-01f, 1.820896E-01f, 1.482544E+00f },
|
||||
{ 2.173617E-01f, 1.880463E-01f, 1.427438E+00f },
|
||||
{ 2.044672E-01f, 1.940065E-01f, 1.369876E+00f },
|
||||
{ 1.914176E-01f, 1.999859E-01f, 1.310576E+00f },
|
||||
{ 1.783672E-01f, 2.060054E-01f, 1.250226E+00f },
|
||||
{ 1.654407E-01f, 2.120981E-01f, 1.189511E+00f },
|
||||
{ 1.527391E-01f, 2.183041E-01f, 1.129050E+00f },
|
||||
{ 1.403439E-01f, 2.246686E-01f, 1.069379E+00f },
|
||||
{ 1.283167E-01f, 2.312426E-01f, 1.010952E+00f },
|
||||
{ 1.167124E-01f, 2.380741E-01f, 9.541809E-01f },
|
||||
{ 1.056121E-01f, 2.451798E-01f, 8.995253E-01f },
|
||||
{ 9.508569E-02f, 2.525682E-01f, 8.473720E-01f },
|
||||
{ 8.518206E-02f, 2.602479E-01f, 7.980093E-01f },
|
||||
{ 7.593120E-02f, 2.682271E-01f, 7.516389E-01f },
|
||||
{ 6.733159E-02f, 2.765005E-01f, 7.082645E-01f },
|
||||
{ 5.932018E-02f, 2.850035E-01f, 6.673867E-01f },
|
||||
{ 5.184106E-02f, 2.936475E-01f, 6.284798E-01f },
|
||||
{ 4.486119E-02f, 3.023319E-01f, 5.911174E-01f },
|
||||
{ 3.836770E-02f, 3.109438E-01f, 5.549619E-01f },
|
||||
{ 3.237296E-02f, 3.194105E-01f, 5.198843E-01f },
|
||||
{ 2.692095E-02f, 3.278683E-01f, 4.862772E-01f },
|
||||
{ 2.204070E-02f, 3.365263E-01f, 4.545497E-01f },
|
||||
{ 1.773951E-02f, 3.456176E-01f, 4.249955E-01f },
|
||||
{ 1.400745E-02f, 3.554018E-01f, 3.978114E-01f },
|
||||
{ 1.082291E-02f, 3.660893E-01f, 3.730218E-01f },
|
||||
{ 8.168996E-03f, 3.775857E-01f, 3.502618E-01f },
|
||||
{ 6.044623E-03f, 3.896960E-01f, 3.291407E-01f },
|
||||
{ 4.462638E-03f, 4.021947E-01f, 3.093356E-01f },
|
||||
{ 3.446810E-03f, 4.148227E-01f, 2.905816E-01f },
|
||||
{ 3.009513E-03f, 4.273539E-01f, 2.726773E-01f },
|
||||
{ 3.090744E-03f, 4.398206E-01f, 2.555143E-01f },
|
||||
{ 3.611221E-03f, 4.523360E-01f, 2.390188E-01f },
|
||||
{ 4.491435E-03f, 4.650298E-01f, 2.231335E-01f },
|
||||
{ 5.652072E-03f, 4.780482E-01f, 2.078158E-01f },
|
||||
{ 7.035322E-03f, 4.915173E-01f, 1.930407E-01f },
|
||||
{ 8.669631E-03f, 5.054224E-01f, 1.788089E-01f },
|
||||
{ 1.060755E-02f, 5.197057E-01f, 1.651287E-01f },
|
||||
{ 1.290468E-02f, 5.343012E-01f, 1.520103E-01f },
|
||||
{ 1.561956E-02f, 5.491344E-01f, 1.394643E-01f },
|
||||
{ 1.881640E-02f, 5.641302E-01f, 1.275353E-01f },
|
||||
{ 2.256923E-02f, 5.792416E-01f, 1.163771E-01f },
|
||||
{ 2.694456E-02f, 5.944264E-01f, 1.061161E-01f },
|
||||
{ 3.199910E-02f, 6.096388E-01f, 9.682266E-02f },
|
||||
{ 3.778185E-02f, 6.248296E-01f, 8.852389E-02f },
|
||||
{ 4.430635E-02f, 6.399656E-01f, 8.118263E-02f },
|
||||
{ 5.146516E-02f, 6.550943E-01f, 7.463132E-02f },
|
||||
{ 5.912224E-02f, 6.702903E-01f, 6.870644E-02f },
|
||||
{ 6.714220E-02f, 6.856375E-01f, 6.327834E-02f },
|
||||
{ 7.538941E-02f, 7.012292E-01f, 5.824484E-02f },
|
||||
{ 8.376697E-02f, 7.171103E-01f, 5.353812E-02f },
|
||||
{ 9.233581E-02f, 7.330917E-01f, 4.914863E-02f },
|
||||
{ 1.011940E-01f, 7.489041E-01f, 4.507511E-02f },
|
||||
{ 1.104362E-01f, 7.642530E-01f, 4.131175E-02f },
|
||||
{ 1.201511E-01f, 7.788199E-01f, 3.784916E-02f },
|
||||
{ 1.303960E-01f, 7.923410E-01f, 3.467234E-02f },
|
||||
{ 1.411310E-01f, 8.048510E-01f, 3.175471E-02f },
|
||||
{ 1.522944E-01f, 8.164747E-01f, 2.907029E-02f },
|
||||
{ 1.638288E-01f, 8.273520E-01f, 2.659651E-02f },
|
||||
{ 1.756832E-01f, 8.376358E-01f, 2.431375E-02f },
|
||||
{ 1.878114E-01f, 8.474653E-01f, 2.220677E-02f },
|
||||
{ 2.001621E-01f, 8.568868E-01f, 2.026852E-02f },
|
||||
{ 2.126822E-01f, 8.659242E-01f, 1.849246E-02f },
|
||||
{ 2.253199E-01f, 8.746041E-01f, 1.687084E-02f },
|
||||
{ 2.380254E-01f, 8.829552E-01f, 1.539505E-02f },
|
||||
{ 2.507787E-01f, 8.910274E-01f, 1.405450E-02f },
|
||||
{ 2.636778E-01f, 8.989495E-01f, 1.283354E-02f },
|
||||
{ 2.768607E-01f, 9.068753E-01f, 1.171754E-02f },
|
||||
{ 2.904792E-01f, 9.149652E-01f, 1.069415E-02f },
|
||||
{ 3.046991E-01f, 9.233858E-01f, 9.753000E-03f },
|
||||
{ 3.196485E-01f, 9.322325E-01f, 8.886096E-03f },
|
||||
{ 3.352447E-01f, 9.412862E-01f, 8.089323E-03f },
|
||||
{ 3.513290E-01f, 9.502378E-01f, 7.359131E-03f },
|
||||
{ 3.677148E-01f, 9.587647E-01f, 6.691736E-03f },
|
||||
{ 3.841856E-01f, 9.665325E-01f, 6.083223E-03f },
|
||||
{ 4.005312E-01f, 9.732504E-01f, 5.529423E-03f },
|
||||
{ 4.166669E-01f, 9.788415E-01f, 5.025504E-03f },
|
||||
{ 4.325420E-01f, 9.832867E-01f, 4.566879E-03f },
|
||||
{ 4.481063E-01f, 9.865720E-01f, 4.149405E-03f },
|
||||
{ 4.633109E-01f, 9.886887E-01f, 3.769336E-03f },
|
||||
{ 4.781440E-01f, 9.897056E-01f, 3.423302E-03f },
|
||||
{ 4.927483E-01f, 9.899849E-01f, 3.108313E-03f },
|
||||
{ 5.073315E-01f, 9.899624E-01f, 2.821650E-03f },
|
||||
{ 5.221315E-01f, 9.900731E-01f, 2.560830E-03f },
|
||||
{ 5.374170E-01f, 9.907500E-01f, 2.323578E-03f },
|
||||
{ 5.534217E-01f, 9.922826E-01f, 2.107847E-03f },
|
||||
{ 5.701242E-01f, 9.943837E-01f, 1.911867E-03f },
|
||||
{ 5.874093E-01f, 9.966221E-01f, 1.734006E-03f },
|
||||
{ 6.051269E-01f, 9.985649E-01f, 1.572736E-03f },
|
||||
{ 6.230892E-01f, 9.997775E-01f, 1.426627E-03f },
|
||||
{ 6.410999E-01f, 9.999440E-01f, 1.294325E-03f },
|
||||
{ 6.590659E-01f, 9.992200E-01f, 1.174475E-03f },
|
||||
{ 6.769436E-01f, 9.978793E-01f, 1.065842E-03f },
|
||||
{ 6.947143E-01f, 9.961934E-01f, 9.673215E-04f },
|
||||
{ 7.123849E-01f, 9.944304E-01f, 8.779264E-04f },
|
||||
{ 7.299978E-01f, 9.927831E-01f, 7.967847E-04f },
|
||||
{ 7.476478E-01f, 9.911578E-01f, 7.231502E-04f },
|
||||
{ 7.654250E-01f, 9.893925E-01f, 6.563501E-04f },
|
||||
{ 7.834009E-01f, 9.873288E-01f, 5.957678E-04f },
|
||||
{ 8.016277E-01f, 9.848127E-01f, 5.408385E-04f },
|
||||
{ 8.201041E-01f, 9.817253E-01f, 4.910441E-04f },
|
||||
{ 8.386843E-01f, 9.780714E-01f, 4.459046E-04f },
|
||||
{ 8.571936E-01f, 9.738860E-01f, 4.049826E-04f },
|
||||
{ 8.754652E-01f, 9.692028E-01f, 3.678818E-04f },
|
||||
{ 8.933408E-01f, 9.640545E-01f, 3.342429E-04f },
|
||||
{ 9.106772E-01f, 9.584409E-01f, 3.037407E-04f },
|
||||
{ 9.273554E-01f, 9.522379E-01f, 2.760809E-04f },
|
||||
{ 9.432502E-01f, 9.452968E-01f, 2.509970E-04f },
|
||||
{ 9.582244E-01f, 9.374773E-01f, 2.282474E-04f },
|
||||
{ 9.721304E-01f, 9.286495E-01f, 2.076129E-04f },
|
||||
{ 9.849237E-01f, 9.187953E-01f, 1.888948E-04f },
|
||||
{ 9.970067E-01f, 9.083014E-01f, 1.719127E-04f },
|
||||
{ 1.008907E+00f, 8.976352E-01f, 1.565030E-04f },
|
||||
{ 1.021163E+00f, 8.872401E-01f, 1.425177E-04f },
|
||||
{ 1.034327E+00f, 8.775360E-01f, 1.298230E-04f },
|
||||
{ 1.048753E+00f, 8.687920E-01f, 1.182974E-04f },
|
||||
{ 1.063937E+00f, 8.607474E-01f, 1.078310E-04f },
|
||||
{ 1.079166E+00f, 8.530233E-01f, 9.832455E-05f },
|
||||
{ 1.093723E+00f, 8.452535E-01f, 8.968787E-05f },
|
||||
{ 1.106886E+00f, 8.370838E-01f, 8.183954E-05f },
|
||||
{ 1.118106E+00f, 8.282409E-01f, 7.470582E-05f },
|
||||
{ 1.127493E+00f, 8.187320E-01f, 6.821991E-05f },
|
||||
{ 1.135317E+00f, 8.086352E-01f, 6.232132E-05f },
|
||||
{ 1.141838E+00f, 7.980296E-01f, 5.695534E-05f },
|
||||
{ 1.147304E+00f, 7.869950E-01f, 5.207245E-05f },
|
||||
{ 1.151897E+00f, 7.756040E-01f, 4.762781E-05f },
|
||||
{ 1.155582E+00f, 7.638996E-01f, 4.358082E-05f },
|
||||
{ 1.158284E+00f, 7.519157E-01f, 3.989468E-05f },
|
||||
{ 1.159934E+00f, 7.396832E-01f, 3.653612E-05f },
|
||||
{ 1.160477E+00f, 7.272309E-01f, 3.347499E-05f },
|
||||
{ 1.159890E+00f, 7.145878E-01f, 3.068400E-05f },
|
||||
{ 1.158259E+00f, 7.017926E-01f, 2.813839E-05f },
|
||||
{ 1.155692E+00f, 6.888866E-01f, 2.581574E-05f },
|
||||
{ 1.152293E+00f, 6.759103E-01f, 2.369574E-05f },
|
||||
{ 1.148163E+00f, 6.629035E-01f, 2.175998E-05f },
|
||||
{ 1.143345E+00f, 6.498911E-01f, 1.999179E-05f },
|
||||
{ 1.137685E+00f, 6.368410E-01f, 1.837603E-05f },
|
||||
{ 1.130993E+00f, 6.237092E-01f, 1.689896E-05f },
|
||||
{ 1.123097E+00f, 6.104541E-01f, 1.554815E-05f },
|
||||
{ 1.113846E+00f, 5.970375E-01f, 1.431231E-05f },
|
||||
{ 1.103152E+00f, 5.834395E-01f, 1.318119E-05f },
|
||||
{ 1.091121E+00f, 5.697044E-01f, 1.214548E-05f },
|
||||
{ 1.077902E+00f, 5.558892E-01f, 1.119673E-05f },
|
||||
{ 1.063644E+00f, 5.420475E-01f, 1.032727E-05f },
|
||||
{ 1.048485E+00f, 5.282296E-01f, 9.530130E-06f },
|
||||
{ 1.032546E+00f, 5.144746E-01f, 8.798979E-06f },
|
||||
{ 1.015870E+00f, 5.007881E-01f, 8.128065E-06f },
|
||||
{ 9.984859E-01f, 4.871687E-01f, 7.512160E-06f },
|
||||
{ 9.804227E-01f, 4.736160E-01f, 6.946506E-06f },
|
||||
{ 9.617111E-01f, 4.601308E-01f, 6.426776E-06f },
|
||||
{ 9.424119E-01f, 4.467260E-01f, 0.000000E+00f },
|
||||
{ 9.227049E-01f, 4.334589E-01f, 0.000000E+00f },
|
||||
{ 9.027804E-01f, 4.203919E-01f, 0.000000E+00f },
|
||||
{ 8.828123E-01f, 4.075810E-01f, 0.000000E+00f },
|
||||
{ 8.629581E-01f, 3.950755E-01f, 0.000000E+00f },
|
||||
{ 8.432731E-01f, 3.828894E-01f, 0.000000E+00f },
|
||||
{ 8.234742E-01f, 3.709190E-01f, 0.000000E+00f },
|
||||
{ 8.032342E-01f, 3.590447E-01f, 0.000000E+00f },
|
||||
{ 7.822715E-01f, 3.471615E-01f, 0.000000E+00f },
|
||||
{ 7.603498E-01f, 3.351794E-01f, 0.000000E+00f },
|
||||
{ 7.373739E-01f, 3.230562E-01f, 0.000000E+00f },
|
||||
{ 7.136470E-01f, 3.108859E-01f, 0.000000E+00f },
|
||||
{ 6.895336E-01f, 2.987840E-01f, 0.000000E+00f },
|
||||
{ 6.653567E-01f, 2.868527E-01f, 0.000000E+00f },
|
||||
{ 6.413984E-01f, 2.751807E-01f, 0.000000E+00f },
|
||||
{ 6.178723E-01f, 2.638343E-01f, 0.000000E+00f },
|
||||
{ 5.948484E-01f, 2.528330E-01f, 0.000000E+00f },
|
||||
{ 5.723600E-01f, 2.421835E-01f, 0.000000E+00f },
|
||||
{ 5.504353E-01f, 2.318904E-01f, 0.000000E+00f },
|
||||
{ 5.290979E-01f, 2.219564E-01f, 0.000000E+00f },
|
||||
{ 5.083728E-01f, 2.123826E-01f, 0.000000E+00f },
|
||||
{ 4.883006E-01f, 2.031698E-01f, 0.000000E+00f },
|
||||
{ 4.689171E-01f, 1.943179E-01f, 0.000000E+00f },
|
||||
{ 4.502486E-01f, 1.858250E-01f, 0.000000E+00f },
|
||||
{ 4.323126E-01f, 1.776882E-01f, 0.000000E+00f },
|
||||
{ 4.150790E-01f, 1.698926E-01f, 0.000000E+00f },
|
||||
{ 3.983657E-01f, 1.623822E-01f, 0.000000E+00f },
|
||||
{ 3.819846E-01f, 1.550986E-01f, 0.000000E+00f },
|
||||
{ 3.657821E-01f, 1.479918E-01f, 0.000000E+00f },
|
||||
{ 3.496358E-01f, 1.410203E-01f, 0.000000E+00f },
|
||||
{ 3.334937E-01f, 1.341614E-01f, 0.000000E+00f },
|
||||
{ 3.174776E-01f, 1.274401E-01f, 0.000000E+00f },
|
||||
{ 3.017298E-01f, 1.208887E-01f, 0.000000E+00f },
|
||||
{ 2.863684E-01f, 1.145345E-01f, 0.000000E+00f },
|
||||
{ 2.714900E-01f, 1.083996E-01f, 0.000000E+00f },
|
||||
{ 2.571632E-01f, 1.025007E-01f, 0.000000E+00f },
|
||||
{ 2.434102E-01f, 9.684588E-02f, 0.000000E+00f },
|
||||
{ 2.302389E-01f, 9.143944E-02f, 0.000000E+00f },
|
||||
{ 2.176527E-01f, 8.628318E-02f, 0.000000E+00f },
|
||||
{ 2.056507E-01f, 8.137687E-02f, 0.000000E+00f },
|
||||
{ 1.942251E-01f, 7.671708E-02f, 0.000000E+00f },
|
||||
{ 1.833530E-01f, 7.229404E-02f, 0.000000E+00f },
|
||||
{ 1.730097E-01f, 6.809696E-02f, 0.000000E+00f },
|
||||
{ 1.631716E-01f, 6.411549E-02f, 0.000000E+00f },
|
||||
{ 1.538163E-01f, 6.033976E-02f, 0.000000E+00f },
|
||||
{ 1.449230E-01f, 5.676054E-02f, 0.000000E+00f },
|
||||
{ 1.364729E-01f, 5.336992E-02f, 0.000000E+00f },
|
||||
{ 1.284483E-01f, 5.016027E-02f, 0.000000E+00f },
|
||||
{ 1.208320E-01f, 4.712405E-02f, 0.000000E+00f },
|
||||
{ 1.136072E-01f, 4.425383E-02f, 0.000000E+00f },
|
||||
{ 1.067579E-01f, 4.154205E-02f, 0.000000E+00f },
|
||||
{ 1.002685E-01f, 3.898042E-02f, 0.000000E+00f },
|
||||
{ 9.412394E-02f, 3.656091E-02f, 0.000000E+00f },
|
||||
{ 8.830929E-02f, 3.427597E-02f, 0.000000E+00f },
|
||||
{ 8.281010E-02f, 3.211852E-02f, 0.000000E+00f },
|
||||
{ 7.761208E-02f, 3.008192E-02f, 0.000000E+00f },
|
||||
{ 7.270064E-02f, 2.816001E-02f, 0.000000E+00f },
|
||||
{ 6.806167E-02f, 2.634698E-02f, 0.000000E+00f },
|
||||
{ 6.368176E-02f, 2.463731E-02f, 0.000000E+00f },
|
||||
{ 5.954815E-02f, 2.302574E-02f, 0.000000E+00f },
|
||||
{ 5.564917E-02f, 2.150743E-02f, 0.000000E+00f },
|
||||
{ 5.197543E-02f, 2.007838E-02f, 0.000000E+00f },
|
||||
{ 4.851788E-02f, 1.873474E-02f, 0.000000E+00f },
|
||||
{ 4.526737E-02f, 1.747269E-02f, 0.000000E+00f },
|
||||
{ 4.221473E-02f, 1.628841E-02f, 0.000000E+00f },
|
||||
{ 3.934954E-02f, 1.517767E-02f, 0.000000E+00f },
|
||||
{ 3.665730E-02f, 1.413473E-02f, 0.000000E+00f },
|
||||
{ 3.412407E-02f, 1.315408E-02f, 0.000000E+00f },
|
||||
{ 3.173768E-02f, 1.223092E-02f, 0.000000E+00f },
|
||||
{ 2.948752E-02f, 1.136106E-02f, 0.000000E+00f },
|
||||
{ 2.736717E-02f, 1.054190E-02f, 0.000000E+00f },
|
||||
{ 2.538113E-02f, 9.775050E-03f, 0.000000E+00f },
|
||||
{ 2.353356E-02f, 9.061962E-03f, 0.000000E+00f },
|
||||
{ 2.182558E-02f, 8.402962E-03f, 0.000000E+00f },
|
||||
{ 2.025590E-02f, 7.797457E-03f, 0.000000E+00f },
|
||||
{ 1.881892E-02f, 7.243230E-03f, 0.000000E+00f },
|
||||
{ 1.749930E-02f, 6.734381E-03f, 0.000000E+00f },
|
||||
{ 1.628167E-02f, 6.265001E-03f, 0.000000E+00f },
|
||||
{ 1.515301E-02f, 5.830085E-03f, 0.000000E+00f },
|
||||
{ 1.410230E-02f, 5.425391E-03f, 0.000000E+00f },
|
||||
{ 1.312106E-02f, 5.047634E-03f, 0.000000E+00f },
|
||||
{ 1.220509E-02f, 4.695140E-03f, 0.000000E+00f },
|
||||
{ 1.135114E-02f, 4.366592E-03f, 0.000000E+00f },
|
||||
{ 1.055593E-02f, 4.060685E-03f, 0.000000E+00f },
|
||||
{ 9.816228E-03f, 3.776140E-03f, 0.000000E+00f },
|
||||
{ 9.128517E-03f, 3.511578E-03f, 0.000000E+00f },
|
||||
{ 8.488116E-03f, 3.265211E-03f, 0.000000E+00f },
|
||||
{ 7.890589E-03f, 3.035344E-03f, 0.000000E+00f },
|
||||
{ 7.332061E-03f, 2.820496E-03f, 0.000000E+00f },
|
||||
{ 6.809147E-03f, 2.619372E-03f, 0.000000E+00f },
|
||||
{ 6.319204E-03f, 2.430960E-03f, 0.000000E+00f },
|
||||
{ 5.861036E-03f, 2.254796E-03f, 0.000000E+00f },
|
||||
{ 5.433624E-03f, 2.090489E-03f, 0.000000E+00f },
|
||||
{ 5.035802E-03f, 1.937586E-03f, 0.000000E+00f },
|
||||
{ 4.666298E-03f, 1.795595E-03f, 0.000000E+00f },
|
||||
{ 4.323750E-03f, 1.663989E-03f, 0.000000E+00f },
|
||||
{ 4.006709E-03f, 1.542195E-03f, 0.000000E+00f },
|
||||
{ 3.713708E-03f, 1.429639E-03f, 0.000000E+00f },
|
||||
{ 3.443294E-03f, 1.325752E-03f, 0.000000E+00f },
|
||||
{ 3.194041E-03f, 1.229980E-03f, 0.000000E+00f },
|
||||
{ 2.964424E-03f, 1.141734E-03f, 0.000000E+00f },
|
||||
{ 2.752492E-03f, 1.060269E-03f, 0.000000E+00f },
|
||||
{ 2.556406E-03f, 9.848854E-04f, 0.000000E+00f },
|
||||
{ 2.374564E-03f, 9.149703E-04f, 0.000000E+00f },
|
||||
{ 2.205568E-03f, 8.499903E-04f, 0.000000E+00f },
|
||||
{ 2.048294E-03f, 7.895158E-04f, 0.000000E+00f },
|
||||
{ 1.902113E-03f, 7.333038E-04f, 0.000000E+00f },
|
||||
{ 1.766485E-03f, 6.811458E-04f, 0.000000E+00f },
|
||||
{ 1.640857E-03f, 6.328287E-04f, 0.000000E+00f },
|
||||
{ 1.524672E-03f, 5.881375E-04f, 0.000000E+00f },
|
||||
{ 1.417322E-03f, 5.468389E-04f, 0.000000E+00f },
|
||||
{ 1.318031E-03f, 5.086349E-04f, 0.000000E+00f },
|
||||
{ 1.226059E-03f, 4.732403E-04f, 0.000000E+00f },
|
||||
{ 1.140743E-03f, 4.404016E-04f, 0.000000E+00f },
|
||||
{ 1.061495E-03f, 4.098928E-04f, 0.000000E+00f },
|
||||
{ 9.877949E-04f, 3.815137E-04f, 0.000000E+00f },
|
||||
{ 9.191847E-04f, 3.550902E-04f, 0.000000E+00f },
|
||||
{ 8.552568E-04f, 3.304668E-04f, 0.000000E+00f },
|
||||
{ 7.956433E-04f, 3.075030E-04f, 0.000000E+00f },
|
||||
{ 7.400120E-04f, 2.860718E-04f, 0.000000E+00f },
|
||||
{ 6.880980E-04f, 2.660718E-04f, 0.000000E+00f },
|
||||
{ 6.397864E-04f, 2.474586E-04f, 0.000000E+00f },
|
||||
{ 5.949726E-04f, 2.301919E-04f, 0.000000E+00f },
|
||||
{ 5.535291E-04f, 2.142225E-04f, 0.000000E+00f },
|
||||
{ 5.153113E-04f, 1.994949E-04f, 0.000000E+00f },
|
||||
{ 4.801234E-04f, 1.859336E-04f, 0.000000E+00f },
|
||||
{ 4.476245E-04f, 1.734067E-04f, 0.000000E+00f },
|
||||
{ 4.174846E-04f, 1.617865E-04f, 0.000000E+00f },
|
||||
{ 3.894221E-04f, 1.509641E-04f, 0.000000E+00f },
|
||||
{ 3.631969E-04f, 1.408466E-04f, 0.000000E+00f },
|
||||
{ 3.386279E-04f, 1.313642E-04f, 0.000000E+00f },
|
||||
{ 3.156452E-04f, 1.224905E-04f, 0.000000E+00f },
|
||||
{ 2.941966E-04f, 1.142060E-04f, 0.000000E+00f },
|
||||
{ 2.742235E-04f, 1.064886E-04f, 0.000000E+00f },
|
||||
{ 2.556624E-04f, 9.931439E-05f, 0.000000E+00f },
|
||||
{ 2.384390E-04f, 9.265512E-05f, 0.000000E+00f },
|
||||
{ 2.224525E-04f, 8.647225E-05f, 0.000000E+00f },
|
||||
{ 2.076036E-04f, 8.072780E-05f, 0.000000E+00f },
|
||||
{ 1.938018E-04f, 7.538716E-05f, 0.000000E+00f },
|
||||
{ 1.809649E-04f, 7.041878E-05f, 0.000000E+00f },
|
||||
{ 1.690167E-04f, 6.579338E-05f, 0.000000E+00f },
|
||||
{ 1.578839E-04f, 6.148250E-05f, 0.000000E+00f },
|
||||
{ 1.474993E-04f, 5.746008E-05f, 0.000000E+00f },
|
||||
{ 1.378026E-04f, 5.370272E-05f, 0.000000E+00f },
|
||||
{ 1.287394E-04f, 5.018934E-05f, 0.000000E+00f },
|
||||
{ 1.202644E-04f, 4.690245E-05f, 0.000000E+00f },
|
||||
{ 1.123502E-04f, 4.383167E-05f, 0.000000E+00f },
|
||||
{ 1.049725E-04f, 4.096780E-05f, 0.000000E+00f },
|
||||
{ 9.810596E-05f, 3.830123E-05f, 0.000000E+00f },
|
||||
{ 9.172477E-05f, 3.582218E-05f, 0.000000E+00f },
|
||||
{ 8.579861E-05f, 3.351903E-05f, 0.000000E+00f },
|
||||
{ 8.028174E-05f, 3.137419E-05f, 0.000000E+00f },
|
||||
{ 7.513013E-05f, 2.937068E-05f, 0.000000E+00f },
|
||||
{ 7.030565E-05f, 2.749380E-05f, 0.000000E+00f },
|
||||
{ 6.577532E-05f, 2.573083E-05f, 0.000000E+00f },
|
||||
{ 6.151508E-05f, 2.407249E-05f, 0.000000E+00f },
|
||||
{ 5.752025E-05f, 2.251704E-05f, 0.000000E+00f },
|
||||
{ 5.378813E-05f, 2.106350E-05f, 0.000000E+00f },
|
||||
{ 5.031350E-05f, 1.970991E-05f, 0.000000E+00f },
|
||||
{ 4.708916E-05f, 1.845353E-05f, 0.000000E+00f },
|
||||
{ 4.410322E-05f, 1.728979E-05f, 0.000000E+00f },
|
||||
{ 4.133150E-05f, 1.620928E-05f, 0.000000E+00f },
|
||||
{ 3.874992E-05f, 1.520262E-05f, 0.000000E+00f },
|
||||
{ 3.633762E-05f, 1.426169E-05f, 0.000000E+00f },
|
||||
{ 3.407653E-05f, 1.337946E-05f, 0.000000E+00f },
|
||||
{ 3.195242E-05f, 1.255038E-05f, 0.000000E+00f },
|
||||
{ 2.995808E-05f, 1.177169E-05f, 0.000000E+00f },
|
||||
{ 2.808781E-05f, 1.104118E-05f, 0.000000E+00f },
|
||||
{ 2.633581E-05f, 1.035662E-05f, 0.000000E+00f },
|
||||
{ 2.469630E-05f, 9.715798E-06f, 0.000000E+00f },
|
||||
{ 2.316311E-05f, 9.116316E-06f, 0.000000E+00f },
|
||||
{ 2.172855E-05f, 8.555201E-06f, 0.000000E+00f },
|
||||
{ 2.038519E-05f, 8.029561E-06f, 0.000000E+00f },
|
||||
{ 1.912625E-05f, 7.536768E-06f, 0.000000E+00f },
|
||||
{ 1.794555E-05f, 7.074424E-06f, 0.000000E+00f },
|
||||
{ 1.683776E-05f, 6.640464E-06f, 0.000000E+00f },
|
||||
{ 1.579907E-05f, 6.233437E-06f, 0.000000E+00f },
|
||||
{ 1.482604E-05f, 5.852035E-06f, 0.000000E+00f },
|
||||
{ 1.391527E-05f, 5.494963E-06f, 0.000000E+00f },
|
||||
{ 1.306345E-05f, 5.160948E-06f, 0.000000E+00f },
|
||||
{ 1.226720E-05f, 4.848687E-06f, 0.000000E+00f },
|
||||
{ 1.152279E-05f, 4.556705E-06f, 0.000000E+00f },
|
||||
{ 1.082663E-05f, 4.283580E-06f, 0.000000E+00f },
|
||||
{ 1.017540E-05f, 4.027993E-06f, 0.000000E+00f },
|
||||
{ 9.565993E-06f, 3.788729E-06f, 0.000000E+00f },
|
||||
{ 8.995405E-06f, 3.564599E-06f, 0.000000E+00f },
|
||||
{ 8.460253E-06f, 3.354285E-06f, 0.000000E+00f },
|
||||
{ 7.957382E-06f, 3.156557E-06f, 0.000000E+00f },
|
||||
{ 7.483997E-06f, 2.970326E-06f, 0.000000E+00f },
|
||||
{ 7.037621E-06f, 2.794625E-06f, 0.000000E+00f },
|
||||
{ 6.616311E-06f, 2.628701E-06f, 0.000000E+00f },
|
||||
{ 6.219265E-06f, 2.472248E-06f, 0.000000E+00f },
|
||||
{ 5.845844E-06f, 2.325030E-06f, 0.000000E+00f },
|
||||
{ 5.495311E-06f, 2.186768E-06f, 0.000000E+00f },
|
||||
{ 5.166853E-06f, 2.057152E-06f, 0.000000E+00f },
|
||||
{ 4.859511E-06f, 1.935813E-06f, 0.000000E+00f },
|
||||
{ 4.571973E-06f, 1.822239E-06f, 0.000000E+00f },
|
||||
{ 4.302920E-06f, 1.715914E-06f, 0.000000E+00f },
|
||||
{ 4.051121E-06f, 1.616355E-06f, 0.000000E+00f },
|
||||
{ 3.815429E-06f, 1.523114E-06f, 0.000000E+00f },
|
||||
{ 3.594719E-06f, 1.435750E-06f, 0.000000E+00f },
|
||||
{ 3.387736E-06f, 1.353771E-06f, 0.000000E+00f },
|
||||
{ 3.193301E-06f, 1.276714E-06f, 0.000000E+00f },
|
||||
{ 3.010363E-06f, 1.204166E-06f, 0.000000E+00f },
|
||||
{ 2.837980E-06f, 1.135758E-06f, 0.000000E+00f },
|
||||
{ 2.675365E-06f, 1.071181E-06f, 0.000000E+00f },
|
||||
{ 2.522020E-06f, 1.010243E-06f, 0.000000E+00f },
|
||||
{ 2.377511E-06f, 9.527779E-07f, 0.000000E+00f },
|
||||
{ 2.241417E-06f, 8.986224E-07f, 0.000000E+00f },
|
||||
{ 2.113325E-06f, 8.476168E-07f, 0.000000E+00f },
|
||||
{ 1.992830E-06f, 7.996052E-07f, 0.000000E+00f },
|
||||
{ 1.879542E-06f, 7.544361E-07f, 0.000000E+00f },
|
||||
{ 1.773083E-06f, 7.119624E-07f, 0.000000E+00f },
|
||||
{ 1.673086E-06f, 6.720421E-07f, 0.000000E+00f },
|
||||
{ 1.579199E-06f, 6.345380E-07f, 0.000000E+00f }
|
||||
};
|
||||
|
||||
// CIE Standard Illuminant D65 relative spectral power distribution,
|
||||
// from 300nm to 830, at 5nm intervals
|
||||
//
|
||||
// Data source:
|
||||
// https://en.wikipedia.org/wiki/Illuminant_D65
|
||||
// https://cielab.xyz/pdf/CIE_sel_colorimetric_tables.xls
|
||||
const size_t CIE_D65_INTERVAL = 5;
|
||||
const size_t CIE_D65_START = 300;
|
||||
const size_t CIE_D65_END = 830;
|
||||
const size_t CIE_D65_COUNT = 107;
|
||||
const float CIE_D65[CIE_D65_COUNT] = {
|
||||
0.0341f,
|
||||
1.6643f,
|
||||
3.2945f,
|
||||
11.7652f,
|
||||
20.2360f,
|
||||
28.6447f,
|
||||
37.0535f,
|
||||
38.5011f,
|
||||
39.9488f,
|
||||
42.4302f,
|
||||
44.9117f,
|
||||
45.7750f,
|
||||
46.6383f,
|
||||
49.3637f,
|
||||
52.0891f,
|
||||
51.0323f,
|
||||
49.9755f,
|
||||
52.3118f,
|
||||
54.6482f,
|
||||
68.7015f,
|
||||
82.7549f,
|
||||
87.1204f,
|
||||
91.4860f,
|
||||
92.4589f,
|
||||
93.4318f,
|
||||
90.0570f,
|
||||
86.6823f,
|
||||
95.7736f,
|
||||
104.865f,
|
||||
110.936f,
|
||||
117.008f,
|
||||
117.410f,
|
||||
117.812f,
|
||||
116.336f,
|
||||
114.861f,
|
||||
115.392f,
|
||||
115.923f,
|
||||
112.367f,
|
||||
108.811f,
|
||||
109.082f,
|
||||
109.354f,
|
||||
108.578f,
|
||||
107.802f,
|
||||
106.296f,
|
||||
104.790f,
|
||||
106.239f,
|
||||
107.689f,
|
||||
106.047f,
|
||||
104.405f,
|
||||
104.225f,
|
||||
104.046f,
|
||||
102.023f,
|
||||
100.000f,
|
||||
98.1671f,
|
||||
96.3342f,
|
||||
96.0611f,
|
||||
95.7880f,
|
||||
92.2368f,
|
||||
88.6856f,
|
||||
89.3459f,
|
||||
90.0062f,
|
||||
89.8026f,
|
||||
89.5991f,
|
||||
88.6489f,
|
||||
87.6987f,
|
||||
85.4936f,
|
||||
83.2886f,
|
||||
83.4939f,
|
||||
83.6992f,
|
||||
81.8630f,
|
||||
80.0268f,
|
||||
80.1207f,
|
||||
80.2146f,
|
||||
81.2462f,
|
||||
82.2778f,
|
||||
80.2810f,
|
||||
78.2842f,
|
||||
74.0027f,
|
||||
69.7213f,
|
||||
70.6652f,
|
||||
71.6091f,
|
||||
72.9790f,
|
||||
74.3490f,
|
||||
67.9765f,
|
||||
61.6040f,
|
||||
65.7448f,
|
||||
69.8856f,
|
||||
72.4863f,
|
||||
75.0870f,
|
||||
69.3398f,
|
||||
63.5927f,
|
||||
55.0054f,
|
||||
46.4182f,
|
||||
56.6118f,
|
||||
66.8054f,
|
||||
65.0941f,
|
||||
63.3828f,
|
||||
63.8434f,
|
||||
64.3040f,
|
||||
61.8779f,
|
||||
59.4519f,
|
||||
55.7054f,
|
||||
51.9590f,
|
||||
54.6998f,
|
||||
57.4406f,
|
||||
58.8765f,
|
||||
60.3125f
|
||||
};
|
||||
|
||||
#endif //TNT_CIE_H
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "RainbowGenerator.h"
|
||||
#include "CIE.h"
|
||||
#include "rainbow.h"
|
||||
#include "srgb.h"
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
using namespace utils;
|
||||
using namespace filament::math;
|
||||
using namespace rainbow;
|
||||
|
||||
RainbowGenerator::RainbowGenerator() = default;
|
||||
|
||||
RainbowGenerator::~RainbowGenerator() = default;
|
||||
|
||||
RainbowGenerator& RainbowGenerator::lut(uint32_t count) noexcept {
|
||||
mLutSize = count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RainbowGenerator& RainbowGenerator::cosine(bool enabled) noexcept {
|
||||
mCosine = enabled;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RainbowGenerator& RainbowGenerator::minDeviation(radian_t min) noexcept {
|
||||
mMinDeviation = min;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RainbowGenerator& RainbowGenerator::maxDeviation(radian_t max) noexcept {
|
||||
mMaxDeviation = max;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RainbowGenerator& RainbowGenerator::samples(uint32_t count) noexcept {
|
||||
mSampleCount = count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RainbowGenerator& RainbowGenerator::temperature(celcius_t t) noexcept {
|
||||
mAirTemperature = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RainbowGenerator& RainbowGenerator::sunArc(radian_t arc) noexcept {
|
||||
mSunArc = arc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rainbow RainbowGenerator::build(JobSystem&) {
|
||||
uint32_t const lutsize = mLutSize;
|
||||
float const minDeviation = mMinDeviation;
|
||||
float const maxDeviation = mMaxDeviation;
|
||||
|
||||
// The sun appears as about a degree in the sky
|
||||
std::default_random_engine rng{ std::random_device{}() };
|
||||
std::uniform_real_distribution<float> distImpactAngle{ -mSunArc * 0.5f, mSunArc * 0.5f };
|
||||
std::uniform_real_distribution<float> distImpact{ -1.0f, 1.0f };
|
||||
std::uniform_int_distribution<int> distWavelengthIndex{ 0, CIE_XYZ_COUNT - 1 };
|
||||
|
||||
int32_t const count = int32_t(mSampleCount);
|
||||
|
||||
float C0, C1;
|
||||
if (mCosine) {
|
||||
float const min = 1.0f - std::cos(minDeviation);
|
||||
float const max = 1.0f - std::cos(maxDeviation);
|
||||
C0 = -1.0f / (max - min);
|
||||
C1 = (1.0f - min) / (max - min);
|
||||
} else {
|
||||
C0 = 1.0f / (maxDeviation - minDeviation);
|
||||
C1 = -minDeviation * C0;
|
||||
}
|
||||
|
||||
Rainbow rainbow{
|
||||
.s = C0,
|
||||
.o = C1,
|
||||
.scale = 0.0f,
|
||||
.data = std::vector<Rainbow::linear_sRGB_t>(lutsize, Rainbow::linear_sRGB_t{})
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
// pick a random ray
|
||||
float const impact = distImpact(rng);
|
||||
radian_t const impactAngle = distImpactAngle(rng);
|
||||
size_t const j = distWavelengthIndex(rng);
|
||||
float const w = float(CIE_XYZ_START + j);
|
||||
float const n = indexOfRefraction(w);
|
||||
|
||||
// incident = asin(impact) - impactAngle. However, because we're integrating
|
||||
// over all impact values at all impact-angles, we don't need to subtract
|
||||
// the impactAngle, because there will always be another {impact, impact-angle}
|
||||
// that will produce the same result
|
||||
radian_t const incident = std::asin(impact);
|
||||
radian_t const refracted = refractFromImpact(n, impact);
|
||||
// water-air fresnel is equal to 1 - air-water fresnel, so we only need to
|
||||
// air-water non-polarized fresnel
|
||||
|
||||
// intensity reflected upon entering the droplet (air-water)
|
||||
float const Raw = fresnel(incident, refracted);
|
||||
|
||||
// intensity reflected upon exiting the droplet (water-air)
|
||||
float const Rwa = fresnel(refracted, incident);
|
||||
|
||||
// intensity transmitted at air-water interface
|
||||
float const Taw = 1 - Raw;
|
||||
|
||||
// intensity transmitted at water-air interface
|
||||
float const Twa = 1 - Rwa;
|
||||
|
||||
for (int const bounces: { 1, 2 }) {
|
||||
radian_t const phi = rainbow::deviation(bounces, incident, refracted) - impactAngle;
|
||||
if (phi >= minDeviation && phi < maxDeviation) {
|
||||
float const v = mCosine ? std::cos(phi) : phi;
|
||||
size_t const index = size_t(std::floor(float(lutsize) * (v * C0 + C1)));
|
||||
if (index < lutsize) {
|
||||
float const T = Taw * std::pow(Rwa, float(bounces)) * Twa;
|
||||
rainbow.data[index] += T * CIE_XYZ[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert to sRGB linear and find the largest value
|
||||
for (float3& c : rainbow.data) {
|
||||
c = srgb::XYZ_to_sRGB(c);
|
||||
rainbow.scale = std::max({ rainbow.scale, c.r, c.g, c.b });
|
||||
}
|
||||
|
||||
// rescale everything to the [0, 1] range
|
||||
for (float3& c : rainbow.data) {
|
||||
c *= 1.0f / rainbow.scale;
|
||||
}
|
||||
|
||||
// integration scale factor (118.518 comes from CIE_XYZ[])
|
||||
double const s = (double(2 * lutsize) /
|
||||
(double(maxDeviation - minDeviation) * double(count * CIE_XYZ_COUNT))) / 118.518;
|
||||
|
||||
rainbow.scale *= float(s);
|
||||
//std::cout << 1/rainbow.scale << std::endl;
|
||||
|
||||
return rainbow;
|
||||
}
|
||||
|
||||
//vec3 sun = frameUniforms.lightColorIntensity.rgb *
|
||||
// (frameUniforms.lightColorIntensity.a * (4.0 * PI));
|
||||
//vec3 direction = normalize(variable_eyeDirection.xyz);
|
||||
//float cosAngle = dot(direction, -frameUniforms.lightDirection);
|
||||
//float angle = acos(cosAngle) * 180.0 / 3.14159;
|
||||
//float first = 35.0;
|
||||
//float range = (60.0 - 35.0);
|
||||
//float s = saturate((angle - first)/range);
|
||||
//int index = int(s * 255);
|
||||
//fragColor.rgb += rainbow[index]*sun;
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_RAINBOWGENERATOR_H
|
||||
#define TNT_RAINBOWGENERATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace utils {
|
||||
class JobSystem;
|
||||
};
|
||||
|
||||
struct Rainbow {
|
||||
using linear_sRGB_t = filament::math::float3;
|
||||
float s; // input parameter scale factor
|
||||
float o; // input parameter offset
|
||||
float scale; // output scale factor
|
||||
std::vector<linear_sRGB_t> data;
|
||||
};
|
||||
|
||||
class RainbowGenerator {
|
||||
public:
|
||||
using radian_t = float;
|
||||
using celcius_t = float;
|
||||
|
||||
RainbowGenerator();
|
||||
~RainbowGenerator();
|
||||
|
||||
// LUT size
|
||||
RainbowGenerator& lut(uint32_t count) noexcept;
|
||||
|
||||
// LUT indexed by cosine
|
||||
RainbowGenerator& cosine(bool enabled) noexcept;
|
||||
|
||||
// min deviation
|
||||
RainbowGenerator& minDeviation(radian_t min) noexcept;
|
||||
|
||||
// max deviation
|
||||
RainbowGenerator& maxDeviation(radian_t max) noexcept;
|
||||
|
||||
// number of samples for the calculation
|
||||
RainbowGenerator& samples(uint32_t count) noexcept;
|
||||
|
||||
// air temperature
|
||||
RainbowGenerator& temperature(celcius_t t) noexcept;
|
||||
|
||||
// sun arc
|
||||
RainbowGenerator& sunArc(radian_t arc) noexcept;
|
||||
|
||||
// bulid the rainbow LUT
|
||||
Rainbow build(utils::JobSystem& js);
|
||||
|
||||
private:
|
||||
size_t mLutSize = 256;
|
||||
radian_t mMinDeviation = 30.0f * filament::math::f::DEG_TO_RAD;
|
||||
radian_t mMaxDeviation = 60.0f * filament::math::f::DEG_TO_RAD;
|
||||
radian_t mSunArc = 1.0f * filament::math::f::DEG_TO_RAD;
|
||||
uint32_t mSampleCount = 10'000'000;
|
||||
float mAirTemperature = 20.0f;
|
||||
bool mCosine = false;
|
||||
};
|
||||
|
||||
#endif //TNT_RAINBOWGENERATOR_H
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace rainbow {
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
using radian_t = float;
|
||||
using nanometer_t = float;
|
||||
using celcius_t = float;
|
||||
|
||||
// fresnel at water droplet interface
|
||||
inline float fresnel(radian_t Bi, radian_t Bt) noexcept {
|
||||
float const r_pe = std::sin(Bi - Bt) / std::sin(Bi + Bt);
|
||||
float const r_pa = std::tan(Bi - Bt) / std::tan(Bi + Bt);
|
||||
float const r = 0.5f * (r_pe * r_pe + r_pa * r_pa);
|
||||
return r;
|
||||
}
|
||||
|
||||
// refraction angle
|
||||
inline radian_t refract(float n, radian_t incident) noexcept {
|
||||
return std::asin(std::sin(incident) / n);
|
||||
}
|
||||
|
||||
inline radian_t refractFromImpact(float n, float impact) noexcept {
|
||||
return std::asin(impact / n);
|
||||
}
|
||||
|
||||
// max incident angle for a given index of refraction
|
||||
inline radian_t maxIncidentAngle(float n) noexcept {
|
||||
return std::acos(std::sqrt((n * n - 1.0f) / 3.0f));
|
||||
}
|
||||
|
||||
// deviation: angle between the ray exiting the droplet and the ground
|
||||
// impactAngle: angle between the ground and the incident ray
|
||||
inline radian_t deviation(int bounces, radian_t incident, radian_t refracted) noexcept {
|
||||
// each bounce adds 180 degrees as well as 2*refracted
|
||||
return ((bounces & 1) ? 0.0f : f::PI) +
|
||||
float(2 + 2 * bounces) * refracted - 2.0f * incident;
|
||||
}
|
||||
|
||||
// index of refraction for a wavelength
|
||||
inline float indexOfRefraction(nanometer_t wavelength) noexcept {
|
||||
celcius_t const temperature = 20;
|
||||
constexpr float a0 = 0.244257733;
|
||||
constexpr float a1 = 0.00974634476;
|
||||
constexpr float a2 = -0.00373234996;
|
||||
constexpr float a3 = 0.000268678472;
|
||||
constexpr float a4 = 0.0015892057;
|
||||
constexpr float a5 = 0.00245934259;
|
||||
constexpr float a6 = 0.90070492;
|
||||
constexpr float a7 = -0.0166626219;
|
||||
constexpr float Ts = 273.15; // [K]
|
||||
constexpr float Ps = 1000; // [kg/m3]
|
||||
constexpr float Ls = 589; // [nm]
|
||||
constexpr float Lir = 5.432937;
|
||||
constexpr float Luv = 0.229202;
|
||||
|
||||
float const T = Ts + temperature; // temperature
|
||||
float const P = 998.2; // FIXME: this also depends on the temperature (20)
|
||||
float const L = wavelength;
|
||||
float const T_ = T / Ts;
|
||||
float const P_ = P / Ps;
|
||||
float const L_ = L / Ls;
|
||||
|
||||
float const R =
|
||||
a0 +
|
||||
a1 * P_ +
|
||||
a2 * T_ +
|
||||
a3 * L_ * L_ * T_ +
|
||||
a4 / (L_ * L_) +
|
||||
a5 / ((L_ * L_) - (Luv * Luv)) +
|
||||
a6 / ((L_ * L_) - (Lir * Lir)) +
|
||||
a7 * P_ * P_;
|
||||
|
||||
float const n = std::sqrt((1.0f + 2.0f * R * P_) / (1.0f - R * P_));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
} // namespace rainbow
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "RainbowGenerator.h"
|
||||
|
||||
#include <image/LinearImage.h>
|
||||
#include <imageio/ImageEncoder.h>
|
||||
|
||||
#include <utils/Path.h>
|
||||
#include <utils/JobSystem.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace image;
|
||||
|
||||
static const char* USAGE = R"TXT(
|
||||
RAINBOWGEN generates a rainbow:
|
||||
|
||||
Usage:
|
||||
RAINBOWGEN [options] output
|
||||
|
||||
Options:
|
||||
--help, -h
|
||||
Print this message
|
||||
|
||||
--license, -L
|
||||
Print copyright and license information
|
||||
|
||||
--format=[exr|hdr|psd|rgbm|rgb32f|png|dds], -f [format]
|
||||
Specify output file format.
|
||||
|
||||
--cosine, -c
|
||||
LUT indexed by 1-cos(deviation)
|
||||
|
||||
--size=integer, -s integer
|
||||
Size of the output rainbow texture, 256 by default.
|
||||
|
||||
--min-deviation=float, -m float
|
||||
Minimum deviation in degrees encoded in the texture, 30 by default.
|
||||
|
||||
--max-deviation=float, -M float
|
||||
Maximum deviation in degrees encoded in the texture, 60 by default.
|
||||
|
||||
--samples=integer, -S integer
|
||||
Number of samples for the simulation, 10,000,000 by default.
|
||||
|
||||
Private use only:
|
||||
--debug, -d
|
||||
Increases the height of the output texture.
|
||||
|
||||
Examples:
|
||||
RAINBOWGEN -h
|
||||
RAINBOWGEN rainbow.png
|
||||
)TXT";
|
||||
|
||||
static void printUsage(const char* name) {
|
||||
std::string const execName(utils::Path(name).getName());
|
||||
const std::string from("RAINBOWGEN");
|
||||
std::string usage(USAGE);
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
usage.replace(pos, from.length(), execName);
|
||||
}
|
||||
puts(usage.c_str());
|
||||
}
|
||||
|
||||
static void license() {
|
||||
static const char *license[] = {
|
||||
#include "licenses/licenses.inc"
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char **p = &license[0];
|
||||
while (*p)
|
||||
std::cout << *p++ << std::endl;
|
||||
}
|
||||
|
||||
static std::optional<image::ImageEncoder::Format> g_format;
|
||||
static bool g_debug = false;
|
||||
|
||||
static int handleArguments(int argc, char* argv[], RainbowGenerator& builder) {
|
||||
static constexpr const char* OPTSTR = "hLs:m:M:S:f:dc";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "license", no_argument, nullptr, 'L' },
|
||||
{ "cosine", no_argument, nullptr, 'c' },
|
||||
{ "format", required_argument, nullptr, 'f' },
|
||||
{ "size", required_argument, nullptr, 's' },
|
||||
{ "min-deviation", required_argument, nullptr, 'm' },
|
||||
{ "max-deviation", required_argument, nullptr, 'M' },
|
||||
{ "samples", required_argument, nullptr, 'S' },
|
||||
{ "debug", no_argument, nullptr, 'd' },
|
||||
{ nullptr, 0, nullptr, 0 } // termination of the option list
|
||||
};
|
||||
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &optionIndex)) >= 0) {
|
||||
std::string const arg(optarg ? optarg : "");
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
case 'L':
|
||||
license();
|
||||
exit(0);
|
||||
case 'f':
|
||||
if (arg == "png") {
|
||||
g_format = ImageEncoder::Format::PNG_LINEAR;
|
||||
} else if (arg == "hdr") {
|
||||
g_format = ImageEncoder::Format::HDR;
|
||||
} else if (arg == "rgbm") {
|
||||
g_format = ImageEncoder::Format::RGBM;
|
||||
} else if (arg == "rgb32f") {
|
||||
g_format = ImageEncoder::Format::RGB_10_11_11_REV;
|
||||
} else if (arg == "exr") {
|
||||
g_format = ImageEncoder::Format::EXR;
|
||||
} else if (arg == "psd") {
|
||||
g_format = ImageEncoder::Format::PSD;
|
||||
} else if (arg == "dds") {
|
||||
g_format = ImageEncoder::Format::DDS_LINEAR;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
builder.cosine(true);
|
||||
break;
|
||||
case 's':
|
||||
builder.lut(stoul(arg));
|
||||
break;
|
||||
case 'm':
|
||||
builder.minDeviation(stof(arg) * f::DEG_TO_RAD);
|
||||
break;
|
||||
case 'M':
|
||||
builder.maxDeviation(stof(arg) * f::DEG_TO_RAD);
|
||||
break;
|
||||
case 'S':
|
||||
builder.samples(stoul(arg));
|
||||
break;
|
||||
case 'd':
|
||||
g_debug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return optind;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
using namespace filament::math;
|
||||
using namespace image;
|
||||
|
||||
// process options
|
||||
RainbowGenerator builder;
|
||||
const int optionIndex = handleArguments(argc, argv, builder);
|
||||
const int numArgs = argc - optionIndex;
|
||||
if (numArgs < 1) {
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
utils::Path const path{ argv[optionIndex] };
|
||||
|
||||
// actual computation
|
||||
utils::JobSystem js;
|
||||
js.adopt();
|
||||
Rainbow const rainbow = builder.build(js);
|
||||
|
||||
// save result
|
||||
size_t const angleCount = rainbow.data.size();
|
||||
size_t const height = g_debug ? 32 : 1;
|
||||
|
||||
LinearImage image(angleCount, height, 3);
|
||||
float3* const pData = reinterpret_cast<float3*>(image.getPixelRef());
|
||||
for (size_t index = 0; index < angleCount; index++) {
|
||||
float3 const c = rainbow.data[index];
|
||||
//printf("vec3( %g, %g, %g ),\n", c.r, c.g, c.b);
|
||||
for (int y = 0; y < height; y++) {
|
||||
pData[angleCount * y + index] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// choose a format, either the one specified or from the name, PNG by default
|
||||
ImageEncoder::Format const format =
|
||||
g_format.has_value() ? g_format.value() :
|
||||
ImageEncoder::chooseFormat(path.getPath(), true);
|
||||
|
||||
// if we don't have an extension, pick one from the format
|
||||
std::string filename = path.getPath();
|
||||
if (path.getExtension().empty()) {
|
||||
std::string const ext = ImageEncoder::chooseExtension(format);
|
||||
filename += ext;
|
||||
}
|
||||
|
||||
std::ofstream outputStream(filename, std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(outputStream, format, image, "", "");
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace srgb {
|
||||
|
||||
using namespace filament::math;
|
||||
|
||||
inline constexpr float3 XYZ_to_sRGB(float3 const v) noexcept {
|
||||
constexpr mat3f const XYZ_sRGB{
|
||||
3.2404542f, -0.9692660f, 0.0556434f,
|
||||
-1.5371385f, 1.8760108f, -0.2040259f,
|
||||
-0.4985314f, 0.0415560f, 1.0572252f
|
||||
};
|
||||
return XYZ_sRGB * v;
|
||||
}
|
||||
|
||||
inline float3 linear_to_sRGB(float3 c) noexcept {
|
||||
for (auto i = 0; i < c.size(); i++) {
|
||||
c[i] = (c[i] <= 0.0031308f) ?
|
||||
c[i] * 12.92f : (std::pow(c[i], 1.0f / 2.4f) * 1.055f) - 0.055f;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline float3 sRGB_to_linear(float3 c) noexcept {
|
||||
for (auto i = 0; i < c.size(); i++) {
|
||||
c[i] = (c[i] <= 0.04045f) ?
|
||||
c[i] / 12.92f : std::pow((c[i] + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
} // namespace rainbow
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.56.0",
|
||||
"version": "1.56.1",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user