Compare commits
18 Commits
pf/test-ex
...
ebridgewat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f04387651 | ||
|
|
7b13964363 | ||
|
|
44b6c63fed | ||
|
|
6908f1b8af | ||
|
|
ad1b36d2b3 | ||
|
|
315c2c273f | ||
|
|
005d835dbe | ||
|
|
cade94ab2c | ||
|
|
5cd2f5626c | ||
|
|
94bbcbf1c3 | ||
|
|
52c998d2b6 | ||
|
|
527d831c15 | ||
|
|
2790f2e64c | ||
|
|
757640e850 | ||
|
|
9d9abca33a | ||
|
|
bea7a7c73f | ||
|
|
dd62f98784 | ||
|
|
395a3eda9b |
@@ -7,5 +7,3 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- Fix build/compile errors when upgrading to MacOS 15.4
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.59.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.59.1'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.59.0'
|
||||
pod 'Filament', '~> 1.59.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.59.2
|
||||
|
||||
- Fix build/compile errors when upgrading to MacOS 15.4
|
||||
|
||||
## v1.59.1
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.59.0
|
||||
VERSION_NAME=1.59.1
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -194,8 +194,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanDriver.cpp
|
||||
src/vulkan/VulkanDriver.h
|
||||
src/vulkan/VulkanDriverFactory.h
|
||||
src/vulkan/VulkanExternalImageManager.cpp
|
||||
src/vulkan/VulkanExternalImageManager.h
|
||||
src/vulkan/VulkanFboCache.cpp
|
||||
src/vulkan/VulkanFboCache.h
|
||||
src/vulkan/VulkanHandles.cpp
|
||||
@@ -257,6 +255,8 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUConstants.h
|
||||
src/webgpu/WebGPUDriver.cpp
|
||||
src/webgpu/WebGPUDriver.h
|
||||
src/webgpu/WebGPUHandles.cpp
|
||||
src/webgpu/WebGPUHandles.h
|
||||
src/webgpu/WebGPUSwapChain.cpp
|
||||
src/webgpu/WebGPUSwapChain.h
|
||||
)
|
||||
|
||||
@@ -55,4 +55,9 @@ public:
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||
const filament::backend::BufferObjectStreamDescriptor& b);
|
||||
#endif
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
||||
|
||||
@@ -1139,6 +1139,7 @@ struct ExternalSamplerDatum {
|
||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
std::string label;
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
// TODO: uncomment when needed
|
||||
|
||||
@@ -149,13 +149,6 @@ public:
|
||||
* - PlatformEGLAndroid
|
||||
*/
|
||||
bool assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired. If true, the
|
||||
* frame is aborted instead of panic. This is only supported for:
|
||||
* - PlatformMetal
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -294,16 +294,6 @@ public:
|
||||
VkQueue getProtectedGraphicsQueue() const noexcept;
|
||||
|
||||
struct ExternalImageMetadata {
|
||||
/**
|
||||
* The Filament texture format.
|
||||
*/
|
||||
TextureFormat filamentFormat;
|
||||
|
||||
/**
|
||||
* The Filament texture usage.
|
||||
*/
|
||||
TextureUsage filamentUsage;
|
||||
|
||||
/**
|
||||
* The width of the external image
|
||||
*/
|
||||
@@ -314,6 +304,11 @@ public:
|
||||
*/
|
||||
uint32_t height;
|
||||
|
||||
/**
|
||||
* The layerCount of the external image
|
||||
*/
|
||||
uint32_t layerCount;
|
||||
|
||||
/**
|
||||
* The layer count of the external image
|
||||
*/
|
||||
@@ -329,6 +324,11 @@ public:
|
||||
*/
|
||||
VkFormat format;
|
||||
|
||||
/**
|
||||
* An external buffer can be protected. This tells you if it is.
|
||||
*/
|
||||
bool isProtected;
|
||||
|
||||
/**
|
||||
* The type of external format (opaque int) if used.
|
||||
*/
|
||||
@@ -348,44 +348,20 @@ public:
|
||||
* Heap information
|
||||
*/
|
||||
uint32_t memoryTypeBits;
|
||||
|
||||
/**
|
||||
* Ycbcr conversion components
|
||||
*/
|
||||
VkComponentMapping ycbcrConversionComponents;
|
||||
|
||||
/**
|
||||
* Ycbcr model
|
||||
*/
|
||||
VkSamplerYcbcrModelConversion ycbcrModel;
|
||||
|
||||
/**
|
||||
* Ycbcr range
|
||||
*/
|
||||
VkSamplerYcbcrRange ycbcrRange;
|
||||
|
||||
/**
|
||||
* Ycbcr x chroma offset
|
||||
*/
|
||||
VkChromaLocation xChromaOffset;
|
||||
|
||||
/**
|
||||
* Ycbcr y chroma offset
|
||||
*/
|
||||
VkChromaLocation yChromaOffset;
|
||||
};
|
||||
|
||||
|
||||
// Note that the image metadata might change per-frame, hence we need a method for extracting
|
||||
// it.
|
||||
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
|
||||
return {};
|
||||
}
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||
|
||||
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
|
||||
return { VK_NULL_HANDLE, VK_NULL_HANDLE };
|
||||
}
|
||||
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat);
|
||||
|
||||
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle);
|
||||
|
||||
protected:
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||
@@ -398,6 +374,20 @@ private:
|
||||
// Platform dependent helper methods
|
||||
static ExtensionSet getSwapchainInstanceExtensionsImpl();
|
||||
|
||||
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device);
|
||||
|
||||
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
static VkSampler createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
static VkImageView createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle);
|
||||
|
||||
// Platform dependent helper methods
|
||||
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) noexcept;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace filament::backend {
|
||||
|
||||
class VulkanPlatformAndroid : public VulkanPlatform {
|
||||
public:
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
||||
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
||||
bool sRGB) noexcept;
|
||||
|
||||
struct UTILS_PUBLIC ExternalImageDescAndroid {
|
||||
@@ -39,26 +39,31 @@ public:
|
||||
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
|
||||
ExternalImageHandleRef externalImage) const noexcept;
|
||||
|
||||
virtual ExternalImageMetadata extractExternalImageMetadata(
|
||||
ExternalImageHandleRef image) const override;
|
||||
|
||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
|
||||
|
||||
protected:
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
|
||||
|
||||
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) const noexcept override;
|
||||
|
||||
private:
|
||||
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
|
||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||
bool sRGB = false;
|
||||
unsigned int width; // Texture width
|
||||
unsigned int height; // Texture height
|
||||
TextureFormat format;// Texture format
|
||||
TextureUsage usage; // Texture usage flags
|
||||
|
||||
protected:
|
||||
~ExternalImageVulkanAndroid() override;
|
||||
};
|
||||
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||
|
||||
using ImageData = VulkanPlatform::ImageData;
|
||||
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||
|
||||
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) const noexcept;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -38,6 +38,12 @@ public:
|
||||
|
||||
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
|
||||
|
||||
// TODO consider that this functionality is not WebGPU-specific, and thus could be
|
||||
// placed in a generic place and even reused across backends. Alternatively,
|
||||
// a 3rd party library could be considered. However, this was a simple and
|
||||
// quick change and works for now.
|
||||
// gets the size (height and width) of the surface/window
|
||||
[[nodiscard]] wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const;
|
||||
// either returns a valid surface or panics
|
||||
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
||||
// either returns a valid adapter or panics
|
||||
|
||||
@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
|
||||
}
|
||||
|
||||
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
|
||||
? DrawableFailureBehavior::ABORT_FRAME
|
||||
: DrawableFailureBehavior::PANIC;
|
||||
return MetalDriverFactory::create(this, driverConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,31 +54,27 @@ using namespace utils;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
static void logCompilationError(utils::io::ostream& out,
|
||||
ShaderStage shaderType, const char* name,
|
||||
static inline std::string to_string(bool b) noexcept { return b ? "true" : "false"; }
|
||||
static inline std::string to_string(int i) noexcept { return std::to_string(i); }
|
||||
static inline std::string to_string(float f) noexcept { return "float(" + std::to_string(f) + ")"; }
|
||||
|
||||
static void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
|
||||
GLuint shaderId, CString const& sourceCode) noexcept;
|
||||
static void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept;
|
||||
|
||||
static void logProgramLinkError(utils::io::ostream& out,
|
||||
const char* name, GLuint program) noexcept;
|
||||
|
||||
static inline std::string to_string(bool b) noexcept {
|
||||
return b ? "true" : "false";
|
||||
}
|
||||
|
||||
static inline std::string to_string(int i) noexcept {
|
||||
return std::to_string(i);
|
||||
}
|
||||
|
||||
static inline std::string to_string(float f) noexcept {
|
||||
return "float(" + std::to_string(f) + ")";
|
||||
}
|
||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source,
|
||||
size_t len) noexcept;
|
||||
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source,
|
||||
size_t len) noexcept;
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||
struct ProgramData {
|
||||
GLuint program{};
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
||||
shaders_t shaders{};
|
||||
};
|
||||
|
||||
~OpenGLProgramToken() override;
|
||||
@@ -90,10 +86,10 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||
ShaderCompilerService& compiler;
|
||||
utils::CString const& name;
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> attributes;
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
|
||||
shaders_source_t shaderSourceCode;
|
||||
void* user = nullptr;
|
||||
struct {
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
||||
shaders_t shaders{};
|
||||
GLuint program = 0;
|
||||
} gl; // 12 bytes
|
||||
|
||||
@@ -140,11 +136,12 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||
|
||||
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() = default;
|
||||
|
||||
void ShaderCompilerService::setUserData(const program_token_t& token, void* user) noexcept {
|
||||
/* static */ void ShaderCompilerService::setUserData(const program_token_t& token,
|
||||
void* user) noexcept {
|
||||
token->user = user;
|
||||
}
|
||||
|
||||
void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept {
|
||||
/* static */ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept {
|
||||
return token->user;
|
||||
}
|
||||
|
||||
@@ -268,101 +265,111 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
token->handle = mCallbackManager.get();
|
||||
|
||||
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
||||
if (mMode == Mode::THREAD_POOL) {
|
||||
// queue a compile job
|
||||
mCompilerThreadPool.queue(priorityQueue, token,
|
||||
[this, &gl, program = std::move(program), token]() mutable {
|
||||
// compile the shaders
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
shaders,
|
||||
token->shaderSourceCode);
|
||||
switch (mMode) {
|
||||
case Mode::THREAD_POOL: {
|
||||
// queue a compile job
|
||||
mCompilerThreadPool.queue(priorityQueue, token,
|
||||
[this, &gl, program = std::move(program), token]() mutable {
|
||||
// compile the shaders
|
||||
shaders_t shaders{};
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
shaders,
|
||||
token->shaderSourceCode);
|
||||
|
||||
// link the program
|
||||
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
||||
// link the program
|
||||
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
||||
|
||||
OpenGLProgramToken::ProgramData programData;
|
||||
programData.shaders = shaders;
|
||||
OpenGLProgramToken::ProgramData programData;
|
||||
programData.shaders = shaders;
|
||||
|
||||
// We need to query the link status here to guarantee that the
|
||||
// program is compiled and linked now (we don't want this to be
|
||||
// deferred to later). We don't care about the result at this point.
|
||||
GLint status = GL_FALSE;
|
||||
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
|
||||
programData.program = glProgram;
|
||||
// We need to query the link status here to guarantee that the
|
||||
// program is compiled and linked now (we don't want this to be
|
||||
// deferred to later). We don't care about the result at this point.
|
||||
GLint status = GL_FALSE;
|
||||
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
|
||||
programData.program = glProgram;
|
||||
|
||||
// we don't need to check for success here, it'll be done on the
|
||||
// main thread side.
|
||||
token->set(programData);
|
||||
// we don't need to check for success here, it'll be done on the
|
||||
// main thread side.
|
||||
token->set(programData);
|
||||
|
||||
mCallbackManager.put(token->handle);
|
||||
mCallbackManager.put(token->handle);
|
||||
|
||||
// caching must be the last thing we do
|
||||
if (token->key && status == GL_TRUE) {
|
||||
// Attempt to cache. This calls glGetProgramBinary.
|
||||
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
|
||||
}
|
||||
});
|
||||
// caching must be the last thing we do
|
||||
if (token->key && status == GL_TRUE) {
|
||||
// Attempt to cache. This calls glGetProgramBinary.
|
||||
mBlobCache.insert(mDriver.mPlatform, token->key, glProgram);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
// this cannot fail because we check compilation status after linking the program
|
||||
// shaders[] is filled with id of shader stages present.
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
token->gl.shaders,
|
||||
token->shaderSourceCode);
|
||||
case Mode::SYNCHRONOUS:
|
||||
case Mode::ASYNCHRONOUS: {
|
||||
// this cannot fail because we check compilation status after linking the program
|
||||
// shaders[] is filled with id of shader stages present.
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
token->gl.shaders,
|
||||
token->shaderSourceCode);
|
||||
|
||||
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
|
||||
assert_invariant(mMode != Mode::THREAD_POOL);
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// don't attempt to link this program if all shaders are not done compiling
|
||||
GLint status;
|
||||
if (token->gl.program) {
|
||||
glGetProgramiv(token->gl.program, GL_COMPLETION_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (auto shader: token->gl.shaders) {
|
||||
if (shader) {
|
||||
glGetShaderiv(shader, GL_COMPLETION_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
return false;
|
||||
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
|
||||
assert_invariant(mMode != Mode::THREAD_POOL);
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// don't attempt to link this program if all shaders are not done compiling
|
||||
GLint status;
|
||||
if (token->gl.program) {
|
||||
glGetProgramiv(token->gl.program, GL_COMPLETION_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (auto shader: token->gl.shaders) {
|
||||
if (shader) {
|
||||
glGetShaderiv(shader, GL_COMPLETION_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!token->gl.program) {
|
||||
// link the program, this also cannot fail because status is checked later.
|
||||
token->gl.program = linkProgram(mDriver.getContext(),
|
||||
token->gl.shaders, token->attributes);
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// wait until the link finishes...
|
||||
return false;
|
||||
if (!token->gl.program) {
|
||||
// link the program, this also cannot fail because status is checked later.
|
||||
token->gl.program = linkProgram(mDriver.getContext(),
|
||||
token->gl.shaders, token->attributes);
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// wait until the link finishes...
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_invariant(token->gl.program);
|
||||
assert_invariant(token->gl.program);
|
||||
|
||||
mCallbackManager.put(token->handle);
|
||||
mCallbackManager.put(token->handle);
|
||||
|
||||
if (token->key) {
|
||||
// TODO: technically we don't have to cache right now. Is it advantageous to
|
||||
// do this later, maybe depending on CPU usage?
|
||||
// attempt to cache if we don't have a thread pool (otherwise it's done
|
||||
// by the pool).
|
||||
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
||||
}
|
||||
if (token->key) {
|
||||
// TODO: technically we don't have to cache right now. Is it advantageous to
|
||||
// do this later, maybe depending on CPU usage?
|
||||
// attempt to cache if we don't have a thread pool (otherwise it's done
|
||||
// by the pool).
|
||||
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case Mode::UNDEFINED: {
|
||||
assert_invariant(false);
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
@@ -377,12 +384,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
|
||||
return program;
|
||||
}
|
||||
|
||||
void ShaderCompilerService::terminate(program_token_t& token) {
|
||||
/* static */ void ShaderCompilerService::terminate(program_token_t& token) {
|
||||
assert_invariant(token);
|
||||
|
||||
token->canceled = true;
|
||||
|
||||
bool const canceled = token->compiler.cancelTickOp(token);
|
||||
bool const isTickOpCanceled = token->compiler.cancelTickOp(token);
|
||||
|
||||
if (token->compiler.mMode == Mode::THREAD_POOL) {
|
||||
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
|
||||
@@ -395,7 +402,7 @@ void ShaderCompilerService::terminate(program_token_t& token) {
|
||||
// order for future callbacks to be successfully called.
|
||||
token->compiler.mCallbackManager.put(token->handle);
|
||||
}
|
||||
} else if (canceled) {
|
||||
} else if (isTickOpCanceled) {
|
||||
// Since the tick op was canceled, we need to .put the token here.
|
||||
token->compiler.mCallbackManager.put(token->handle);
|
||||
}
|
||||
@@ -432,7 +439,8 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) noexcept {
|
||||
/* static */ void ShaderCompilerService::getProgramFromCompilerPool(
|
||||
program_token_t& token) noexcept {
|
||||
OpenGLProgramToken::ProgramData const& programData{ token->get() };
|
||||
if (!token->canceled) {
|
||||
token->gl.shaders = programData.shaders;
|
||||
@@ -443,40 +451,53 @@ void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) n
|
||||
GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
if (!token->gl.program) {
|
||||
if (mMode == Mode::THREAD_POOL) {
|
||||
// we need this program right now, remove it from the queue
|
||||
auto job = mCompilerThreadPool.dequeue(token);
|
||||
if (job) {
|
||||
// if we were able to remove it, we execute the job now, otherwise it means
|
||||
// it's being executed right now.
|
||||
job();
|
||||
switch (mMode) {
|
||||
case Mode::THREAD_POOL: {
|
||||
// we need this program right now, remove it from the queue
|
||||
auto job = mCompilerThreadPool.dequeue(token);
|
||||
if (job) {
|
||||
// if we were able to remove it, we execute the job now, otherwise it means
|
||||
// it's being executed right now.
|
||||
job();
|
||||
}
|
||||
|
||||
if (!token->canceled) {
|
||||
token->compiler.cancelTickOp(token);
|
||||
}
|
||||
|
||||
// Block until we get the program from the pool. Generally this wouldn't block
|
||||
// because we just compiled the program above, when executing job.
|
||||
ShaderCompilerService::getProgramFromCompilerPool(token);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!token->canceled) {
|
||||
case Mode::ASYNCHRONOUS: {
|
||||
// we force the program link -- which might stall, either here or below in
|
||||
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
|
||||
token->compiler.cancelTickOp(token);
|
||||
|
||||
token->gl.program =
|
||||
linkProgram(mDriver.getContext(), token->gl.shaders, token->attributes);
|
||||
|
||||
assert_invariant(token->gl.program);
|
||||
|
||||
mCallbackManager.put(token->handle);
|
||||
|
||||
if (token->key) {
|
||||
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Block until we get the program from the pool. Generally this wouldn't block
|
||||
// because we just compiled the program above, when executing job.
|
||||
ShaderCompilerService::getProgramFromCompilerPool(token);
|
||||
} else if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// we force the program link -- which might stall, either here or below in
|
||||
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
|
||||
token->compiler.cancelTickOp(token);
|
||||
|
||||
token->gl.program = linkProgram(mDriver.getContext(),
|
||||
token->gl.shaders, token->attributes);
|
||||
|
||||
assert_invariant(token->gl.program);
|
||||
|
||||
mCallbackManager.put(token->handle);
|
||||
|
||||
if (token->key) {
|
||||
mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program);
|
||||
case Mode::SYNCHRONOUS: {
|
||||
// if we don't have a program yet, block until we get it.
|
||||
tick();
|
||||
break;
|
||||
}
|
||||
|
||||
case Mode::UNDEFINED: {
|
||||
assert_invariant(false);
|
||||
}
|
||||
} else {
|
||||
// if we don't have a program yet, block until we get it.
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,12 +540,12 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
* checked until after the program is linked.
|
||||
* This always returns the GL shader IDs or zero a shader stage is not present.
|
||||
*/
|
||||
void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
/* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
||||
shaders_t& outShaders,
|
||||
UTILS_UNUSED_IN_RELEASE shaders_source_t& outShaderSourceCode) noexcept {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
|
||||
@@ -635,209 +656,13 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
}
|
||||
}
|
||||
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
// drivers don't allow the quotation marks. This source modification happens in-place.
|
||||
void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept {
|
||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
||||
removeGoogleLineDirectives(source, len); // length is unaffected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
|
||||
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
|
||||
// the number of views, which is assumed as a single digit, for multiview.
|
||||
// This source modification happens in-place.
|
||||
void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
|
||||
int32_t eyeCount, char* source, size_t len) noexcept {
|
||||
// We don't use regular expression in favor of performance.
|
||||
if (context.ext.OVR_multiview2) {
|
||||
const std::string_view shader{ source, len };
|
||||
const std::string_view layout = "layout";
|
||||
const std::string_view num_views = "num_views";
|
||||
size_t found = 0;
|
||||
while (true) {
|
||||
found = shader.find(layout, found);
|
||||
if (found == std::string_view::npos) {
|
||||
break;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + layout.size());
|
||||
if (found == std::string_view::npos || shader[found] != '(') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
if (shader.compare(found, num_views.size(), num_views) != 0) {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + num_views.size());
|
||||
if (found == std::string_view::npos || shader[found] != '=') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
// We assume the value should be one-digit number.
|
||||
assert_invariant(eyeCount < 10);
|
||||
assert_invariant(!::isdigit(source[found + 1]));
|
||||
source[found] = '0' + eyeCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||
// macOS doesn't support GL_ARB_shading_language_packing
|
||||
// Also GLES3.0 didn't have the full set of packing/unpacking functions
|
||||
std::string_view ShaderCompilerService::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
|
||||
using namespace std::literals;
|
||||
#ifdef BACKEND_OPENGL_VERSION_GL
|
||||
if (!context.isAtLeastGL<4, 2>() && !context.ext.ARB_shading_language_packing) {
|
||||
return R"(
|
||||
|
||||
// these don't handle denormals, NaNs or inf
|
||||
float u16tofp32(highp uint v) {
|
||||
v <<= 16u;
|
||||
highp uint s = v & 0x80000000u;
|
||||
highp uint n = v & 0x7FFFFFFFu;
|
||||
highp uint nz = (n == 0u) ? 0u : 0xFFFFFFFFu;
|
||||
return uintBitsToFloat(s | ((((n >> 3u) + (0x70u << 23u))) & nz));
|
||||
}
|
||||
vec2 unpackHalf2x16(highp uint v) {
|
||||
return vec2(u16tofp32(v&0xFFFFu), u16tofp32(v>>16u));
|
||||
}
|
||||
uint fp32tou16(float val) {
|
||||
uint f32 = floatBitsToUint(val);
|
||||
uint f16 = 0u;
|
||||
uint sign = (f32 >> 16u) & 0x8000u;
|
||||
int exponent = int((f32 >> 23u) & 0xFFu) - 127;
|
||||
uint mantissa = f32 & 0x007FFFFFu;
|
||||
if (exponent > 15) {
|
||||
f16 = sign | (0x1Fu << 10u);
|
||||
} else if (exponent > -15) {
|
||||
exponent += 15;
|
||||
mantissa >>= 13;
|
||||
f16 = sign | uint(exponent << 10) | mantissa;
|
||||
} else {
|
||||
f16 = sign;
|
||||
}
|
||||
return f16;
|
||||
}
|
||||
highp uint packHalf2x16(vec2 v) {
|
||||
highp uint x = fp32tou16(v.x);
|
||||
highp uint y = fp32tou16(v.y);
|
||||
return (y << 16u) | x;
|
||||
}
|
||||
highp uint packUnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||
highp uint a = uint(v.x);
|
||||
highp uint b = uint(v.y) << 8;
|
||||
highp uint c = uint(v.z) << 16;
|
||||
highp uint d = uint(v.w) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
highp uint packSnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||
highp uint a = uint((int(v.x) & 0xff));
|
||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||
return vec4(float((v & 0x000000ffu) ),
|
||||
float((v & 0x0000ff00u) >> 8),
|
||||
float((v & 0x00ff0000u) >> 16),
|
||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||
}
|
||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
#endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
|
||||
return R"(
|
||||
|
||||
highp uint packUnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||
highp uint a = uint(v.x);
|
||||
highp uint b = uint(v.y) << 8;
|
||||
highp uint c = uint(v.z) << 16;
|
||||
highp uint d = uint(v.w) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
highp uint packSnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||
highp uint a = uint((int(v.x) & 0xff));
|
||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||
return vec4(float((v & 0x000000ffu) ),
|
||||
float((v & 0x0000ff00u) >> 8),
|
||||
float((v & 0x00ff0000u) >> 16),
|
||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||
}
|
||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
#endif // BACKEND_OPENGL_VERSION_GLES
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
// split shader source code in three:
|
||||
// - the version line
|
||||
// - extensions
|
||||
// - everything else
|
||||
std::array<std::string_view, 3> ShaderCompilerService::splitShaderSource(std::string_view source) noexcept {
|
||||
auto version_start = source.find("#version");
|
||||
assert_invariant(version_start != std::string_view::npos);
|
||||
|
||||
auto version_eol = source.find('\n', version_start) + 1;
|
||||
assert_invariant(version_eol != std::string_view::npos);
|
||||
|
||||
auto prolog_start = version_eol;
|
||||
auto prolog_eol = source.rfind("\n#extension"); // last #extension line
|
||||
if (prolog_eol == std::string_view::npos) {
|
||||
prolog_eol = prolog_start;
|
||||
} else {
|
||||
prolog_eol = source.find('\n', prolog_eol + 1) + 1;
|
||||
}
|
||||
auto body_start = prolog_eol;
|
||||
|
||||
std::string_view const version = source.substr(version_start, version_eol - version_start);
|
||||
std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
|
||||
std::string_view const body = source.substr(body_start, source.length() - body_start);
|
||||
return { version, prolog, body };
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a program from the given shader IDs and links it. This cannot fail because errors
|
||||
* are checked later. This always returns a valid GL program ID (which doesn't mean the
|
||||
* program itself is valid).
|
||||
*/
|
||||
GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
|
||||
/* static */ GLuint ShaderCompilerService::linkProgram(OpenGLContext& context,
|
||||
shaders_t const& shaders,
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
@@ -913,7 +738,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
* Checks a program link status and logs errors and frees resources on failure.
|
||||
* Returns true on success.
|
||||
*/
|
||||
bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noexcept {
|
||||
/* static */ bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noexcept {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
|
||||
@@ -948,20 +773,24 @@ bool ShaderCompilerService::checkProgramStatus(program_token_t const& token) noe
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
UTILS_NOINLINE
|
||||
void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
||||
const char* name, GLuint shaderId,
|
||||
UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
||||
/* static */ void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
|
||||
GLuint shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
||||
|
||||
auto to_string = [](ShaderStage type) -> const char* {
|
||||
switch (type) {
|
||||
case ShaderStage::VERTEX: return "vertex";
|
||||
case ShaderStage::FRAGMENT: return "fragment";
|
||||
case ShaderStage::COMPUTE: return "compute";
|
||||
case ShaderStage::VERTEX:
|
||||
return "vertex";
|
||||
case ShaderStage::FRAGMENT:
|
||||
return "fragment";
|
||||
case ShaderStage::COMPUTE:
|
||||
return "compute";
|
||||
}
|
||||
};
|
||||
|
||||
{ // scope for the temporary string storage
|
||||
{// scope for the temporary string storage
|
||||
GLint length = 0;
|
||||
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);
|
||||
|
||||
@@ -969,8 +798,7 @@ void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
||||
glGetShaderInfoLog(shaderId, length, nullptr, infoLog.data());
|
||||
|
||||
out << "Compilation error in " << to_string(shaderType) << " shader \"" << name << "\":\n"
|
||||
<< "\"" << infoLog.c_str() << "\""
|
||||
<< io::endl;
|
||||
<< "\"" << infoLog.c_str() << "\"" << io::endl;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -996,7 +824,7 @@ void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
||||
/* static */ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
||||
GLint length = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
|
||||
|
||||
@@ -1004,9 +832,204 @@ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noe
|
||||
glGetProgramInfoLog(program, length, nullptr, infoLog.data());
|
||||
|
||||
out << "Link error in \"" << name << "\":\n"
|
||||
<< "\"" << infoLog.c_str() << "\""
|
||||
<< io::endl;
|
||||
<< "\"" << infoLog.c_str() << "\"" << io::endl;
|
||||
}
|
||||
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
// drivers don't allow the quotation marks. This source modification happens in-place.
|
||||
/* static */ void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source,
|
||||
size_t len) noexcept {
|
||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
||||
removeGoogleLineDirectives(source, len);// length is unaffected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
|
||||
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
|
||||
// the number of views, which is assumed as a single digit, for multiview.
|
||||
// This source modification happens in-place.
|
||||
/* static */ void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source,
|
||||
size_t len) noexcept {
|
||||
// We don't use regular expression in favor of performance.
|
||||
if (context.ext.OVR_multiview2) {
|
||||
const std::string_view shader{ source, len };
|
||||
const std::string_view layout = "layout";
|
||||
const std::string_view num_views = "num_views";
|
||||
size_t found = 0;
|
||||
while (true) {
|
||||
found = shader.find(layout, found);
|
||||
if (found == std::string_view::npos) {
|
||||
break;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + layout.size());
|
||||
if (found == std::string_view::npos || shader[found] != '(') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
if (shader.compare(found, num_views.size(), num_views) != 0) {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + num_views.size());
|
||||
if (found == std::string_view::npos || shader[found] != '=') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
// We assume the value should be one-digit number.
|
||||
assert_invariant(eyeCount < 10);
|
||||
assert_invariant(!::isdigit(source[found + 1]));
|
||||
source[found] = '0' + eyeCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||
// macOS doesn't support GL_ARB_shading_language_packing
|
||||
// Also GLES3.0 didn't have the full set of packing/unpacking functions
|
||||
/* static */ std::string_view process_ARB_shading_language_packing(
|
||||
OpenGLContext& context) noexcept {
|
||||
using namespace std::literals;
|
||||
#ifdef BACKEND_OPENGL_VERSION_GL
|
||||
if (!context.isAtLeastGL<4, 2>() && !context.ext.ARB_shading_language_packing) {
|
||||
return R"(
|
||||
|
||||
// these don't handle denormals, NaNs or inf
|
||||
float u16tofp32(highp uint v) {
|
||||
v <<= 16u;
|
||||
highp uint s = v & 0x80000000u;
|
||||
highp uint n = v & 0x7FFFFFFFu;
|
||||
highp uint nz = (n == 0u) ? 0u : 0xFFFFFFFFu;
|
||||
return uintBitsToFloat(s | ((((n >> 3u) + (0x70u << 23u))) & nz));
|
||||
}
|
||||
vec2 unpackHalf2x16(highp uint v) {
|
||||
return vec2(u16tofp32(v&0xFFFFu), u16tofp32(v>>16u));
|
||||
}
|
||||
uint fp32tou16(float val) {
|
||||
uint f32 = floatBitsToUint(val);
|
||||
uint f16 = 0u;
|
||||
uint sign = (f32 >> 16u) & 0x8000u;
|
||||
int exponent = int((f32 >> 23u) & 0xFFu) - 127;
|
||||
uint mantissa = f32 & 0x007FFFFFu;
|
||||
if (exponent > 15) {
|
||||
f16 = sign | (0x1Fu << 10u);
|
||||
} else if (exponent > -15) {
|
||||
exponent += 15;
|
||||
mantissa >>= 13;
|
||||
f16 = sign | uint(exponent << 10) | mantissa;
|
||||
} else {
|
||||
f16 = sign;
|
||||
}
|
||||
return f16;
|
||||
}
|
||||
highp uint packHalf2x16(vec2 v) {
|
||||
highp uint x = fp32tou16(v.x);
|
||||
highp uint y = fp32tou16(v.y);
|
||||
return (y << 16u) | x;
|
||||
}
|
||||
highp uint packUnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||
highp uint a = uint(v.x);
|
||||
highp uint b = uint(v.y) << 8;
|
||||
highp uint c = uint(v.z) << 16;
|
||||
highp uint d = uint(v.w) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
highp uint packSnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||
highp uint a = uint((int(v.x) & 0xff));
|
||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||
return vec4(float((v & 0x000000ffu) ),
|
||||
float((v & 0x0000ff00u) >> 8),
|
||||
float((v & 0x00ff0000u) >> 16),
|
||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||
}
|
||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
#endif// BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
if (!context.isES2() && !context.isAtLeastGLES<3, 1>()) {
|
||||
return R"(
|
||||
|
||||
highp uint packUnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, 0.0, 1.0) * 255.0);
|
||||
highp uint a = uint(v.x);
|
||||
highp uint b = uint(v.y) << 8;
|
||||
highp uint c = uint(v.z) << 16;
|
||||
highp uint d = uint(v.w) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
highp uint packSnorm4x8(mediump vec4 v) {
|
||||
v = round(clamp(v, -1.0, 1.0) * 127.0);
|
||||
highp uint a = uint((int(v.x) & 0xff));
|
||||
highp uint b = uint((int(v.y) & 0xff)) << 8;
|
||||
highp uint c = uint((int(v.z) & 0xff)) << 16;
|
||||
highp uint d = uint((int(v.w) & 0xff)) << 24;
|
||||
return (a|b|c|d);
|
||||
}
|
||||
mediump vec4 unpackUnorm4x8(highp uint v) {
|
||||
return vec4(float((v & 0x000000ffu) ),
|
||||
float((v & 0x0000ff00u) >> 8),
|
||||
float((v & 0x00ff0000u) >> 16),
|
||||
float((v & 0xff000000u) >> 24)) / 255.0;
|
||||
}
|
||||
mediump vec4 unpackSnorm4x8(highp uint v) {
|
||||
int a = int(((v ) & 0xffu) << 24u) >> 24 ;
|
||||
int b = int(((v >> 8u) & 0xffu) << 24u) >> 24 ;
|
||||
int c = int(((v >> 16u) & 0xffu) << 24u) >> 24 ;
|
||||
int d = int(((v >> 24u) & 0xffu) << 24u) >> 24 ;
|
||||
return clamp(vec4(float(a), float(b), float(c), float(d)) / 127.0, -1.0, 1.0);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
#endif// BACKEND_OPENGL_VERSION_GLES
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
// split shader source code in three:
|
||||
// - the version line
|
||||
// - extensions
|
||||
// - everything else
|
||||
/* static */ std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept {
|
||||
auto version_start = source.find("#version");
|
||||
assert_invariant(version_start != std::string_view::npos);
|
||||
|
||||
auto version_eol = source.find('\n', version_start) + 1;
|
||||
assert_invariant(version_eol != std::string_view::npos);
|
||||
|
||||
auto prolog_start = version_eol;
|
||||
auto prolog_eol = source.rfind("\n#extension");// last #extension line
|
||||
if (prolog_eol == std::string_view::npos) {
|
||||
prolog_eol = prolog_start;
|
||||
} else {
|
||||
prolog_eol = source.find('\n', prolog_eol + 1) + 1;
|
||||
}
|
||||
auto body_start = prolog_eol;
|
||||
|
||||
std::string_view const version = source.substr(version_start, version_eol - version_start);
|
||||
std::string_view const prolog = source.substr(prolog_start, prolog_eol - prolog_start);
|
||||
std::string_view const body = source.substr(body_start, source.length() - body_start);
|
||||
return { version, prolog, body };
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -57,6 +57,8 @@ class ShaderCompilerService {
|
||||
|
||||
public:
|
||||
using program_token_t = std::shared_ptr<OpenGLProgramToken>;
|
||||
using shaders_t = std::array<GLuint, Program::SHADER_TYPE_COUNT>;
|
||||
using shaders_source_t = std::array<utils::CString, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
explicit ShaderCompilerService(OpenGLDriver& driver);
|
||||
|
||||
@@ -134,22 +136,9 @@ private:
|
||||
OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
|
||||
bool multiview, shaders_t& outShaders, shaders_source_t& outShaderSourceCode) noexcept;
|
||||
|
||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
|
||||
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
|
||||
|
||||
static GLuint linkProgram(OpenGLContext& context,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
|
||||
static GLuint linkProgram(OpenGLContext& context, shaders_t const& shaders,
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept;
|
||||
|
||||
static bool checkProgramStatus(program_token_t const& token) noexcept;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/BufferObjectStreamDescriptor.h>
|
||||
#include <backend/DescriptorSetOffsetArray.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PipelineState.h>
|
||||
@@ -437,6 +438,10 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
|
||||
<< ", user=" << b.getUser() << " }";
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
|
||||
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
|
||||
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
||||
return out << "PixelBufferDescriptor{ " << base
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_VALIDATION)
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
|
||||
#else
|
||||
#define FVK_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
@@ -200,7 +200,9 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
||||
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
|
||||
mResourceManager(
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags),
|
||||
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
||||
mPlatform->getDevice())),
|
||||
@@ -219,8 +221,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mQueryManager(mPlatform->getDevice()),
|
||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
||||
&mDescriptorSetLayoutCache),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
|
||||
@@ -313,7 +313,7 @@ void VulkanDriver::terminate() {
|
||||
|
||||
mCurrentSwapChain = {};
|
||||
mDefaultRenderTarget = {};
|
||||
mPipelineState = {};
|
||||
mBoundPipeline = {};
|
||||
|
||||
mQueryManager.terminate();
|
||||
|
||||
@@ -325,13 +325,9 @@ void VulkanDriver::terminate() {
|
||||
|
||||
mCommands.terminate();
|
||||
|
||||
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
|
||||
// descriptorSetLayoutCache
|
||||
mExternalImageManager.terminate();
|
||||
|
||||
mStagePool.terminate();
|
||||
mPipelineCache.terminate();
|
||||
mFramebufferCache.reset();
|
||||
mFramebufferCache.terminate();
|
||||
mSamplerCache.terminate();
|
||||
mDescriptorSetLayoutCache.terminate();
|
||||
mDescriptorSetCache.terminate();
|
||||
@@ -385,10 +381,6 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
// Do nothing.
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
mExternalImageManager.onBeginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
||||
@@ -429,16 +421,12 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
|
||||
if (mExternalImageManager.isExternallySampledTexture(texture)) {
|
||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
||||
mAppState.hasBoundExternalImages = true;
|
||||
} else {
|
||||
VulkanSamplerCache::Params cacheParams = {
|
||||
.sampler = params,
|
||||
};
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||
}
|
||||
// TODO: YcbcrConversion?
|
||||
VulkanSamplerCache::Params cacheParams = {
|
||||
.sampler = params,
|
||||
};
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||
}
|
||||
|
||||
void VulkanDriver::flush(int) {
|
||||
@@ -575,35 +563,41 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto const& metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
||||
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
if (metadata.isProtected) {
|
||||
usage |= backend::TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
// In theory the following are reasonable expectations, but in practice it's hard for client's
|
||||
// to match up the dimensions of the texture with that of the AHB.
|
||||
// assert_invariant(width == metadata.width);
|
||||
// assert_invariant(height == metadata.height);
|
||||
// assert_invariant(format == metadata.filamentFormat);
|
||||
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
VkImageUsageFlags vkUsage = metadata.usage;
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
assert_invariant(width == metadata.width);
|
||||
assert_invariant(height == metadata.height);
|
||||
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
uint32_t const memoryTypeIndex =
|
||||
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||
<< "failed to find a valid memory type for external image memory.";
|
||||
|
||||
VkImage vkimg;
|
||||
VkDeviceMemory deviceMemory;
|
||||
std::tie(vkimg, deviceMemory) = mPlatform->createVkImageFromExternal(externalImage);
|
||||
|
||||
VkSamplerYcbcrConversion conversion =
|
||||
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
|
||||
std::tie(vkimg, deviceMemory) =
|
||||
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
||||
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
|
||||
metadata.format, conversion, metadata.samples, metadata.width, metadata.height,
|
||||
metadata.layers, usage, mStagePool);
|
||||
|
||||
if (conversion != VK_NULL_HANDLE) {
|
||||
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
|
||||
}
|
||||
|
||||
// Unlike uploaded textures or swapchains, we need to explicit transition this
|
||||
// texture into the read layout.
|
||||
auto& commands = mCommands.get();
|
||||
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
|
||||
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
|
||||
metadata.layerCount, usage, mStagePool);
|
||||
|
||||
texture.inc();
|
||||
}
|
||||
@@ -636,8 +630,6 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
}
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
texture.dec();
|
||||
|
||||
mExternalImageManager.removeExternallySampledTexture(texture);
|
||||
}
|
||||
|
||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
@@ -806,11 +798,6 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||
auto set = mDescriptorSetCache.createSet(dsh, layout);
|
||||
set.inc();
|
||||
|
||||
if (layout->hasExternalSamplers()) {
|
||||
mAppState.hasExternalSamplerLayouts = true;
|
||||
mExternalImageManager.addDescriptorSet(layout, set);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
||||
@@ -930,19 +917,11 @@ void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh) {
|
||||
auto layout = resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||
layout.dec();
|
||||
|
||||
if (layout->hasExternalSamplers()) {
|
||||
mExternalImageManager.removeDescriptorSetLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
set.dec();
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
mExternalImageManager.removeDescriptorSet(set);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
|
||||
@@ -1503,6 +1482,7 @@ void VulkanDriver::endRenderPass(int) {
|
||||
// pipeline barrier between framebuffer writes and shader reads.
|
||||
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
||||
|
||||
mRenderPassFboInfo = {};
|
||||
mCurrentRenderPass.renderTarget = {};
|
||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||
|
||||
@@ -1543,7 +1523,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
swapChain->acquire(resized);
|
||||
|
||||
if (resized) {
|
||||
mFramebufferCache.reset();
|
||||
mFramebufferCache.resetFramebuffers();
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
||||
@@ -1562,10 +1542,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
||||
|
||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
|
||||
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
|
||||
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||
mPipelineState.pipelineLayout, stage, index, value);
|
||||
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||
mBoundPipeline.pipelineLayout, stage, index, value);
|
||||
}
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string) {
|
||||
@@ -1744,27 +1724,6 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
// We need to determine whether to delay bindning until draw().
|
||||
mPipelineState.bindInDraw.first = false;
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto& layouts = pipelineState.pipelineLayout.setLayout;
|
||||
auto haveExternalSamplers = [&](auto hwHandle) {
|
||||
if (!hwHandle) {
|
||||
return false;
|
||||
}
|
||||
auto layout =
|
||||
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, hwHandle);
|
||||
return layout->hasExternalSamplers();
|
||||
};
|
||||
if (std::any_of(layouts.begin(), layouts.end(), haveExternalSamplers)) {
|
||||
mPipelineState.bindInDraw = { true, pipelineState };
|
||||
return;
|
||||
}
|
||||
}
|
||||
bindPipelineImpl(pipelineState);
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto commands = mCurrentRenderPass.commandBuffer;
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
|
||||
@@ -1833,11 +1792,10 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState) {
|
||||
|
||||
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
|
||||
|
||||
mPipelineState = {
|
||||
mBoundPipeline = {
|
||||
.program = program,
|
||||
.pipelineLayout = pipelineLayout,
|
||||
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
|
||||
.bindInDraw = {false, {}},
|
||||
};
|
||||
|
||||
mPipelineCache.bindLayout(pipelineLayout);
|
||||
@@ -1884,24 +1842,14 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto const& [bindInDraw, pipelineSt] = mPipelineState.bindInDraw;
|
||||
bool const hasUpdated =
|
||||
mExternalImageManager.prepareBindSets(mDescriptorSetCache.getBoundSets());
|
||||
if (bindInDraw || hasUpdated) {
|
||||
bindPipelineImpl(pipelineSt);
|
||||
}
|
||||
mPipelineState.bindInDraw.first = false;
|
||||
}
|
||||
|
||||
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
|
||||
mPipelineState.pipelineLayout,
|
||||
mPipelineState.descriptorSetMask);
|
||||
mBoundPipeline.pipelineLayout,
|
||||
mBoundPipeline.descriptorSetMask);
|
||||
|
||||
// Finally, make the actual draw call. TODO: support subranges
|
||||
uint32_t const firstIndex = indexOffset;
|
||||
constexpr int32_t vertexOffset = 0;
|
||||
constexpr uint32_t firstInstId = 0;
|
||||
const uint32_t firstIndex = indexOffset;
|
||||
const int32_t vertexOffset = 0;
|
||||
const uint32_t firstInstId = 0;
|
||||
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||
#include "vulkan/VulkanExternalImageManager.h"
|
||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
@@ -120,7 +119,6 @@ private:
|
||||
|
||||
private:
|
||||
void collectGarbage();
|
||||
void bindPipelineImpl(PipelineState const& pipelineState);
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
fvkmemory::ResourceManager mResourceManager;
|
||||
@@ -145,29 +143,21 @@ private:
|
||||
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||
VulkanQueryManager mQueryManager;
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
|
||||
// This is necessary for us to write to push constants after binding a pipeline.
|
||||
struct {
|
||||
// For push constant
|
||||
resource_ptr<VulkanProgram> program;
|
||||
// For push commiting dynamic ubos in draw()
|
||||
VkPipelineLayout pipelineLayout;
|
||||
fvkutils::DescriptorSetMask descriptorSetMask;
|
||||
} mBoundPipeline = {};
|
||||
|
||||
std::pair<bool, PipelineState> bindInDraw = {false, {}};
|
||||
} mPipelineState = {};
|
||||
|
||||
// We need to store information about a render pass to enable better barriers at the end of a
|
||||
// renderpass.
|
||||
struct {
|
||||
// This tracks whether the app has seen external samplers bound to a the descriptor set.
|
||||
// This will force bindPipeline to take a slow path.
|
||||
bool hasExternalSamplerLayouts = false;
|
||||
bool hasBoundExternalImages = false;
|
||||
|
||||
bool hasExternalSamplers() const noexcept {
|
||||
return hasExternalSamplerLayouts && hasBoundExternalImages;
|
||||
}
|
||||
} mAppState;
|
||||
using AttachmentArray =
|
||||
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
|
||||
AttachmentArray attachments;
|
||||
} mRenderPassFboInfo = {};
|
||||
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanExternalImageManager.h"
|
||||
|
||||
#include "VulkanDescriptorSetCache.h"
|
||||
#include "VulkanDescriptorSetLayoutCache.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
void erase(std::vector<T>& v, std::function<bool(T const&)> f) {
|
||||
auto newEnd = std::remove_if(v.begin(), v.end(), f);
|
||||
v.erase(newEnd, v.end());
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
|
||||
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
|
||||
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
|
||||
: mPlatform(platform),
|
||||
mSamplerCache(samplerCache),
|
||||
mYcbcrConversionCache(ycbcrConversionCache),
|
||||
mDescriptorSetCache(setCache),
|
||||
mDescriptorSetLayoutCache(layoutCache) {
|
||||
}
|
||||
|
||||
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
|
||||
|
||||
void VulkanExternalImageManager::terminate() {
|
||||
mSetAndLayouts.clear();
|
||||
mSetBindings.clear();
|
||||
mImages.clear();
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::onBeginFrame() {
|
||||
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
|
||||
image.hasBeenValidated = false;
|
||||
});
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::prepareBindSets(SetArray const& sets) {
|
||||
bool hasUpdated = false;
|
||||
for (auto set: sets) {
|
||||
if (!set) {
|
||||
continue;
|
||||
}
|
||||
if (auto itr = std::find_if(mSetAndLayouts.begin(), mSetAndLayouts.end(),
|
||||
[&](auto const& setAndLayout) { return setAndLayout.first == set; });
|
||||
itr != mSetAndLayouts.end()) {
|
||||
hasUpdated = updateSetAndLayout(itr->first, itr->second) || hasUpdated;
|
||||
}
|
||||
}
|
||||
return hasUpdated;
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::updateSetAndLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto findImage = [&](fvkmemory::resource_ptr<VulkanTexture> texture) -> ImageData* {
|
||||
auto itr = std::find_if(mImages.begin(), mImages.end(), [&](ImageData const& data) {
|
||||
return data.ptr == texture;
|
||||
});
|
||||
assert_invariant(itr != mImages.end());
|
||||
return &(*itr);
|
||||
};
|
||||
|
||||
//std::vector<std::pair<uint8_t, ImageData*>> externalImages;
|
||||
utils::FixedCapacityVector<std::pair<uint8_t, VkSampler>> samplerAndBindings;
|
||||
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
|
||||
|
||||
bool hasImageUpdates = false;
|
||||
for (auto& bindingInfo : mSetBindings) {
|
||||
if (bindingInfo.set != set) {
|
||||
continue;
|
||||
}
|
||||
auto imageData = findImage(bindingInfo.image);
|
||||
hasImageUpdates = updateImage(imageData) || hasImageUpdates;
|
||||
|
||||
auto samplerParams = bindingInfo.samplerParams;
|
||||
// according to spec, these must match chromaFilter
|
||||
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
|
||||
samplerParams.filterMag = SamplerMagFilter::NEAREST;
|
||||
samplerParams.filterMin = SamplerMinFilter::NEAREST;
|
||||
|
||||
auto sampler = mSamplerCache->getSampler({
|
||||
.sampler = samplerParams,
|
||||
.conversion = imageData->conversion,
|
||||
});
|
||||
samplerAndBindings.push_back({ bindingInfo.binding, sampler });
|
||||
}
|
||||
|
||||
// We need to sort by binding number
|
||||
std::sort(samplerAndBindings.begin(), samplerAndBindings.end());
|
||||
|
||||
utils::FixedCapacityVector<VkSampler> outSamplers;
|
||||
outSamplers.reserve(MAX_SAMPLER_COUNT);
|
||||
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
|
||||
[&](auto const& b) { outSamplers.push_back(b.second); });
|
||||
|
||||
VkDescriptorSetLayout const oldLayout = layout->getVkLayout();
|
||||
VkDescriptorSetLayout const newLayout =
|
||||
mDescriptorSetLayoutCache->getVkLayout(layout->bitmask, outSamplers);
|
||||
bool const hasLayoutUpdate = oldLayout != newLayout;
|
||||
layout->setVkLayout(newLayout);
|
||||
|
||||
assert_invariant(
|
||||
(!hasImageUpdates && !hasLayoutUpdate) ||
|
||||
(hasImageUpdates && hasLayoutUpdate));
|
||||
|
||||
if (!hasLayoutUpdate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto foldBitsInHalf = [](auto bitset) {
|
||||
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(bitset) * 4;
|
||||
decltype(bitset) outBitset;
|
||||
bitset.forEachSetBit([&](size_t index) { outBitset.set(index % BITMASK_LOWER_BITS_LEN); });
|
||||
return outBitset;
|
||||
};
|
||||
// We need to build a new descriptor set from the new layout
|
||||
VkDescriptorSet oldSet = set->getVkSet();
|
||||
VkDescriptorSet newSet = mDescriptorSetCache->getVkSet(layout);
|
||||
|
||||
using Bitmask = fvkutils::UniformBufferBitmask;
|
||||
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
|
||||
|
||||
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
|
||||
auto const samplers = layout->bitmask.sampler & (~layout->bitmask.externalSampler);
|
||||
|
||||
// each bitmask denotes a binding index, and separated into two stages - vertex and buffer
|
||||
// We fold the two stages into just the lower half of the bits to denote a combined set of
|
||||
// bindings.
|
||||
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
|
||||
|
||||
// TODO: fix the size for better memory
|
||||
std::vector<VkCopyDescriptorSet> copies;
|
||||
copyBindings.forEachSetBit([&](size_t index) {
|
||||
copies.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
|
||||
.srcSet = oldSet,
|
||||
.srcBinding = (uint32_t) index,
|
||||
.dstSet = newSet,
|
||||
.dstBinding = (uint32_t) index,
|
||||
.descriptorCount = 1,
|
||||
});
|
||||
});
|
||||
vkUpdateDescriptorSets(mPlatform->getDevice(), 0, nullptr, copies.size(), copies.data());
|
||||
|
||||
set->setVkSet(newSet);
|
||||
|
||||
// We need to release the vkset, which is no longer used, back into the pool.
|
||||
mDescriptorSetCache->manualRecyle(layout->count, oldLayout, oldSet);
|
||||
|
||||
// We need to update the external samplers in the set
|
||||
for (auto& bindingInfo: mSetBindings) {
|
||||
if (bindingInfo.set != set) {
|
||||
continue;
|
||||
}
|
||||
mDescriptorSetCache->updateSampler(set, bindingInfo.binding, bindingInfo.image,
|
||||
VK_NULL_HANDLE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata) {
|
||||
// This external image does not require external sampler (YUV conversion).
|
||||
if (metadata.externalFormat == 0) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
VulkanYcbcrConversionCache::Params ycbcrParams = {
|
||||
.conversion = {
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
|
||||
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
|
||||
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
|
||||
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
|
||||
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
|
||||
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
|
||||
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
|
||||
|
||||
// Unclear where to get the chromaFilter, we just assume it's nearest.
|
||||
.chromaFilter = SamplerMagFilter::NEAREST,
|
||||
},
|
||||
.format = metadata.filamentFormat,
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
return mYcbcrConversionCache->getConversion(ycbcrParams);
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::updateImage(ImageData* image) {
|
||||
if (image->hasBeenValidated) {
|
||||
return false;
|
||||
}
|
||||
image->hasBeenValidated = true;
|
||||
|
||||
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
|
||||
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
|
||||
if (vkYcbcr == image->conversion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
image->ptr->setYcbcrConversion(vkYcbcr, metadata.externalFormat != 0);
|
||||
image->conversion = vkYcbcr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::addDescriptorSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
|
||||
mSetAndLayouts.push_back({set, layout});
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeDescriptorSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
|
||||
erase<SetAndLayout>(mSetAndLayouts,
|
||||
[&](auto const& setLayout) { return (setLayout.first == inSet); });
|
||||
erase<SetBindingInfo>(mSetBindings,
|
||||
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeDescriptorSetLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> inLayout) {
|
||||
erase<SetAndLayout>(mSetAndLayouts,
|
||||
[&](auto const& setLayout) { return (setLayout.second == inLayout); });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::bindExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
|
||||
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
|
||||
// Should we do duplicate validation here?
|
||||
mSetBindings.push_back({ bindingPoint, image, set, samplerParams });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::addExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
Platform::ExternalImageHandleRef platformHandleRef) {
|
||||
mImages.push_back({ image, platformHandleRef, false });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image) {
|
||||
erase<SetBindingInfo>(mSetBindings,
|
||||
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
|
||||
erase<ImageData>(mImages, [&](auto const& imageData) { return imageData.ptr == image; });
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::isExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image) const {
|
||||
return std::find_if(mImages.begin(), mImages.end(),
|
||||
[&](auto const& imageData) { return imageData.ptr == image; }) != mImages.end();
|
||||
}
|
||||
|
||||
|
||||
} // namesapce filament::backend
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanYcbcrConversionCache;
|
||||
class VulkanSamplerCache;
|
||||
class VulkanDescriptorSetLayoutCache;
|
||||
class VulkanDescriptorSetCache;
|
||||
|
||||
// Manages the logic of external images and their quirks wrt Vulikan.
|
||||
class VulkanExternalImageManager {
|
||||
public:
|
||||
|
||||
VulkanExternalImageManager(
|
||||
VulkanPlatform* platform,
|
||||
VulkanSamplerCache* samplerCache,
|
||||
VulkanYcbcrConversionCache* ycbcrConversionCache,
|
||||
VulkanDescriptorSetCache* setCache,
|
||||
VulkanDescriptorSetLayoutCache* layoutCache);
|
||||
|
||||
~VulkanExternalImageManager();
|
||||
|
||||
void terminate();
|
||||
|
||||
void onBeginFrame();
|
||||
|
||||
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
// This sets the currently bound layouts objects for the pipeline
|
||||
bool prepareBindSets(SetArray const& layouts);
|
||||
|
||||
void addDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void removeDescriptorSetLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
SamplerParams samplerParams);
|
||||
|
||||
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
Platform::ExternalImageHandleRef platformHandleRef);
|
||||
|
||||
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
|
||||
|
||||
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
|
||||
|
||||
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata);
|
||||
|
||||
private:
|
||||
|
||||
struct ImageData {
|
||||
fvkmemory::resource_ptr<VulkanTexture> ptr;
|
||||
Platform::ExternalImageHandle platformHandle;
|
||||
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
bool updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
bool updateImage(ImageData* imageData);
|
||||
|
||||
VulkanPlatform* mPlatform;
|
||||
VulkanSamplerCache* mSamplerCache;
|
||||
VulkanYcbcrConversionCache* mYcbcrConversionCache;
|
||||
VulkanDescriptorSetCache* mDescriptorSetCache;
|
||||
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
|
||||
|
||||
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
|
||||
|
||||
struct SetBindingInfo {
|
||||
uint8_t binding = 0;
|
||||
fvkmemory::resource_ptr<VulkanTexture> image;
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
|
||||
SamplerParams samplerParams;
|
||||
};
|
||||
|
||||
// Use vectors instead of hash maps because we only expect small number of entries.
|
||||
std::vector<SetAndLayout> mSetAndLayouts;
|
||||
std::vector<SetBindingInfo> mSetBindings;
|
||||
std::vector<ImageData> mImages;
|
||||
};
|
||||
|
||||
} // filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
@@ -340,15 +340,21 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
void VulkanFboCache::reset() noexcept {
|
||||
for (auto pair : mFramebufferCache) {
|
||||
void VulkanFboCache::resetFramebuffers() noexcept {
|
||||
for (const auto& pair: mFramebufferCache) {
|
||||
mRenderPassRefCount[pair.first.renderPass]--;
|
||||
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
|
||||
}
|
||||
mFramebufferCache.clear();
|
||||
for (auto pair : mRenderPassCache) {
|
||||
}
|
||||
|
||||
void VulkanFboCache::terminate() noexcept {
|
||||
resetFramebuffers();
|
||||
|
||||
for (const auto& pair: mRenderPassCache) {
|
||||
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
|
||||
}
|
||||
mRenderPassRefCount.clear();
|
||||
mRenderPassCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,8 +106,11 @@ public:
|
||||
// Evicts old unused Vulkan objects. Call this once per frame.
|
||||
void gc() noexcept;
|
||||
|
||||
// Frees all Framebuffer objects. Call this every time a the swapchain is resized
|
||||
void resetFramebuffers() noexcept;
|
||||
|
||||
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
|
||||
void reset() noexcept;
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
|
||||
@@ -132,10 +132,6 @@ public:
|
||||
return id() == other.id() && type() == other.type();
|
||||
}
|
||||
|
||||
inline bool operator!=(resource_ptr<D> const& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
inline explicit operator bool() const {
|
||||
return bool(mRef);
|
||||
}
|
||||
|
||||
@@ -993,6 +993,30 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
|
||||
return mImpl->mProtectedGraphicsQueue;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
|
||||
ExternalImageHandleRef externalImage) {
|
||||
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
|
||||
usage);
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat) {
|
||||
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
|
||||
viewType, swizzle);
|
||||
}
|
||||
|
||||
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
|
||||
return getSwapchainInstanceExtensionsImpl();
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
#include <backend/platforms/VulkanPlatformAndroid.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
#include "vulkan/VulkanContext.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <private/backend/BackendUtilsAndroid.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include "vulkan/utils/Image.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
@@ -57,7 +57,7 @@ VkFormat transformVkFormat(VkFormat format, bool sRGB) {
|
||||
}
|
||||
|
||||
bool isProtectedFromUsage(uint64_t usage) {
|
||||
return usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
|
||||
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
|
||||
}
|
||||
|
||||
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
||||
@@ -120,9 +120,7 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
||||
usage = 0;
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
// We shouldn't be using external samplers as input attachments
|
||||
// usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
|
||||
if (isDepthFormat) {
|
||||
@@ -138,38 +136,87 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
||||
return { format, usage };
|
||||
}
|
||||
|
||||
std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
||||
bool sRGB) {
|
||||
auto const format = mapToFilamentFormat(desc.format, sRGB);
|
||||
return {
|
||||
format,
|
||||
mapToFilamentUsage(desc.usage, format),
|
||||
};
|
||||
}
|
||||
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage) {
|
||||
VulkanPlatform::ImageData data;
|
||||
|
||||
// if external format we need to specifiy it in the allocation
|
||||
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
||||
|
||||
const VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
// pass down the format (external means we don't have it VK defined)
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
.pNext = useExternalFormat ? &externalFormat : nullptr,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
||||
};
|
||||
|
||||
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
imageInfo.pNext = &externalCreateInfo;
|
||||
imageInfo.format = metadata.format;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.extent = {
|
||||
metadata.width,
|
||||
metadata.height,
|
||||
1u,
|
||||
};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = metadata.layers;
|
||||
imageInfo.samples = metadata.samples;
|
||||
imageInfo.usage = usage;
|
||||
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Allocate the memory
|
||||
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.buffer = buffer,
|
||||
};
|
||||
VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = &androidHardwareBufferInfo,
|
||||
.image = data.first,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &memoryDedicatedAllocateInfo,
|
||||
.allocationSize = metadata.allocationSize,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
if (aHardwareBuffer) {
|
||||
AHardwareBuffer_release(aHardwareBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
|
||||
|
||||
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
auto bufferImpl = const_cast<AHardwareBuffer*>(buffer);
|
||||
AHardwareBuffer_acquire(bufferImpl);
|
||||
|
||||
AHardwareBuffer_Desc hardwareBufferDescription = {};
|
||||
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
|
||||
|
||||
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
|
||||
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
||||
p->sRGB = sRGB;
|
||||
p->height = hardwareBufferDescription.height;
|
||||
p->width = hardwareBufferDescription.width;
|
||||
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
|
||||
p->format = textureFormat;
|
||||
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
|
||||
return Platform::ExternalImageHandle{ p };
|
||||
}
|
||||
|
||||
@@ -178,20 +225,23 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||
|
||||
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
|
||||
ExternalImageHandleRef externalImage) const noexcept {
|
||||
auto metadata = extractExternalImageMetadata(externalImage);
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
|
||||
return {
|
||||
.width = metadata.width,
|
||||
.height = metadata.height,
|
||||
.format = metadata.filamentFormat,
|
||||
.usage = metadata.filamentUsage,
|
||||
.width = fvkExternalImage->width,
|
||||
.height = fvkExternalImage->height,
|
||||
.format = fvkExternalImage->format,
|
||||
.usage = fvkExternalImage->usage,
|
||||
};
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImageMetadata(
|
||||
ExternalImageHandleRef image) const {
|
||||
auto const* fvkExternalImage = static_cast<ExternalImageVulkanAndroid const*>(image.get());
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
|
||||
ExternalImageHandleRef externalImage) {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
|
||||
ExternalImageMetadata metadata = {};
|
||||
ExternalImageMetadata metadata;
|
||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc bufferDesc;
|
||||
@@ -199,30 +249,16 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImag
|
||||
metadata.width = bufferDesc.width;
|
||||
metadata.height = bufferDesc.height;
|
||||
metadata.layers = bufferDesc.layers;
|
||||
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
|
||||
std::tie(metadata.format, metadata.usage) =
|
||||
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||
std::tie(metadata.filamentFormat, metadata.filamentUsage) =
|
||||
getFilamentFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||
|
||||
if (isProtectedFromUsage(bufferDesc.usage)) {
|
||||
metadata.filamentUsage |= TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
// TODO: The following seems unnecessary. we should be able to discern directly from the
|
||||
// bufferDesc.
|
||||
if (any(metadata.filamentUsage & TextureUsage::BLIT_SRC)) {
|
||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(metadata.filamentUsage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
}
|
||||
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
VkAndroidHardwareBufferPropertiesANDROID properties = {
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
|
||||
@@ -232,116 +268,138 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImag
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkGetAndroidHardwareBufferProperties failed with error="
|
||||
<< static_cast<int32_t>(result);
|
||||
|
||||
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
|
||||
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
|
||||
<< "mismatched image format( " << metadata.format << ") and queried format("
|
||||
<< bufferPropertiesFormat << ") for external image (AHB)";
|
||||
metadata.externalFormat = formatInfo.externalFormat;
|
||||
|
||||
// Choose either externalFormat > 0 or metadata.format and prefer the latter.
|
||||
if (metadata.externalFormat > 0 && metadata.format != VK_FORMAT_UNDEFINED) {
|
||||
// See VUID-VkImageCreateInfo-pNext-09457
|
||||
metadata.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
metadata.externalFormat = 0;
|
||||
}
|
||||
|
||||
metadata.allocationSize = properties.allocationSize;
|
||||
metadata.memoryTypeBits = properties.memoryTypeBits;
|
||||
|
||||
metadata.ycbcrConversionComponents = formatInfo.samplerYcbcrConversionComponents;
|
||||
metadata.ycbcrModel = formatInfo.suggestedYcbcrModel;
|
||||
metadata.ycbcrRange = formatInfo.suggestedYcbcrRange;
|
||||
metadata.xChromaOffset = formatInfo.suggestedXChromaOffset;
|
||||
metadata.yChromaOffset = formatInfo.suggestedYChromaOffset;
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
|
||||
ExternalImageHandleRef externalImage) const {
|
||||
auto const& metadata = extractExternalImageMetadata(externalImage);
|
||||
|
||||
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
||||
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
|
||||
memoryTypeIndex, usage);
|
||||
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
return data;
|
||||
}
|
||||
|
||||
// if external format we need to specifiy it in the allocation
|
||||
bool const useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
||||
|
||||
VkExternalFormatANDROID const externalFormat = {
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
VkExternalMemoryImageCreateInfo const externalCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
.pNext = useExternalFormat ? &externalFormat : nullptr,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
VkImageCreateInfo const imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &externalCreateInfo,
|
||||
.flags = useExternalFormat ? VK_IMAGE_CREATE_ALIAS_BIT : 0u,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = metadata.format,
|
||||
.extent = {
|
||||
metadata.width,
|
||||
metadata.height,
|
||||
1u,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = metadata.layers,
|
||||
.samples = metadata.samples,
|
||||
.usage = metadata.usage,
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
|
||||
VkDevice const device = getDevice();
|
||||
|
||||
VkImage image;
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Allocate the memory
|
||||
VkImportAndroidHardwareBufferInfoANDROID const androidHardwareBufferInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||
.buffer = buffer,
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.conversion = conversion,
|
||||
};
|
||||
VkMemoryDedicatedAllocateInfo const memoryDedicatedAllocateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = &androidHardwareBufferInfo,
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.flags = 0,
|
||||
.image = image,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
.viewType = viewType,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create VkImageView."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(
|
||||
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
|
||||
uint32_t internalFormat) {
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties(getPhysicalDevice(), &memoryProperties);
|
||||
|
||||
|
||||
VkMemoryPropertyFlags const requiredMemoryFlags =
|
||||
any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
|
||||
metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
VkMemoryAllocateInfo const allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &memoryDedicatedAllocateInfo,
|
||||
.allocationSize = metadata.allocationSize,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkDeviceMemory memory;
|
||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &memory);
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
result = vkBindImageMemory(getDevice(), image, memory, 0);
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
VkSamplerCreateInfo samplerInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
VkSampler sampler;
|
||||
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
return { image, memory };
|
||||
<< "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
|
||||
@@ -369,9 +427,20 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
|
||||
// Deprecated platform dependent helper methods
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return ExternalImageMetadata{};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return ImageData{};
|
||||
}
|
||||
|
||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
||||
VkInstance instance, uint64_t flags) noexcept {
|
||||
return SurfaceBundle{};
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -24,28 +24,12 @@
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
// Platform specific includes and defines
|
||||
#if defined(__APPLE__)
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
#ifndef VK_MVK_macos_surface
|
||||
#error VK_MVK_macos_surface is not defined
|
||||
#endif
|
||||
#elif defined(FILAMENT_IOS)
|
||||
// Metal is not available when building for the iOS simulator on Desktop.
|
||||
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
|
||||
#if METAL_AVAILABLE
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#endif
|
||||
|
||||
#ifndef VK_MVK_ios_surface
|
||||
#error VK_MVK_ios_surface is not defined
|
||||
#endif
|
||||
#define METALVIEW_TAG 255
|
||||
#else
|
||||
#error Not a supported Apple + Vulkan platform
|
||||
#ifndef VK_MVK_macos_surface
|
||||
#error VK_MVK_macos_surface is not defined
|
||||
#endif
|
||||
|
||||
using namespace bluevk;
|
||||
@@ -54,48 +38,52 @@ namespace filament::backend {
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
||||
ExtensionSet const ret = {
|
||||
#if defined(__APPLE__)
|
||||
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
|
||||
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
|
||||
VK_MVK_IOS_SURFACE_EXTENSION_NAME,
|
||||
#endif
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
||||
VkInstance instance, uint64_t flags) noexcept {
|
||||
VkSurfaceKHR surface;
|
||||
#if defined(__APPLE__)
|
||||
NSView* nsview = (__bridge NSView*) nativeWindow;
|
||||
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
|
||||
NSView* nsview = (__bridge NSView*) nativeWindow;
|
||||
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
|
||||
|
||||
// Create the VkSurface.
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
||||
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pView = (__bridge void*) nsview;
|
||||
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
|
||||
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
||||
// Create the VkSurface.
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
|
||||
<< "Unable to load vkCreateIOSSurfaceMVK function.";
|
||||
VkIOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pView = metalLayer;
|
||||
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
||||
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pView = (__bridge void*) nsview;
|
||||
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||
(VkSurfaceKHR*) &surface);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
|
||||
#endif
|
||||
return std::make_tuple(surface, VkExtent2D{});
|
||||
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
|
||||
return std::make_tuple(surface, VkExtent2D{});
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -84,6 +84,30 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
||||
VulkanPlatform::ExtensionSet const ret = {
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "WebGPUSwapChain.h"
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
@@ -227,6 +228,14 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
|
||||
driverConfig.disableHeapHandleTags) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printInstanceDetails(mPlatform.getInstance());
|
||||
#endif
|
||||
mAdapter = mPlatform.requestAdapter(nullptr);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(mAdapter);
|
||||
#endif
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(mDevice);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -256,6 +265,7 @@ void WebGPUDriver::terminate() {
|
||||
}
|
||||
|
||||
void WebGPUDriver::tick(int) {
|
||||
mDevice.Tick();
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
@@ -285,15 +295,27 @@ void WebGPUDriver::finish(int) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
if (rph) {
|
||||
destructHandle<WGPURenderPrimitive>(rph);
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
if (vbih) {
|
||||
destructHandle<WGPUVertexBufferInfo>(vbih);
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
if (vbh) {
|
||||
destructHandle<WGPUVertexBuffer>(vbh);
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||
if (ibh) {
|
||||
destructHandle<WGPUIndexBuffer>(ibh);
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
@@ -309,13 +331,10 @@ void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
||||
if (sch) {
|
||||
destructHandle<WebGPUSwapChain>(sch);
|
||||
}
|
||||
mSwapChain = nullptr;
|
||||
// TODO: use webgpu handle allocator from
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// if (sch) {
|
||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
||||
// destruct(sch, hwSwapChain);
|
||||
// }
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
|
||||
@@ -325,16 +344,16 @@ void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
|
||||
if (tqh) {
|
||||
destructHandle<WebGPUDescriptorSetLayout>(tqh);
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// return allocAndConstructHandle<HwSwapChain>();
|
||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WebGPUSwapChain>();
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||
@@ -342,7 +361,7 @@ Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
@@ -370,15 +389,15 @@ Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPURenderTarget>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
||||
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUVertexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||
@@ -386,11 +405,11 @@ Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
||||
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPURenderPrimitive>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPUVertexBufferInfo>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
@@ -398,12 +417,11 @@ Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WGPURenderTarget>();
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
||||
return Handle<HwDescriptorSetLayout>(
|
||||
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
|
||||
return allocHandle<WebGPUDescriptorSetLayout>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||
@@ -419,21 +437,15 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
|
||||
mSwapChain = nullptr;
|
||||
mNativeWindow = nativeWindow;
|
||||
assert_invariant(!mSwapChain);
|
||||
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||
mAdapter = mPlatform.requestAdapter(surface);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(mAdapter);
|
||||
#endif
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(mDevice);
|
||||
#endif
|
||||
|
||||
mQueue = mDevice.GetQueue();
|
||||
mSwapChain = std::make_unique<WebGPUSwapChain>(std::move(surface), mAdapter, mDevice, flags);
|
||||
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||
mDevice, flags);
|
||||
assert_invariant(mSwapChain);
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
@@ -446,9 +458,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
||||
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
||||
#endif
|
||||
// TODO: use webgpu handle allocator from.
|
||||
// https://github.com/google/filament/pull/8566
|
||||
// hwSwapChain->swapChain = mSwapChain.get();
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
@@ -498,7 +507,11 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
|
||||
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||
assert_invariant(!mDefaultRenderTarget);
|
||||
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
|
||||
assert_invariant(mDefaultRenderTarget);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||
@@ -509,7 +522,9 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||
backend::DescriptorSetLayout&& info) {}
|
||||
backend::DescriptorSetLayout&& info) {
|
||||
constructHandle<WebGPUDescriptorSetLayout>(dslh, std::move(info), &mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
Handle<HwDescriptorSetLayout> dslh) {}
|
||||
@@ -659,6 +674,10 @@ void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
|
||||
|
||||
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
Handle<HwBufferObject> boh) {
|
||||
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
|
||||
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
|
||||
assert_invariant(index < vertexBuffer->buffers.size());
|
||||
vertexBuffer->setBuffer(bufferObject, index);
|
||||
}
|
||||
|
||||
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
||||
@@ -691,18 +710,71 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||
.label = "command_encoder"
|
||||
};
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
assert_invariant(mCommandEncoder);
|
||||
// TODO: Remove this code once WebGPU pipeline is implemented
|
||||
static float red = 1.0f;
|
||||
if (red - 0.01 > 0) {
|
||||
red -= 0.01;
|
||||
} else {
|
||||
red = 1.0f;
|
||||
}
|
||||
assert_invariant(mTextureView);
|
||||
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
|
||||
.view = mTextureView,
|
||||
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
|
||||
.depthSlice = wgpu::kDepthSliceUndefined,
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = wgpu::Color{red, 0 , 0 , 1},
|
||||
};
|
||||
|
||||
wgpu::RenderPassDescriptor renderPassDescriptor = {
|
||||
.colorAttachmentCount = 1,
|
||||
.colorAttachments = &renderPassColorAttachment,
|
||||
.depthStencilAttachment = nullptr,
|
||||
.timestampWrites = nullptr,
|
||||
};
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
|
||||
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
|
||||
}
|
||||
|
||||
void WebGPUDriver::endRenderPass(int) {
|
||||
mRenderPassEncoder.End();
|
||||
mRenderPassEncoder = nullptr;
|
||||
wgpu::CommandBufferDescriptor commandBufferDescriptor {
|
||||
.label = "command_buffer",
|
||||
};
|
||||
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
|
||||
assert_invariant(mCommandBuffer);
|
||||
}
|
||||
|
||||
void WebGPUDriver::nextSubpass(int) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
|
||||
"WebGPU driver does not support distinct draw/read swap chains.");
|
||||
auto* swapChain = handleCast<WebGPUSwapChain>(drawSch);
|
||||
mSwapChain = swapChain;
|
||||
assert_invariant(mSwapChain);
|
||||
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||
mTextureView = mSwapChain->getCurrentSurfaceTextureView(surfaceSize);
|
||||
assert_invariant(mTextureView);
|
||||
}
|
||||
|
||||
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||
mCommandEncoder = nullptr;
|
||||
mQueue.Submit(1, &mCommandBuffer);
|
||||
mCommandBuffer = nullptr;
|
||||
mTextureView = nullptr;
|
||||
assert_invariant(mSwapChain);
|
||||
mSwapChain->present();
|
||||
}
|
||||
|
||||
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
#include "webgpu/WebGPUSwapChain.h"
|
||||
#include "WebGPUHandles.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUSwapChain;
|
||||
|
||||
/**
|
||||
* WebGPU backend (driver) implementation
|
||||
*/
|
||||
@@ -60,10 +62,14 @@ private:
|
||||
wgpu::Adapter mAdapter = nullptr;
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Queue mQueue = nullptr;
|
||||
// TODO consider moving to handle allocator when ready
|
||||
std::unique_ptr<WebGPUSwapChain> mSwapChain = nullptr;
|
||||
void* mNativeWindow = nullptr;
|
||||
WebGPUSwapChain* mSwapChain = nullptr;
|
||||
uint64_t mNextFakeHandle = 1;
|
||||
|
||||
wgpu::CommandEncoder mCommandEncoder = nullptr;
|
||||
wgpu::TextureView mTextureView = nullptr;
|
||||
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
@@ -93,6 +99,21 @@ private:
|
||||
return mHandleAllocator.allocate<D>();
|
||||
}
|
||||
|
||||
template<typename D, typename B, typename ... ARGS>
|
||||
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
|
||||
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
template<typename D, typename B>
|
||||
D* handleCast(Handle<B> handle) noexcept {
|
||||
return mHandleAllocator.handle_cast<D*>(handle);
|
||||
}
|
||||
|
||||
template<typename D, typename B>
|
||||
void destructHandle(Handle<B>& handle) noexcept {
|
||||
auto* p = mHandleAllocator.handle_cast<D*>(handle);
|
||||
return mHandleAllocator.deallocate(handle, p);
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
153
filament/backend/src/webgpu/WebGPUHandles.cpp
Normal file
153
filament/backend/src/webgpu/WebGPUHandles.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
wgpu::Buffer createIndexBuffer(wgpu::Device const& device, uint8_t elementSize, uint32_t indexCount) {
|
||||
wgpu::BufferDescriptor descriptor{ .label = "index_buffer",
|
||||
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
|
||||
.size = elementSize * indexCount,
|
||||
.mappedAtCreation = false };
|
||||
return device.CreateBuffer(&descriptor);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
|
||||
uint32_t indexCount)
|
||||
: buffer(createIndexBuffer(device, elementSize, indexCount)) {}
|
||||
|
||||
|
||||
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
|
||||
Handle<WGPUVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vextexCount),
|
||||
vbih(vbih),
|
||||
buffers(bufferCount) {
|
||||
wgpu::BufferDescriptor descriptor {
|
||||
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex,
|
||||
.size = vextexCount * bufferCount,
|
||||
.mappedAtCreation = false };
|
||||
|
||||
for (uint32_t i = 0; i < bufferCount; ++i) {
|
||||
descriptor.label = ("vertex_buffer_" + std::to_string(i)).c_str();
|
||||
buffers[i] = device.CreateBuffer(&descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Empty function is a place holder for verxtex buffer updates and should be
|
||||
// updated for that purpose.
|
||||
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
|
||||
|
||||
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
|
||||
: HwBufferObject(byteCount),
|
||||
bufferObjectBinding(bindingType) {}
|
||||
|
||||
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
|
||||
wgpu::ShaderStage ret = wgpu::ShaderStage::None;
|
||||
if (any(ShaderStageFlags::VERTEX & fFlags)) {
|
||||
ret |= wgpu::ShaderStage::Vertex;
|
||||
}
|
||||
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
|
||||
ret |= wgpu::ShaderStage::Fragment;
|
||||
}
|
||||
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
|
||||
ret |= wgpu::ShaderStage::Compute;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
|
||||
wgpu::Device const* device) {
|
||||
assert_invariant(device->Get());
|
||||
|
||||
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||
// debugging. For now, hack an incrementing value.
|
||||
static int layoutNum = 0;
|
||||
|
||||
|
||||
uint samplerCount =
|
||||
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
|
||||
return fEntry.type == DescriptorType::SAMPLER ||
|
||||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
|
||||
});
|
||||
|
||||
|
||||
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
|
||||
wEntries.reserve(layout.bindings.size() + samplerCount);
|
||||
|
||||
for (auto fEntry: layout.bindings) {
|
||||
auto& wEntry = wEntries.emplace_back();
|
||||
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
|
||||
wEntry.binding = fEntry.binding * 2;
|
||||
|
||||
switch (fEntry.type) {
|
||||
// TODO Metal treats these the same. Is this fine?
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
case DescriptorType::SAMPLER: {
|
||||
// Sampler binding is 2n+1 due to split.
|
||||
auto& samplerEntry = wEntries.emplace_back();
|
||||
samplerEntry.binding = fEntry.binding * 2 + 1;
|
||||
samplerEntry.visibility = wEntry.visibility;
|
||||
// We are simply hoping that undefined and defaults suffices here.
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
|
||||
break;
|
||||
}
|
||||
case DescriptorType::UNIFORM_BUFFER: {
|
||||
wEntry.buffer.hasDynamicOffset =
|
||||
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
|
||||
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
// TODO: Ideally we fill minBindingSize
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::INPUT_ATTACHMENT: {
|
||||
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
|
||||
PANIC_POSTCONDITION("Input Attachment is not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER: {
|
||||
// TODO: Vulkan does not support this, can we?
|
||||
PANIC_POSTCONDITION("Shader storage is not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Currently flags are only used to specify dynamic offset.
|
||||
|
||||
// UNUSED
|
||||
// fEntry.count
|
||||
}
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
|
||||
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
|
||||
// debugging. For now, hack an incrementing value.
|
||||
.label{ "layout_"+ layout.label + std::to_string(++layoutNum) },
|
||||
.entryCount = wEntries.size(),
|
||||
.entries = wEntries.data()
|
||||
};
|
||||
// TODO Do we need to defer this until we have more info on textures and samplers??
|
||||
mLayout = device->CreateBindGroupLayout(&layoutDescriptor);
|
||||
}
|
||||
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
|
||||
}// namespace filament::backend
|
||||
156
filament/backend/src/webgpu/WebGPUHandles.h
Normal file
156
filament/backend/src/webgpu/WebGPUHandles.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct WGPUBufferObject;
|
||||
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
||||
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
attributes(attributes) {}
|
||||
AttributeArray attributes;
|
||||
};
|
||||
|
||||
struct WGPUVertexBuffer : public HwVertexBuffer {
|
||||
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
|
||||
Handle<WGPUVertexBufferInfo> vbih);
|
||||
|
||||
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
|
||||
|
||||
Handle<WGPUVertexBufferInfo> vbih;
|
||||
utils::FixedCapacityVector<wgpu::Buffer> buffers;
|
||||
};
|
||||
|
||||
struct WGPUIndexBuffer : public HwIndexBuffer {
|
||||
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
|
||||
uint32_t indexCount);
|
||||
|
||||
wgpu::Buffer buffer;
|
||||
};
|
||||
|
||||
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPUBufferObject : HwBufferObject {
|
||||
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
|
||||
|
||||
wgpu::Buffer buffer;
|
||||
const BufferObjectBinding bufferObjectBinding;
|
||||
};
|
||||
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout {
|
||||
public:
|
||||
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const* device);
|
||||
~WebGPUDescriptorSetLayout();
|
||||
|
||||
private:
|
||||
// TODO: If this is useful elsewhere, remove it from this class
|
||||
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
|
||||
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
|
||||
|
||||
wgpu::BindGroupLayout mLayout;
|
||||
};
|
||||
|
||||
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPUTexture : public HwTexture {
|
||||
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
|
||||
|
||||
// constructors for creating texture views
|
||||
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
wgpu::Texture texture = nullptr;
|
||||
};
|
||||
|
||||
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
WGPURenderPrimitive();
|
||||
|
||||
void setBuffers(WGPUVertexBufferInfo const* const vbi,
|
||||
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
|
||||
|
||||
WGPUVertexBuffer* vertexBuffer = nullptr;
|
||||
WGPUIndexBuffer* indexBuffer = nullptr;
|
||||
};
|
||||
|
||||
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
|
||||
// Update the struct when used by WebGPU driver.
|
||||
struct WGPURenderTarget : public HwRenderTarget {
|
||||
class Attachment {
|
||||
public:
|
||||
friend struct WGPURenderTarget;
|
||||
|
||||
Attachment() = default;
|
||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
||||
: level(level),
|
||||
layer(layer),
|
||||
texture(gpuTexture->texture),
|
||||
mWGPUTexture(gpuTexture) {}
|
||||
|
||||
uint8_t level = 0;
|
||||
uint16_t layer = 0;
|
||||
|
||||
private:
|
||||
wgpu::Texture texture = nullptr;
|
||||
WGPUTexture* mWGPUTexture = nullptr;
|
||||
};
|
||||
|
||||
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
||||
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
|
||||
WGPURenderTarget()
|
||||
: HwRenderTarget(0, 0),
|
||||
defaultRenderTarget(true) {}
|
||||
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
|
||||
const RenderPassParams& params);
|
||||
|
||||
math::uint2 getAttachmentSize() noexcept;
|
||||
|
||||
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
|
||||
uint8_t getSamples() const { return samples; }
|
||||
|
||||
Attachment getDrawColorAttachment(size_t index);
|
||||
Attachment getReadColorAttachment(size_t index);
|
||||
|
||||
private:
|
||||
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
|
||||
bool defaultRenderTarget = false;
|
||||
uint8_t samples = 1;
|
||||
|
||||
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
math::uint2 attachmentSize = {};
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
@@ -190,10 +190,13 @@ wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||
}
|
||||
}
|
||||
|
||||
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device& device,
|
||||
wgpu::SurfaceCapabilities const& capabilities, bool useSRGBColorSpace) {
|
||||
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||
wgpu::SurfaceCapabilities const& capabilities, wgpu::Extent2D const& surfaceSize,
|
||||
bool useSRGBColorSpace) {
|
||||
config.device = device;
|
||||
config.usage = wgpu::TextureUsage::RenderAttachment;
|
||||
config.width = surfaceSize.width;
|
||||
config.height = surfaceSize.height;
|
||||
config.format =
|
||||
selectColorFormat(capabilities.formatCount, capabilities.formats, useSRGBColorSpace);
|
||||
config.presentMode =
|
||||
@@ -205,8 +208,8 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device& device,
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter,
|
||||
wgpu::Device& device, uint64_t flags)
|
||||
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags)
|
||||
: mSurface(surface) {
|
||||
wgpu::SurfaceCapabilities capabilities = {};
|
||||
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||
@@ -217,33 +220,57 @@ WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter
|
||||
#endif
|
||||
}
|
||||
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
||||
initConfig(mConfig, device, capabilities, useSRGBColorSpace);
|
||||
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
|
||||
mSurface.Configure(&mConfig);
|
||||
}
|
||||
|
||||
WebGPUSwapChain::~WebGPUSwapChain() {
|
||||
if (mConfigured) {
|
||||
mSurface.Unconfigure();
|
||||
mConfigured = false;
|
||||
}
|
||||
mSurface.Unconfigure();
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture* texture) {
|
||||
if (width < 1 || height < 1) {
|
||||
PANIC_LOG("WebGPUSwapChain::GetCurrentTexture: Invalid width and/or height requested.");
|
||||
return;
|
||||
}
|
||||
if (mConfig.width != width || mConfig.height != height || !mConfigured) {
|
||||
mConfig.width = width;
|
||||
mConfig.height = height;
|
||||
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
|
||||
<< "WebGPUSwapChain::setExtent: Invalid width " << currentSurfaceSize.width
|
||||
<< " and/or height " << currentSurfaceSize.height << " requested.";
|
||||
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) {
|
||||
mConfig.width = currentSurfaceSize.width;
|
||||
mConfig.height = currentSurfaceSize.height;
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printSurfaceConfiguration(mConfig);
|
||||
#endif
|
||||
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height
|
||||
<< utils::io::endl;
|
||||
// TODO we may need to ensure no surface texture is flight when we do this. some
|
||||
// synchronization may be necessary
|
||||
mSurface.Configure(&mConfig);
|
||||
mConfigured = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mSurface.GetCurrentTexture(texture);
|
||||
wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
|
||||
wgpu::Extent2D const& currentSurfaceSize) {
|
||||
setExtent(currentSurfaceSize);
|
||||
wgpu::SurfaceTexture surfaceTexture;
|
||||
mSurface.GetCurrentTexture(&surfaceTexture);
|
||||
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
|
||||
return nullptr;
|
||||
}
|
||||
// Create a view for this surface texture
|
||||
// TODO: review these initiliazations as webgpu pipeline gets mature
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor = {
|
||||
.label = "texture_view",
|
||||
.format = surfaceTexture.texture.GetFormat(),
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = 1
|
||||
};
|
||||
return surfaceTexture.texture.CreateView(&textureViewDescriptor);
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::present() {
|
||||
assert_invariant(mSurface);
|
||||
mSurface.Present();
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -19,24 +19,28 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUSwapChain : public Platform::SwapChain {
|
||||
class WebGPUSwapChain final : public Platform::SwapChain, HwSwapChain {
|
||||
public:
|
||||
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Adapter& adapter, wgpu::Device& device,
|
||||
uint64_t flags);
|
||||
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags);
|
||||
~WebGPUSwapChain();
|
||||
|
||||
void GetCurrentTexture(uint32_t width, uint32_t height, wgpu::SurfaceTexture*);
|
||||
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||
|
||||
void present();
|
||||
|
||||
private:
|
||||
void setExtent(wgpu::Extent2D const&);
|
||||
|
||||
wgpu::Surface mSurface = {};
|
||||
wgpu::SurfaceConfiguration mConfig = {};
|
||||
bool mConfigured = false;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -28,6 +29,14 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
||||
return wgpu::Extent2D{
|
||||
.width = static_cast<uint32_t>(ANativeWindow_getWidth(window)),
|
||||
.height = static_cast<uint32_t>(ANativeWindow_getHeight(window))
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
||||
surfaceSourceAndroidWindow.window = nativeWindow;
|
||||
|
||||
@@ -24,21 +24,8 @@
|
||||
#include <cstdint>
|
||||
|
||||
// Platform specific includes and defines
|
||||
#if defined(__APPLE__)
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#elif defined(FILAMENT_IOS)
|
||||
// Metal is not available when building for the iOS simulator on Desktop.
|
||||
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
|
||||
#if METAL_AVAILABLE
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#endif
|
||||
// is this needed?
|
||||
#define METALVIEW_TAG 255
|
||||
#else
|
||||
#error Not a supported Apple + WebGPU platform
|
||||
#endif
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
/**
|
||||
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
||||
@@ -46,14 +33,19 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
// Both IOS and MacOS expects CAMetalLayer.
|
||||
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||
return wgpu::Extent2D{
|
||||
.width = static_cast<uint32_t>(metalLayer.drawableSize.width),
|
||||
.height = static_cast<uint32_t>(metalLayer.drawableSize.height)
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__APPLE__)
|
||||
auto nsView = (__bridge NSView*) nativeWindow;
|
||||
FILAMENT_CHECK_POSTCONDITION(nsView) << "Unable to obtain Metal-backed NSView.";
|
||||
[nsView setWantsLayer:YES];
|
||||
id metalLayer = [CAMetalLayer layer];
|
||||
[nsView setLayer:metalLayer];
|
||||
// Both IOS and MacOS expects CAMetalLayer.
|
||||
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||
@@ -62,19 +54,6 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||
#elif defined(FILAMENT_IOS)
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||
.nextInChain = &surfaceSourceMetalLayer,
|
||||
.label = "metal_surface",
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||
#else
|
||||
#error Not a supported Apple + WebGPU platform
|
||||
#endif
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,74 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
auto surfaceExtent = wgpu::Extent2D{};
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
surfaceExtent.width = ptrval->width;
|
||||
surfaceExtent.height = ptrval->height;
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window size for Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
const xcb_setup_t* setup = xcb_get_setup(g_x11.connection);
|
||||
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
|
||||
xcb_screen_t* screen = screen_iter.data;
|
||||
surfaceExtent.width = static_cast<uint32_t>(screen->width_in_pixels);
|
||||
surfaceExtent.height = static_cast<uint32_t>(screen->height_in_pixels);
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||
"XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
int screenNumber = DefaultScreen(g_x11.display);
|
||||
Screen* screen = ScreenOfDisplay(g_x11.display, screenNumber);
|
||||
surfaceExtent.width = static_cast<uint32_t>(WidthOfScreen(screen));
|
||||
surfaceExtent.height = static_cast<uint32_t>(HeightOfScreen(screen));
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||
"XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Cannot get window surface size for X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Cannot get window surface size for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
#endif
|
||||
return surfaceExtent;
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
|
||||
@@ -30,6 +30,16 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
HWND window = static_cast<HWND>(nativeWindow);
|
||||
RECT windowRect;
|
||||
GetWindowRect(window, &windowRect);
|
||||
return wgpu::Extent2D{
|
||||
.width = static_cast<uint32_t>(windowRect.right - windowRect.left),
|
||||
.height = static_cast<uint32_t>(windowRect.bottom - windowRect.top)
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
// TODO verify this is necessary for Dawn implementation as well:
|
||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
||||
|
||||
@@ -56,7 +56,7 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
|
||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||
|
||||
mDescriptorSetLayout = cleanup.add(
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ "kLayouts", kLayouts }));
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||
|
||||
@@ -317,15 +317,6 @@ public:
|
||||
*/
|
||||
size_t metalUploadBufferSizeBytes = 512 * 1024;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired.
|
||||
*
|
||||
* Each frame rendered requires a CAMetalDrawable texture, which is
|
||||
* presented on-screen at the completion of each frame. These are
|
||||
* limited and provided round-robin style by the system.
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
|
||||
@@ -75,6 +75,14 @@ public:
|
||||
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
|
||||
utils::CString const& getNameOrDefault() const noexcept {
|
||||
if (const auto& name = getName(); !name.empty()) {
|
||||
return name;
|
||||
}
|
||||
static const utils::CString sDefaultName = "(none)";
|
||||
return sDefaultName;
|
||||
}
|
||||
|
||||
private:
|
||||
utils::CString mName;
|
||||
};
|
||||
|
||||
@@ -138,7 +138,6 @@ Engine* FEngine::create(Builder const& builder) {
|
||||
.forceGLES2Context = instance->getConfig().forceGLES2Context,
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
|
||||
@@ -734,7 +733,6 @@ int FEngine::loop() {
|
||||
.forceGLES2Context = mConfig.forceGLES2Context,
|
||||
.stereoscopicType = mConfig.stereoscopicType,
|
||||
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
|
||||
|
||||
@@ -269,12 +269,14 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
|
||||
if (texture && texture->textureHandleCanMutate()) {
|
||||
mTextureParameters[binding] = { texture, sampler.getSamplerParams() };
|
||||
} else {
|
||||
// Ensure to erase the binding from mTextureParameters since it will not
|
||||
// be updated.
|
||||
mTextureParameters.erase(binding);
|
||||
|
||||
Handle<HwTexture> handle{};
|
||||
if (texture) {
|
||||
handle = texture->getHwHandleForSampling();
|
||||
assert_invariant(handle == texture->getHwHandle());
|
||||
} else {
|
||||
mTextureParameters.erase(binding);
|
||||
}
|
||||
mDescriptorSet.setSampler(binding, handle, sampler.getSamplerParams());
|
||||
}
|
||||
|
||||
@@ -161,7 +161,13 @@ Texture::Builder& Texture::Builder::name(StaticString const& name) noexcept {
|
||||
Texture* Texture::Builder::build(Engine& engine) {
|
||||
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
|
||||
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
|
||||
<< "Texture format " << uint16_t(mImpl->mFormat) << " not supported on this platform";
|
||||
<< "Texture format " << uint16_t(mImpl->mFormat)
|
||||
<< " not supported on this platform, texture name="
|
||||
<< getNameOrDefault().c_str_safe();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mWidth > 0 && mImpl->mHeight > 0)
|
||||
<< "Texture has invalid dimensions: (" << mImpl->mWidth << ", " << mImpl->mHeight
|
||||
<< "), texture name=" << getNameOrDefault().c_str_safe();
|
||||
}
|
||||
const bool isProtectedTexturesSupported =
|
||||
downcast(engine).getDriverApi().isProtectedTexturesSupported();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.59.0"
|
||||
spec.version = "1.59.1"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.0/filament-v1.59.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.1/filament-v1.59.1-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -33,11 +33,11 @@ namespace filament::descriptor_sets {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{{
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{"postProcess", {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
}};
|
||||
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{"depthVariant",{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
}};
|
||||
|
||||
@@ -46,14 +46,14 @@ static DescriptorSetLayout const depthVariantDescriptorSetLayout{{
|
||||
// dedicated SSR vertex shader), which uses perViewDescriptorSetLayout.
|
||||
// This means that PerViewBindingPoints::SHADOWS must be in the layout even though it's not used
|
||||
// by the SSR variant.
|
||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{{
|
||||
static DescriptorSetLayout const ssrVariantDescriptorSetLayout{"ssrVariant", {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE },
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR },
|
||||
}};
|
||||
|
||||
static DescriptorSetLayout perViewDescriptorSetLayout = {{
|
||||
static DescriptorSetLayout perViewDescriptorSetLayout = {"perView", {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS },
|
||||
@@ -68,7 +68,7 @@ static DescriptorSetLayout perViewDescriptorSetLayout = {{
|
||||
{ DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG },
|
||||
}};
|
||||
|
||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {{
|
||||
static DescriptorSetLayout perRenderableDescriptorSetLayout = {"preRenderable",{
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
||||
|
||||
@@ -704,7 +704,9 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
|
||||
::prepareNativeWindow(mWindow);
|
||||
|
||||
void* metalLayer = nullptr;
|
||||
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN) {
|
||||
|
||||
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN
|
||||
|| config.backend == filament::Engine::Backend::WEBGPU) {
|
||||
metalLayer = setUpMetalLayer(nativeWindow);
|
||||
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
|
||||
nativeSwapChain = metalLayer;
|
||||
|
||||
@@ -205,7 +205,13 @@ void setup_window(Window& w, Engine* engine) {
|
||||
void* nativeSwapChain = nativeWindow;
|
||||
#if defined(__APPLE__)
|
||||
void* metalLayer = nullptr;
|
||||
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
if (kBackend == filament::Engine::Backend::METAL || kBackend == filament::Engine::Backend::VULKAN
|
||||
|| kBackend == filament::Engine::Backend::WEBGPU) {
|
||||
#else
|
||||
if (kBackend == filament::Engine::Backend::METAL || kBackend == filament::Engine::Backend::VULKAN) {
|
||||
#endif
|
||||
metalLayer = setUpMetalLayer(nativeWindow);
|
||||
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
|
||||
nativeSwapChain = metalLayer;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.59.0",
|
||||
"version": "1.59.1",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user